Skip to content

Commit 528cc77

Browse files
committed
Merge remote-tracking branch 'origin/main' into docs/project-structure
2 parents bcf3005 + 5421c3e commit 528cc77

56 files changed

Lines changed: 294 additions & 282 deletions

Some content is hidden

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

docs/content/1.guide/4.rpc.md

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,7 @@ Scope with your devframe id, then a kebab-case action: `my-devframe:get-modules`
5656

5757
### Handler arguments
5858

59-
Handlers accept any serializable arguments. Declared `args` schemas validate each at the boundary; extra object fields still reach the handler.
60-
61-
> [!TIP]
62-
> If your app already pulls in **zod**, use it for your RPC schemas to reuse a dependency you already ship.
63-
64-
> [!WARNING]
65-
> Declared `args`/`returns` schemas are enforced at runtime; failing arguments or return values are rejected with `DF0043` / `DF0044`.
59+
Handlers accept any serializable arguments. Declared `args` and `returns` schemas are enforced at runtime; extra object fields still reach the handler.
6660

6761
### Setup vs handler
6862

@@ -198,15 +192,7 @@ The WS transport picks one of two encoders per function:
198192
| `false` (default) | `structured-clone-es` | `s:` | `Map`, `Set`, `Date`, `BigInt`, cycles, class instances |
199193
| `true` (opt-in) | strict `JSON.stringify` | _(unprefixed)_ | JSON-only |
200194

201-
When every function is JSON-flagged, the wire stays plain JSON.
202-
203-
### Discovering shape errors during dev
204-
205-
When a `jsonSerializable: true` handler returns a value JSON cannot round-trip (`Map`, `Date`, …), the strict serializer throws [`DF0020`](/errors/DF0020).
206-
207-
### MCP requires JSON
208-
209-
`agent: {...}` requires `jsonSerializable: true`; one without the other throws [`DF0019`](/errors/DF0019).
195+
When every function is JSON-flagged, the wire stays plain JSON. A `jsonSerializable: true` handler must return JSON-only values — `Map`, `Date`, and friends won't round-trip.
210196

211197
## Agent exposure
212198

@@ -230,6 +216,8 @@ defineRpcFunction({
230216
})
231217
```
232218

219+
Exposing a function over MCP requires `jsonSerializable: true`.
220+
233221
## What's next
234222

235223
- [Shared State](/guide/shared-state) — state synced across clients

docs/content/4.helpers/3.interactive-auth.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ As `auth` it wires `rpcFunctions`, `authorize`, and `onConnect` — see [Securit
2929
|--------|---------|---------|
3030
| `clientAuthTokens` | `undefined` | Pre-shared bearer tokens, always trusted. |
3131
| `banner` | a small boxed console message | Called with `{ code, url }`; prints via `printBanner()`. |
32+
| `onTrusted` | `undefined` | Called with `{ session, authToken }` once a code exchange succeeds, so a host rendering its own banner can retract it. |
3233
| `serverUrl` | `context.host.resolveOrigin()` | Magic-link base URL. |
3334

3435
Returns a `DevframeAuthHandler`:
@@ -57,4 +58,6 @@ if (!auth.authorize(methodName, session))
5758
auth.onConnect(peer, session)
5859
```
5960

61+
An exchange rotates the code and prints the new one, and `onTrusted` fires after that, so a host retracting a sticky notice drops that follow-up too and calls `auth.printBanner()` when it next wants a code on screen.
62+
6063
Auth storage is internal, not `devframe/node/hub-internals`.

docs/content/6.errors/DF0014.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ description: 'RPC function "{name}" has an invalid agent field — description m
99
1010
## Cause
1111

12-
An RPC function was defined with an `agent` field (opting it in for exposure to agents via the MCP adapter), but the required `description` property is missing or empty.
13-
14-
Agents rely on the description to decide when to invoke a tool. Empty or placeholder descriptions would produce unusable agent surface.
12+
An RPC function opts into agent exposure with an `agent` field, but its required `description` is missing or empty. Agents rely on the description to decide when to invoke a tool.
1513

1614
## Example
1715

@@ -28,7 +26,7 @@ defineRpcFunction({
2826

2927
## Fix
3028

31-
Provide a non-empty `description` (~1–3 sentences) explaining what the tool does and when agents should invoke it:
29+
Provide a non-empty `description` (~1–3 sentences) explaining what the tool does and when agents should invoke it, or remove the `agent` field to keep it RPC-only.
3230

3331
```ts
3432
defineRpcFunction({
@@ -41,8 +39,6 @@ defineRpcFunction({
4139
})
4240
```
4341

44-
If you didn't intend for this function to be agent-exposed, remove the `agent` field entirely (default-deny).
45-
4642
## Source
4743

4844
- [`packages/devframe/src/node/host-agent.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/host-agent.ts) — agent registration throws `DF0014` when a tool's `agent.description` is missing or empty.

docs/content/6.errors/DF0017.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ description: 'Failed to start MCP server ({transport}): {reason}'
1111

1212
The MCP server failed while initializing. Common reasons:
1313

14-
- `@modelcontextprotocol/server` is not installed. This is a peer dependency — add it to your devtool's dependencies.
15-
- The stdio transport threw during `connect()` (e.g. stdin/stdout is not available).
16-
- The route-based MCP server (`cli.mcp`) could not load its transport module — usually the missing SDK peer dependency.
14+
- The `@modelcontextprotocol/server` peer dependency is missing (the stdio and route-based transports both need it).
15+
- The stdio transport threw during `connect()` (e.g. stdin/stdout unavailable).
1716

1817
## Fix
1918

20-
- **Missing SDK**: `pnpm add @modelcontextprotocol/server` (or npm/yarn equivalent) in the package that imports `devframe/adapters/mcp` or enables `cli.mcp`.
21-
- **Transport init failure**: check the underlying error (attached as `cause`) for specifics.
19+
- **Missing SDK**: `pnpm add @modelcontextprotocol/server` in the package that imports `devframe/adapters/mcp` or enables `cli.mcp`.
20+
- **Transport failure**: inspect the underlying error attached as `cause`.
2221

2322
## Source
2423

docs/content/6.errors/DF0019.md

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,21 @@ description: 'RPC function "{name}" has agent set but jsonSerializable is not tr
99
1010
## Cause
1111

12-
The `agent` field exposes an RPC function as an MCP tool. MCP only consumes JSON-shaped data. Functions whose payloads can include `Map`, `Set`, `Date`, `BigInt`, circular references, or class instances cannot be safely advertised to agents.
13-
14-
A registered function is rejected when `agent` is present and `jsonSerializable` is not explicitly `true`.
12+
The `agent` field exposes an RPC function as an MCP tool, and MCP only consumes JSON-shaped data. A function with `agent` set is rejected unless it also declares `jsonSerializable: true`.
1513

1614
## Example
1715

1816
```ts
1917
defineRpcFunction({
2018
name: 'my-plugin:summary',
2119
agent: { description: 'Returns a summary' },
22-
// missing `jsonSerializable: true` → registration throws DF0019
23-
handler: () => ({ items: [1, 2, 3] }),
20+
handler: () => ({ items: [1, 2, 3] }), // ✗ throws DF0019 — missing jsonSerializable: true
2421
})
2522
```
2623

2724
## Fix
2825

29-
Either declare the payload as JSON-safe:
26+
Set `jsonSerializable: true` if the payload is JSON-safe, or remove `agent` to keep it RPC-only.
3027

3128
```ts
3229
defineRpcFunction({
@@ -37,15 +34,6 @@ defineRpcFunction({
3734
})
3835
```
3936

40-
Or remove `agent` to keep the function as an internal RPC (no agent exposure):
41-
42-
```ts
43-
defineRpcFunction({
44-
name: 'my-plugin:summary',
45-
handler: () => new Map([['a', 1]]),
46-
})
47-
```
48-
4937
## Source
5038

5139
- [`packages/devframe/src/rpc/collector.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/rpc/collector.ts)`RpcFunctionsCollectorBase.register()` throws `DF0019` when a definition has `agent` set but is not declared `jsonSerializable: true`.

docs/content/6.errors/DF0020.md

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,14 @@ description: 'RPC function "{name}" declares jsonSerializable: true but the valu
99
1010
## Cause
1111

12-
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:
12+
A `jsonSerializable: true` function encodes its args and return value with strict `JSON.stringify` (on the wire and in build dumps). The serializer throws at the offending value rather than emit a corrupt payload when it hits anything JSON cannot round-trip losslessly:
1313

1414
- `Map`, `Set`, `WeakMap`, `WeakSet`
15-
- `Date` (silently coerced to ISO string by JSON)
16-
- `BigInt`
15+
- `Date` (JSON coerces it to an ISO string)
16+
- `BigInt`, `Symbol`, `Function`
1717
- circular references
1818
- non-plain class instances
1919
- `undefined` leaves
20-
- `Symbol`
21-
- `Function`
22-
23-
When the strict serializer encounters one of these, it throws synchronously at the offending call rather than producing a corrupt payload.
2420

2521
## Example
2622

@@ -29,26 +25,14 @@ defineRpcFunction({
2925
name: 'my-plugin:graph',
3026
jsonSerializable: true,
3127
handler: () => ({
32-
nodes: new Map([['a', 1]]), // throws DF0020 with type=Map, path="nodes"
28+
nodes: new Map([['a', 1]]), // throws DF0020 type=Map, path="nodes"
3329
}),
3430
})
3531
```
3632

3733
## Fix
3834

39-
Either drop `jsonSerializable: true` so the function uses `structured-clone-es` (round-trips `Map`, `Set`, etc.):
40-
41-
```ts
42-
defineRpcFunction({
43-
name: 'my-plugin:graph',
44-
// jsonSerializable: false (default) — Map/Set survive the wire and the dump
45-
handler: () => ({
46-
nodes: new Map([['a', 1]]),
47-
}),
48-
})
49-
```
50-
51-
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.
35+
Drop `jsonSerializable: true` to fall back to `structured-clone-es` (round-trips `Map`, `Set`, etc.), or convert the payload to a JSON-safe shape before returning. Keeping `agent` exposure requires the JSON-safe shape.
5236

5337
## Source
5438

docs/content/6.errors/DF0029.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ description: 'Stream "{channel}#{id}" dropped {dropped} chunk(s) after exceeding
99
1010
## Cause
1111

12-
A streaming subscriber's queue grew past its `highWaterMark` because the consumer is slower than the producer. The oldest chunks were dropped to keep memory bounded.
13-
14-
This is a soft warning — the stream keeps running and remaining chunks still flow.
12+
The consumer is slower than the producer, so the subscriber's queue grew past its `highWaterMark` and the oldest chunks were dropped to keep memory bounded. The stream keeps running and remaining chunks still flow.
1513

1614
## Fix
1715

1816
- Raise `highWaterMark` on `rpc.streaming.subscribe(channel, id, { highWaterMark })` if the consumer can occasionally catch up.
19-
- Slow the producer so it doesn't outpace the wire (e.g. throttle, debounce, or batch chunks server-side).
20-
- Switch to `sharedState` if you only need the latest value rather than every intermediate chunk.
17+
- Slow the producer throttle, debounce, or batch chunks server-side.
18+
- Use `sharedState` if you only need the latest value rather than every chunk.
2119

2220
## Source
2321

docs/content/6.errors/DF0030.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ description: 'Stream "{channel}#{id}" is unknown — no producer has called chan
99
1010
## Cause
1111

12-
A client subscribed to a stream id that the server-side channel doesn't know about. Either the producer never started a stream with that id, the producer already ended it and `replayWindow` is `0`, or the client passed the wrong id.
12+
A client subscribed to a stream id no server-side producer has started. Either the producer never called `channel.start({ id })`, it already ended the stream and `replayWindow` is `0`, or the client passed the wrong id.
1313

1414
## Fix
1515

16-
- Make sure the action that returns the stream id runs **before** the client subscribes — typically by awaiting `rpc.call('your-action')` and using the returned id.
17-
- Bump `replayWindow` on `ctx.rpc.streaming.create(name, { replayWindow })` if you need clients to resume after the producer has finished but kept the buffer warm.
18-
- Check the id is propagated correctly across boundaries (action return value → component prop → subscribe call).
16+
- Run the producer before clients subscribe — typically await `rpc.call('your-action')` and use the returned id.
17+
- Bump `replayWindow` on `ctx.rpc.streaming.create(name, { replayWindow })` to let clients resume after the producer finishes.
18+
- Verify the id propagates correctly (action return → component prop → subscribe call).
1919

2020
## Source
2121

docs/content/6.errors/DF0033.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,12 @@ description: 'Failed to start dev RPC bridge for "{id}": {reason}'
99
1010
## Cause
1111

12-
`devframeViteBridge()` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA (Vite, Nuxt, Astro, etc.) with devframe's RPC backend. Common reasons:
13-
14-
- The preferred port is in use and no fallback range was configured.
15-
- Calling `def.setup(ctx)` threw — the devframe's own setup logic surfaced an error.
16-
- A required peer (e.g. `get-port-please` or `h3`) is missing or mismatched.
17-
18-
This is a soft warning — the surrounding Vite dev server keeps running, but the host-served SPA will fail its `__connection.json` lookup until the bridge starts.
12+
`devframeViteBridge()` (from `@devframes/vite`) could not bring up the bridge dev server that pairs a host-served SPA with devframe's RPC backend — usually because the preferred port is taken with no fallback range, or `def.setup(ctx)` threw. The surrounding Vite dev server keeps running, but the SPA's `__connection.json` lookup fails until the bridge starts.
1913

2014
## Fix
2115

22-
- Pin a port via `cli.port` / `cli.portRange` on the devframe definition, or via `port` on `devframeViteBridge`.
23-
- Inspect the `reason` (or the attached `cause`) for the underlying error — fix the setup function or free the port.
24-
- For Nuxt: pass `devMiddleware: { port: <free-port> }` to the `@devframes/nuxt` module.
16+
- Pin a port via `cli.port` / `cli.portRange` on the definition, or `port` on `devframeViteBridge`.
17+
- Inspect `reason` (or the attached `cause`) to fix the setup function or free the port.
2518

2619
## Source
2720

docs/content/6.errors/DF0035.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
title: 'DF0035: Storage File Persist Failed'
3+
description: 'Failed to persist storage file: {filepath}'
4+
---
5+
6+
## Message
7+
8+
> Failed to persist storage file: `{filepath}`
9+
10+
## Cause
11+
12+
A shared-state store's debounced write to disk failed — the directory could not be created, or the temp-file write / atomic rename threw. Usually the storage directory is not writable or the disk is full.
13+
14+
## Fix
15+
16+
Check that the storage directory is writable and has free space.
17+
18+
## Source
19+
20+
- [`packages/devframe/src/node/storage.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/storage.ts) — the debounced `updated` handler reports this when writing the temp file or renaming it into place fails.

0 commit comments

Comments
 (0)