Skip to content

Commit df0195b

Browse files
antfuclaude
andauthored
feat(devframe): jsonSerializable declaration for RPC + per-call wire dispatch (#301)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1a2a81d commit df0195b

45 files changed

Lines changed: 1175 additions & 53 deletions

Some content is hidden

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

devframe/docs/.vitepress/sidebar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[]
2626
text: 'Error Reference',
2727
link: `${prefix}/errors/`,
2828
collapsed: true,
29-
items: Array.from({ length: 18 }, (_, i) => {
29+
items: Array.from({ length: 28 }, (_, i) => {
3030
const code = `DF${String(i + 1).padStart(4, '0')}`
3131
return { text: code, link: `${prefix}/errors/${code}` }
3232
}),

devframe/docs/errors/DF0019.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0019: Agent Requires JSON-Serializable RPC
6+
7+
> Package: `devframe`
8+
9+
## Message
10+
11+
> RPC function "`{name}`" has `agent` set but `jsonSerializable` is not `true` — MCP requires JSON-serializable data.
12+
13+
## Cause
14+
15+
The `agent` field exposes an RPC function as an MCP tool. MCP and the underlying schema-conversion path (`@valibot/to-json-schema`) only consume JSON-shaped data. Functions whose payloads can include `Map`, `Set`, `Date`, `BigInt`, circular references, or class instances cannot be safely advertised to agents.
16+
17+
A registered function is rejected when `agent` is present and `jsonSerializable` is not explicitly `true`.
18+
19+
## Example
20+
21+
```ts
22+
defineRpcFunction({
23+
name: 'my-plugin:summary',
24+
agent: { description: 'Returns a summary' },
25+
// missing `jsonSerializable: true` → registration throws DF0019
26+
handler: () => ({ items: [1, 2, 3] }),
27+
})
28+
```
29+
30+
## Fix
31+
32+
Either declare the payload as JSON-safe:
33+
34+
```ts
35+
defineRpcFunction({
36+
name: 'my-plugin:summary',
37+
jsonSerializable: true,
38+
agent: { description: 'Returns a summary' },
39+
handler: () => ({ items: [1, 2, 3] }),
40+
})
41+
```
42+
43+
Or remove `agent` to keep the function as an internal RPC (no agent exposure):
44+
45+
```ts
46+
defineRpcFunction({
47+
name: 'my-plugin:summary',
48+
handler: () => new Map([['a', 1]]),
49+
})
50+
```
51+
52+
## Source
53+
54+
`packages/devframe/src/rpc/collector.ts`

devframe/docs/errors/DF0020.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0020: Non-JSON Value in JSON-Serializable RPC
6+
7+
> Package: `devframe`
8+
9+
## Message
10+
11+
> RPC function "`{name}`" declares `jsonSerializable: true` but the value at "`{path}`" is a `{type}`.
12+
13+
## Cause
14+
15+
The function is declared `jsonSerializable: true`, which means its args and return value are encoded with strict `JSON.stringify` (both on the wire and in build dumps). The strict serializer rejects any value that JSON cannot round-trip losslessly:
16+
17+
- `Map`, `Set`, `WeakMap`, `WeakSet`
18+
- `Date` (silently coerced to ISO string by JSON)
19+
- `BigInt`
20+
- circular references
21+
- non-plain class instances
22+
- `undefined` leaves
23+
- `Symbol`
24+
- `Function`
25+
26+
When the strict serializer encounters one of these, it throws synchronously at the offending call rather than producing a corrupt payload.
27+
28+
## Example
29+
30+
```ts
31+
defineRpcFunction({
32+
name: 'my-plugin:graph',
33+
jsonSerializable: true,
34+
handler: () => ({
35+
nodes: new Map([['a', 1]]), // ← throws DF0020 with type=Map, path="nodes"
36+
}),
37+
})
38+
```
39+
40+
## Fix
41+
42+
Either drop `jsonSerializable: true` so the function uses `structured-clone-es` (round-trips `Map`, `Set`, etc.):
43+
44+
```ts
45+
defineRpcFunction({
46+
name: 'my-plugin:graph',
47+
// jsonSerializable: false (default) — Map/Set survive the wire and the dump
48+
handler: () => ({
49+
nodes: new Map([['a', 1]]),
50+
}),
51+
})
52+
```
53+
54+
Or convert the payload to a JSON-safe shape (e.g. an array of entries, an ISO string, a plain object) before returning. Note: removing `jsonSerializable: true` also disables `agent` exposure; if you need MCP, you must use a JSON-safe shape.
55+
56+
## Source
57+
58+
`packages/devframe/src/rpc/serialization.ts`

devframe/docs/errors/DF0021.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0021: RPC Function Already Registered
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0001`.
10+
11+
## Message
12+
13+
> RPC function "`{name}`" is already registered
14+
15+
## Cause
16+
17+
`ctx.rpc.register()` was called twice with the same `name`. RPC names must be unique within a devtool.
18+
19+
## Fix
20+
21+
Either give the second registration a distinct name, or pass `force: true` to overwrite the previous one (e.g. during HMR-driven re-registration).
22+
23+
```ts
24+
ctx.rpc.register(defineRpcFunction({ name: 'my-plugin:fn', handler: () => 1 }), true /* force */)
25+
```
26+
27+
## Source
28+
29+
`packages/devframe/src/rpc/collector.ts`

devframe/docs/errors/DF0022.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0022: RPC Function Not Registered (Update)
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0002`.
10+
11+
## Message
12+
13+
> RPC function "`{name}`" is not registered. Use register() to add new functions.
14+
15+
## Cause
16+
17+
`ctx.rpc.update()` was called for a function that was never registered. `update()` is for replacing an existing definition.
18+
19+
## Fix
20+
21+
Call `ctx.rpc.register()` first, or pass `force: true` to `update()` to register-or-replace in one call.
22+
23+
## Source
24+
25+
`packages/devframe/src/rpc/collector.ts`

devframe/docs/errors/DF0023.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0023: RPC Function Not Registered (Get)
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0003`.
10+
11+
## Message
12+
13+
> RPC function "`{name}`" is not registered
14+
15+
## Cause
16+
17+
A consumer asked for the schema or handler of a function that has never been registered with `ctx.rpc.register()`.
18+
19+
## Fix
20+
21+
Confirm the function name matches a registration. RPC names are namespaced — typos in the prefix are a common cause.
22+
23+
## Source
24+
25+
`packages/devframe/src/rpc/collector.ts`

devframe/docs/errors/DF0024.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0024: Missing RPC Handler
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0004`.
10+
11+
## Message
12+
13+
> Either handler or setup function must be provided for RPC function "`{name}`"
14+
15+
## Cause
16+
17+
The RPC definition has neither a `handler` nor a `setup` returning `{ handler }`. devframe has nothing to invoke when the function is called.
18+
19+
## Fix
20+
21+
Add either `handler: ...` directly on the definition, or `setup: ctx => ({ handler: ... })` if the handler depends on context.
22+
23+
## Source
24+
25+
`packages/devframe/src/rpc/handler.ts`

devframe/docs/errors/DF0025.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0025: Function Not in Dump Store
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0005`.
10+
11+
## Message
12+
13+
> Function "`{name}`" not found in dump store
14+
15+
## Cause
16+
17+
A static-mode client called an RPC function that was not baked into the build dump. This usually means the function was added after the dump was generated, or its name changed between build and runtime.
18+
19+
## Fix
20+
21+
Re-run `createBuild` to regenerate the dump, or check that the call site uses the same name registered on the server.
22+
23+
## Source
24+
25+
`packages/devframe/src/rpc/dumps.ts`

devframe/docs/errors/DF0026.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0026: No Dump Match
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0006`.
10+
11+
## Message
12+
13+
> No dump match for "`{name}`" with args: `{args}`
14+
15+
## Cause
16+
17+
A static-mode client called an RPC function with arguments that don't match any pre-computed record, and no `fallback` was set on the dump.
18+
19+
## Fix
20+
21+
Either widen the function's `dump.inputs` to cover the requested arguments, or provide `dump.fallback` so unmatched calls resolve to a default value instead of throwing.
22+
23+
```ts
24+
defineRpcFunction({
25+
name: 'my-plugin:get',
26+
dump: {
27+
inputs: [['known-id']],
28+
fallback: null,
29+
},
30+
})
31+
```
32+
33+
## Source
34+
35+
`packages/devframe/src/rpc/dumps.ts`

devframe/docs/errors/DF0027.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# DF0027: Invalid Dump Configuration
6+
7+
> Package: `devframe`
8+
9+
> Migrated from `DTK0007`.
10+
11+
## Message
12+
13+
> Function "`{name}`" with type "`{type}`" cannot have dump configuration. Only "static" and "query" types support dumps.
14+
15+
## Cause
16+
17+
A `dump` field was attached to an `'action'` or `'event'` function. These types perform side effects rather than returning queryable data — there is nothing meaningful to pre-compute.
18+
19+
## Fix
20+
21+
Drop the `dump` field, or change the function `type` to `'static'` / `'query'` if pre-computation is appropriate.
22+
23+
## Source
24+
25+
`packages/devframe/src/rpc/validation.ts`

0 commit comments

Comments
 (0)