Skip to content

Commit e6c03ea

Browse files
committed
refactor(rpc): rename schema builder to simple-schema; use it in plugins; docs use valibot
- Rename devframe/utils/schema -> devframe/utils/simple-schema and rename the exported type DevframeSchema -> SimpleSchema. The builder is now explicitly documented as discouraged for app code (a minimal, best-effort validator for devframe's own first-party packages). - Extend the builder with record/union/literal and make object() infer optional keys for optional() fields (matching valibot/zod). - Migrate the built-in plugins (assets, og, terminals) off valibot onto devframe/utils/simple-schema, and drop valibot from their dependencies. - Docs: never reference the built-in builder; all schema examples use valibot with an explicit install hint (npm i valibot). BREAKING CHANGE: devframe/utils/schema is renamed to devframe/utils/simple-schema and its exported type DevframeSchema is renamed to SimpleSchema.
1 parent df12ef6 commit e6c03ea

46 files changed

Lines changed: 787 additions & 537 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

alias.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const alias = {
2626
'devframe/utils/nanoid': r('devframe/src/utils/nanoid.ts'),
2727
'devframe/utils/open': r('devframe/src/utils/open.ts'),
2828
'devframe/utils/promise': r('devframe/src/utils/promise.ts'),
29-
'devframe/utils/schema': r('devframe/src/utils/schema.ts'),
29+
'devframe/utils/simple-schema': r('devframe/src/utils/simple-schema.ts'),
3030
'devframe/utils/scope': r('devframe/src/utils/scope.ts'),
3131
'devframe/utils/serve-static': r('devframe/src/utils/serve-static.ts'),
3232
'devframe/utils/shared-state': r('devframe/src/utils/shared-state.ts'),

docs/guide/devframe-definition.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Every Devframe tool starts with a single `defineDevframe` call. The returned `De
1010

1111
```ts twoslash
1212
import { defineDevframe, defineRpcFunction } from 'devframe'
13-
import * as v from 'valibot'
13+
import * as v from 'valibot' // npm i valibot
1414
1515
export default defineDevframe({
1616
id: 'my-devframe',

docs/guide/rpc.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ sequenceDiagram
2222

2323
```ts
2424
import { defineRpcFunction } from 'devframe'
25-
import { s } from 'devframe/utils/schema' // or bring your own: valibot / zod / arktype
25+
import * as v from 'valibot' // npm i valibot (or use zod / arktype)
2626
2727
export const getModules = defineRpcFunction({
2828
name: 'get-modules', // bare — the scope namespaces it to `my-devframe:get-modules`
2929
type: 'query',
30-
args: [s.object({ limit: s.number() })],
31-
returns: s.array(s.object({ id: s.string(), size: s.number() })),
30+
args: [v.object({ limit: v.number() })],
31+
returns: v.array(v.object({ id: v.string(), size: v.number() })),
3232
setup: ctx => ({
3333
handler: async ({ limit }) => {
3434
// `ctx` is the full DevframeNodeContext.
@@ -77,14 +77,14 @@ Use `static` for data collected once during `setup` and shipped to read-only sta
7777

7878
Handlers accept any serializable arguments. Declare `args` schemas — any [Standard Schema](https://standardschema.dev/) validator (valibot, zod, arktype, …) — and each argument is validated at the boundary before the handler runs; a mismatch is rejected with a coded diagnostic. Validation guards the payload without rewriting it, so extra object fields the schema doesn't mention still reach the handler.
7979

80-
Devframe forces no validator on you. Bring the one you already use, or reach for the built-in zero-dependency builder at `devframe/utils/schema` (imported as `s` below):
80+
Devframe forces no validator on you: bring whichever [Standard Schema](https://standardschema.dev/) validator you prefer (valibot, zod, arktype) and install it yourself. The examples here use valibot (`npm i valibot`):
8181

8282
```ts
8383
defineRpcFunction({
8484
name: 'get-file',
8585
type: 'query',
86-
args: [s.object({ path: s.string(), includeSource: s.optional(s.boolean()) })],
87-
returns: s.object({ path: s.string(), source: s.optional(s.string()) }),
86+
args: [v.object({ path: v.string(), includeSource: v.optional(v.boolean()) })],
87+
returns: v.object({ path: v.string(), source: v.optional(v.string()) }),
8888
setup: () => ({
8989
handler: async ({ path, includeSource }) => ({
9090
path,
@@ -94,7 +94,7 @@ defineRpcFunction({
9494
})
9595
```
9696

97-
Prefer a single object argument (`args: [s.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes.
97+
Prefer a single object argument (`args: [v.object({ ... })]`) over positional args — property names are self-describing and agents/IDEs work best with object shapes.
9898

9999
> [!WARNING]
100100
> Declared `args`/`returns` schemas are enforced at runtime — a call whose arguments, or a handler whose return value, fail the schema is rejected with `DF0043` / `DF0044`. Make sure each schema matches what the function actually accepts and returns; a schema stricter than reality will now reject calls that previously ran.
@@ -247,7 +247,7 @@ defineRpcFunction({
247247
name: 'build-meta',
248248
type: 'static',
249249
args: [],
250-
returns: s.object({ version: s.string(), builtAt: s.number() }),
250+
returns: v.object({ version: v.string(), builtAt: v.number() }),
251251
setup: () => ({
252252
handler: async () => ({ version: '1.0.0', builtAt: Date.now() }),
253253
}),
@@ -311,8 +311,8 @@ defineRpcFunction({
311311
name: 'get-modules',
312312
type: 'query',
313313
jsonSerializable: true,
314-
args: [s.object({ limit: s.number() })],
315-
returns: s.array(s.object({ id: s.string(), size: s.number() })),
314+
args: [v.object({ limit: v.number() })],
315+
returns: v.array(v.object({ id: v.string(), size: v.number() })),
316316
agent: {
317317
description: 'List the N largest modules in the current build. Safe to call freely.',
318318
title: 'List modules',

docs/guide/standalone-cli.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ const payload = await my.rpc.call('get-payload')
168168

169169
## Typed CLI flags
170170

171-
For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below, or zod / arktype / devframe's built-in `s`) so they're validated at parse time and typed at the call site:
171+
For flags that are specific to your tool, declare them with any [Standard Schema](https://standardschema.dev/) validator (valibot below`npm i valibot` — or zod / arktype) so they're validated at parse time and typed at the call site:
172172

173173
```ts
174174
import type { InferCliFlags } from 'devframe/adapters/cac'
175175
import { defineDevframe } from 'devframe'
176176
import { defineCliFlags } from 'devframe/adapters/cac'
177-
import * as v from 'valibot'
177+
import * as v from 'valibot' // npm i valibot
178178
179179
const appFlags = defineCliFlags({
180180
depth: v.pipe(v.number(), v.integer()),

docs/guide/streaming.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Create the channel once in `setup`. Channels are framework-neutral, so the same
2929

3030
```ts
3131
import { defineDevframe, defineRpcFunction } from 'devframe'
32-
import * as v from 'valibot'
32+
import * as v from 'valibot' // npm i valibot
3333
3434
export default defineDevframe({
3535
id: 'my-devframe',

docs/helpers/common-rpc-functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ defineDevframe({
2828
| `KNOWN_EDITORS` || `readonly string[]` || The editor commands `openInEditor`'s `editor` argument accepts (`code`, `vim`, `subl`, `idea`, …). |
2929
| `KnownEditor` || type || Union of `KNOWN_EDITORS`. |
3030

31-
Both functions are `action`-type RPCs returning `void` and declare their arguments with devframe's built-in zero-dependency `s` builder from `devframe/utils/schema` `openInEditor`'s `editor` argument is `s.optional(s.picklist(KNOWN_EDITORS))`, so a value outside `KNOWN_EDITORS` fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs.
31+
Both functions are `action`-type RPCs returning `void`, and their arguments are schema-validated `openInEditor`'s `editor` argument is restricted to `KNOWN_EDITORS`, so a value outside that list fails validation rather than reaching the underlying `launch-editor` process spawn. Both handlers dynamically `import()` their underlying `devframe/utils/*` implementation, so the `launch-editor` and `open` dependencies only load when the recipe actually runs.
3232

3333
The `devframe/recipes/open-helpers` entry (`openHelpers`) remains as a deprecated alias for this module — new code should import `commonRpcFunctions` from `devframe/recipes/common-rpc-functions`.
3434

packages/devframe/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"./utils/nanoid": "./dist/utils/nanoid.mjs",
5151
"./utils/open": "./dist/utils/open.mjs",
5252
"./utils/promise": "./dist/utils/promise.mjs",
53-
"./utils/schema": "./dist/utils/schema.mjs",
53+
"./utils/simple-schema": "./dist/utils/simple-schema.mjs",
5454
"./utils/scope": "./dist/utils/scope.mjs",
5555
"./utils/serve-static": "./dist/utils/serve-static.mjs",
5656
"./utils/shared-state": "./dist/utils/shared-state.mjs",

packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { s } from 'devframe/utils/schema'
1+
import { s } from 'devframe/utils/simple-schema'
22
import { describe, expect, it } from 'vitest'
33
import { argsToJsonSchema, returnToJsonSchema } from '../to-json-schema'
44

packages/devframe/src/recipes/common-rpc-functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { s } from 'devframe/utils/schema'
1+
import { s } from 'devframe/utils/simple-schema'
22
import { defineRpcFunction } from '../rpc/define'
33

44
/**

packages/devframe/src/recipes/interactive-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { DevframeNodeContext, DevframeNodeRpcSession } from 'devframe/types'
22
import type { DevframeAuthHandler } from '../node/auth'
33
import { colors } from 'devframe/utils/colors'
4-
import { s } from 'devframe/utils/schema'
4+
import { s } from 'devframe/utils/simple-schema'
55
import { DEVFRAME_AUTH_TOKEN_QUERY_PARAM, isAnonymousRpcMethod } from '../constants'
66
import { buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, verifyAuthToken } from '../node/auth/state'
77
import { getInternalContext } from '../node/hub-internals/context'

0 commit comments

Comments
 (0)