devframe 0.9.8 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/_shared-CCzf9Hub.d.mts +83 -0
  2. package/dist/_shared-U8D-xzrp.mjs +115 -0
  3. package/dist/adapters/build.d.mts +6 -6
  4. package/dist/adapters/build.mjs +33 -36
  5. package/dist/adapters/cac.d.mts +12 -1
  6. package/dist/adapters/cac.mjs +8 -8
  7. package/dist/adapters/dev.d.mts +17 -17
  8. package/dist/adapters/dev.mjs +2 -2
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +31 -28
  11. package/dist/adapters/initiate.mjs +1 -1
  12. package/dist/adapters/mcp.d.mts +28 -14
  13. package/dist/adapters/mcp.mjs +76 -40
  14. package/dist/{agent-tool-name-EgfoFO8C.mjs → agent-tool-name-DGzeuWcO.mjs} +2 -2
  15. package/dist/cli/main.d.mts +1 -1
  16. package/dist/cli/main.mjs +41 -17
  17. package/dist/client/index.d.mts +36 -36
  18. package/dist/client/index.mjs +90 -52
  19. package/dist/{client-assets-C7SXgTWY.mjs → client-assets-DNdhagGm.mjs} +2 -2
  20. package/dist/constants.d.mts +8 -7
  21. package/dist/constants.mjs +7 -6
  22. package/dist/{context-omlma_1n.mjs → context-B29Oh0oA.mjs} +166 -93
  23. package/dist/{context-DftYyXaR.d.mts → context-BJiKJqyW.d.mts} +5 -5
  24. package/dist/{context-CLF4-8Pp.d.mts → context-CHW4BZOj.d.mts} +203 -134
  25. package/dist/{context-KUqAfawQ.mjs → context-jXO5vzjH.mjs} +3 -3
  26. package/dist/{crypto-token-XCqTSMg9.mjs → crypto-token-TVbGGFuj.mjs} +1 -1
  27. package/dist/{dev-CzkPCJwi.mjs → dev-CRYHx7iG.mjs} +73 -40
  28. package/dist/{diagnostics-BXWW3VK-.mjs → diagnostics-DZA6KQ_Z.mjs} +32 -19
  29. package/dist/{events-4daw0uK9.mjs → events-BV4Dj59a.mjs} +2 -2
  30. package/dist/{services-install-CqqQVls5.mjs → host-agent-CKqSTEX9.mjs} +15 -151
  31. package/dist/{host-h3-fRbF9yor.mjs → host-h3-BROI6SQ4.mjs} +2 -2
  32. package/dist/in-page-channel/index.d.mts +14 -14
  33. package/dist/in-page-channel/index.mjs +63 -54
  34. package/dist/{index-D3k0BIhe.d.mts → index-CNC390b_.d.mts} +4 -4
  35. package/dist/{index-BzyopqxV.d.mts → index-pIVt92jP.d.mts} +9 -9
  36. package/dist/index.d.mts +8 -8
  37. package/dist/index.mjs +2 -2
  38. package/dist/{instance-registry-BjlMQbFB.mjs → instance-registry-DCq762YJ.mjs} +9 -9
  39. package/dist/{instance-shell-Tcp5hGoq.d.mts → instance-shell-DZipaP9k.d.mts} +18 -18
  40. package/dist/{instance-shell-CpVW4zG0.mjs → instance-shell-DvYCwrDy.mjs} +223 -67
  41. package/dist/internal/index.d.mts +45 -26
  42. package/dist/internal/index.mjs +10 -10
  43. package/dist/node/auth.d.mts +2 -2
  44. package/dist/node/auth.mjs +2 -2
  45. package/dist/node/hub-internals.d.mts +2 -2
  46. package/dist/node/hub-internals.mjs +2 -2
  47. package/dist/node/index.d.mts +5 -5
  48. package/dist/node/index.mjs +2 -2
  49. package/dist/{nostics-SPGDgHEP.d.mts → nostics-0sKe5HIj.d.mts} +2 -2
  50. package/dist/{nostics-CzECRXpE.mjs → nostics-D0PvLZsn.mjs} +1 -1
  51. package/dist/origin-DGDH8BI9.d.mts +45 -0
  52. package/dist/origin-DZit9g29.mjs +80 -0
  53. package/dist/recipes/common-rpc-functions.d.mts +5 -5
  54. package/dist/recipes/common-rpc-functions.mjs +3 -3
  55. package/dist/recipes/interactive-auth.d.mts +6 -6
  56. package/dist/recipes/interactive-auth.mjs +6 -6
  57. package/dist/{remote-assets-DezQmPQU.mjs → remote-assets-DMFzLSoH.mjs} +27 -7
  58. package/dist/{remote-assets-B13_vRkJ.d.mts → remote-assets-eBvSKZMS.d.mts} +7 -7
  59. package/dist/{revoke-COLW0cOi.mjs → revoke-cqztiCX4.mjs} +1 -1
  60. package/dist/rpc/dump.d.mts +19 -12
  61. package/dist/rpc/dump.mjs +2 -2
  62. package/dist/rpc/index.d.mts +2 -2
  63. package/dist/rpc/index.mjs +2 -3
  64. package/dist/rpc/transports/sse-client.d.mts +1 -1
  65. package/dist/rpc/transports/sse-client.mjs +30 -14
  66. package/dist/rpc/transports/sse-server.d.mts +4 -4
  67. package/dist/rpc/transports/sse-server.mjs +7 -7
  68. package/dist/rpc/transports/ws-bun.d.mts +5 -5
  69. package/dist/rpc/transports/ws-bun.mjs +8 -2
  70. package/dist/rpc/transports/ws-client.d.mts +1 -1
  71. package/dist/rpc/transports/ws-client.mjs +2 -2
  72. package/dist/rpc/transports/ws-deno.d.mts +4 -4
  73. package/dist/rpc/transports/ws-deno.mjs +8 -2
  74. package/dist/rpc/transports/ws-server.d.mts +2 -1
  75. package/dist/rpc/transports/ws-server.mjs +232 -1
  76. package/dist/{rpc-core-DlHd276T.mjs → rpc-core-CvkU23bQ.mjs} +14 -2
  77. package/dist/{rpc-core-B3W3z9ff.d.mts → rpc-core-DsyDViQy.d.mts} +4 -4
  78. package/dist/serialization-DFg5sUt6.mjs +116 -0
  79. package/dist/services-install-Ben4yh52.mjs +154 -0
  80. package/dist/session-DPfj6zQG.mjs +16 -0
  81. package/dist/{shared-state--sduc6Xi.mjs → shared-state-CaxB1q0h.mjs} +2 -2
  82. package/dist/{simple-schema-DQPZrAaZ.mjs → simple-schema-Bi40l-P6.mjs} +2 -2
  83. package/dist/{sse-client-DwX021ST.d.mts → sse-client-BQ4jS3eO.d.mts} +4 -4
  84. package/dist/{state-CK9LjrnT.mjs → state-Mwt6--el.mjs} +17 -9
  85. package/dist/{storage-BXNVhKyR.mjs → storage-B70AXK64.mjs} +2 -2
  86. package/dist/types/index.d.mts +6 -6
  87. package/dist/{types-CMdPYhOx.d.mts → types-Dq4i156u.d.mts} +17 -17
  88. package/dist/utils/agent-tool-name.d.mts +2 -2
  89. package/dist/utils/agent-tool-name.mjs +2 -2
  90. package/dist/utils/colors.d.mts +1 -1
  91. package/dist/utils/crypto-token.d.mts +1 -1
  92. package/dist/utils/crypto-token.mjs +1 -1
  93. package/dist/utils/events.d.mts +1 -1
  94. package/dist/utils/nostics.d.mts +1 -1
  95. package/dist/utils/nostics.mjs +1 -1
  96. package/dist/utils/open.mjs +1 -1
  97. package/dist/utils/origin.d.mts +2 -0
  98. package/dist/utils/origin.mjs +80 -0
  99. package/dist/utils/remote-assets.d.mts +2 -2
  100. package/dist/utils/remote-assets.mjs +1 -1
  101. package/dist/utils/serve-static.d.mts +4 -4
  102. package/dist/utils/serve-static.mjs +56 -19
  103. package/dist/utils/shared-state.d.mts +1 -1
  104. package/dist/utils/shared-state.mjs +1 -1
  105. package/dist/utils/simple-schema.d.mts +5 -5
  106. package/dist/utils/simple-schema.mjs +2 -2
  107. package/dist/utils/streaming-channel.d.mts +1 -1
  108. package/dist/utils/streaming-channel.mjs +2 -2
  109. package/dist/utils/when.d.mts +5 -5
  110. package/dist/{validation-YrZH6atx.mjs → validation-CfV1s0SF.mjs} +2 -2
  111. package/dist/{wire-codec-0K-o5MYW.mjs → wire-codec-BHGt0sSs.mjs} +3 -3
  112. package/dist/{static-DV_qUSRS.mjs → write-BuLCrn9h.mjs} +153 -137
  113. package/dist/{ws-client-7_lZi-B9.d.mts → ws-client-Xpxt0oPA.d.mts} +3 -3
  114. package/dist/{ws-server-VyHlanzy.d.mts → ws-server-D2nv68Hg.d.mts} +13 -37
  115. package/package.json +3 -6
  116. package/skills/devframe/SKILL.md +5 -3
  117. package/dist/_shared-BM3PdYli.mjs +0 -68
  118. package/dist/_shared-mtXpcJIv.d.mts +0 -41
  119. package/dist/diagnostics-CD8nlgll.mjs +0 -39
  120. package/dist/serialization-BGzEwAdr.mjs +0 -77
  121. package/dist/ws-server-Y_AumUQH.mjs +0 -294
@@ -8,10 +8,10 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
8
8
  * best-effort validator that exists only so devframe's own first-party
9
9
  * packages (recipes, built-in plugins) can declare `args`/`returns`/flag
10
10
  * schemas without taking on a validator dependency. It implements a small
11
- * subset of primitives and approximates refinements — it is not a
11
+ * subset of primitives and approximates refinements; it is not a
12
12
  * general-purpose validator.
13
13
  *
14
- * For your own code, prefer a real Standard Schema validator — **valibot**,
14
+ * For your own code, prefer a real Standard Schema validator: **valibot**,
15
15
  * **zod**, or **arktype**. Devframe's RPC and CLI-flag layers accept any of
16
16
  * them; install the one you like and use it directly:
17
17
  *
@@ -46,7 +46,7 @@ declare function string(): SimpleSchema<string>;
46
46
  declare function number(): SimpleSchema<number>;
47
47
  /** A boolean. */
48
48
  declare function boolean(): SimpleSchema<boolean>;
49
- /** `undefined` — mirrors valibot's `void`. */
49
+ /** `undefined`, mirroring valibot's `void`. */
50
50
  declare function voidType(): SimpleSchema<void>;
51
51
  /** `null`. */
52
52
  declare function nullType(): SimpleSchema<null>;
@@ -64,7 +64,7 @@ declare function array<T extends StandardSchemaV1>(item: T): SimpleSchema<Standa
64
64
  type Prettify<T> = { [K in keyof T]: T[K]; } & {};
65
65
  /**
66
66
  * Map a shape to its object type, turning fields whose type includes
67
- * `undefined` (i.e. `optional()`) into optional keys — mirroring how
67
+ * `undefined` (i.e. `optional()`) into optional keys, mirroring how
68
68
  * valibot/zod render `optional` object entries.
69
69
  */
70
70
  type InferField<T extends StandardSchemaV1, Mode extends 'input' | 'output'> = Mode extends 'input' ? StandardSchemaV1.InferInput<T> : StandardSchemaV1.InferOutput<T>;
@@ -78,7 +78,7 @@ declare function nullable<T extends StandardSchemaV1>(inner: T): SimpleSchema<St
78
78
  /** Attach a human-readable description (used for CLI option help). */
79
79
  declare function describe<T extends SimpleSchema<any, any>>(schema: T, description: string): T;
80
80
  /**
81
- * Grouped access to every builder — `s.string()`, `s.object({ ... })`,
81
+ * Grouped access to every builder: `s.string()`, `s.object({ ... })`,
82
82
  * `s.void()`, etc. Handy for a valibot-like `import { s } from
83
83
  * 'devframe/utils/simple-schema'` call site.
84
84
  */
@@ -37,7 +37,7 @@ function number() {
37
37
  function boolean() {
38
38
  return make("boolean", (v) => typeof v === "boolean" ? ok(v) : fail("Expected a boolean"));
39
39
  }
40
- /** `undefined` — mirrors valibot's `void`. */
40
+ /** `undefined`, mirroring valibot's `void`. */
41
41
  function voidType() {
42
42
  return make("void", (v) => v === void 0 ? ok(void 0) : fail("Expected undefined"));
43
43
  }
@@ -130,7 +130,7 @@ function describe(schema, description) {
130
130
  };
131
131
  }
132
132
  /**
133
- * Grouped access to every builder — `s.string()`, `s.object({ ... })`,
133
+ * Grouped access to every builder: `s.string()`, `s.object({ ... })`,
134
134
  * `s.void()`, etc. Handy for a valibot-like `import { s } from
135
135
  * 'devframe/utils/simple-schema'` call site.
136
136
  */
@@ -1,2 +1,2 @@
1
- import { C as createStreamSink, S as createStreamReader, _ as CreateStreamSinkOptions, b as StreamSink, g as CreateStreamReaderOptions, h as BufferedChunk, v as StreamErrorPayload, x as StreamSinkEvents, y as StreamReader } from "../context-CLF4-8Pp.mjs";
1
+ import { C as createStreamSink, S as createStreamReader, _ as CreateStreamSinkOptions, b as StreamSink, g as CreateStreamReaderOptions, h as BufferedChunk, v as StreamErrorPayload, x as StreamSinkEvents, y as StreamReader } from "../context-CHW4BZOj.mjs";
2
2
  export { BufferedChunk, CreateStreamReaderOptions, CreateStreamSinkOptions, StreamErrorPayload, StreamReader, StreamSink, StreamSinkEvents, createStreamReader, createStreamSink };
@@ -6,7 +6,7 @@ var StreamClosedError = class extends Error {
6
6
  name = "StreamClosedError";
7
7
  };
8
8
  /**
9
- * Build a server-side stream sink. RPC-agnostic — the RPC host wires
9
+ * Build a server-side stream sink. RPC-agnostic; the RPC host wires
10
10
  * `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
11
11
  * for late or reconnecting subscribers.
12
12
  */
@@ -77,7 +77,7 @@ function createStreamSink(options = {}) {
77
77
  };
78
78
  }
79
79
  /**
80
- * Build a client-side stream reader. RPC-agnostic — the RPC host calls
80
+ * Build a client-side stream reader. RPC-agnostic; the RPC host calls
81
81
  * `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
82
82
  * terminal frame. Consumers iterate with `for await` or pipe `readable`.
83
83
  */
@@ -4,13 +4,13 @@ import { WhenExpression as WhenExpression$1 } from "whenexpr";
4
4
  * Context object for evaluating `when` expressions.
5
5
  *
6
6
  * Built-in variables:
7
- * - `clientType` — `'embedded' | 'standalone'`
8
- * - `dockOpen` — whether the dock panel is open
9
- * - `paletteOpen` — whether the command palette is open
10
- * - `dockSelectedId` — ID of the selected dock entry (empty string if none)
7
+ * - `clientType`: `'embedded' | 'standalone'`
8
+ * - `dockOpen`: whether the dock panel is open
9
+ * - `paletteOpen`: whether the command palette is open
10
+ * - `dockSelectedId`: ID of the selected dock entry (empty string if none)
11
11
  *
12
12
  * Plugins can add namespaced variables using dot or colon separators:
13
- * - `vite.mode`, `vite:mode` — stored as `{ 'vite.mode': 'development' }` or nested `{ vite: { mode: 'development' } }`
13
+ * - `vite.mode`, `vite:mode`: stored as `{ 'vite.mode': 'development' }` or nested `{ vite: { mode: 'development' } }`
14
14
  *
15
15
  * Supported operators: `!`, `==`, `!=`, `===`, `!==`, `<`, `<=`, `>`, `>=`,
16
16
  * `&&`, `||`, `+`, `-`, `*`, `/`, `%`. Parentheses, number and string literals
@@ -1,4 +1,4 @@
1
- import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
1
+ import { r as diagnostics } from "./serialization-DFg5sUt6.mjs";
2
2
  import { createHash } from "node:crypto";
3
3
  //#region ../../node_modules/.pnpm/ohash@2.0.12/node_modules/ohash/dist/_chunks/is-equal.mjs
4
4
  function serialize(input) {
@@ -220,7 +220,7 @@ async function validateRpcArgs(name, argsSchema, args) {
220
220
  /**
221
221
  * Validate a handler's resolved return value against its declared schema.
222
222
  * Throws `DF0039` when the value fails the schema, otherwise returns the
223
- * original value unchanged (guard-only, never rewriting the payload — see
223
+ * original value unchanged (guard-only, never rewriting the payload; see
224
224
  * {@link validateRpcArgs}). Passes through when no return schema is set.
225
225
  *
226
226
  * @internal
@@ -1,4 +1,4 @@
1
- import { n as strictJsonStringify } from "./serialization-BGzEwAdr.mjs";
1
+ import { n as strictJsonStringify } from "./serialization-DFg5sUt6.mjs";
2
2
  import { n as structuredCloneStringify, t as structuredCloneParse } from "./structured-clone-CbAV5rFI.mjs";
3
3
  //#region src/rpc/wire-codec.ts
4
4
  const EMPTY_WIRE_DEFS = /* @__PURE__ */ new Map();
@@ -8,7 +8,7 @@ const EMPTY_WIRE_DEFS = /* @__PURE__ */ new Map();
8
8
  * JSON (methods declared `jsonSerializable: true`) and `s:`-prefixed
9
9
  * structured-clone (everything else, including all error envelopes), with a
10
10
  * request-id → method map so a response independently picks the same
11
- * encoder as its request. One codec per connection — request-id spaces
11
+ * encoder as its request. One codec per connection; request-id spaces
12
12
  * don't collide across connections.
13
13
  *
14
14
  * @internal
@@ -35,7 +35,7 @@ function createRpcWireCodec(definitions = EMPTY_WIRE_DEFS) {
35
35
  }
36
36
  /**
37
37
  * Peek at a wire frame's birpc envelope without engaging a codec's
38
- * request-id bookkeeping — used by the SSE transport to route a frame
38
+ * request-id bookkeeping; used by the SSE transport to route a frame
39
39
  * (park a POST for its response / answer with a bare 202) before it is
40
40
  * handed to birpc proper.
41
41
  *
@@ -1,6 +1,9 @@
1
- import { n as validateDefinitions, r as getRpcHandler, s as hash } from "./validation-YrZH6atx.mjs";
2
- import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
3
- import { DEVFRAME_RPC_DUMP_DIRNAME } from "./constants.mjs";
1
+ import { n as validateDefinitions, r as getRpcHandler, s as hash } from "./validation-CfV1s0SF.mjs";
2
+ import { n as strictJsonStringify, r as diagnostics } from "./serialization-DFg5sUt6.mjs";
3
+ import { DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME } from "./constants.mjs";
4
+ import { n as structuredCloneStringify } from "./structured-clone-CbAV5rFI.mjs";
5
+ import fs from "node:fs/promises";
6
+ import { dirname, resolve } from "pathe";
4
7
  //#region ../../node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
5
8
  var Node = class {
6
9
  value;
@@ -156,9 +159,9 @@ function serializeWithSeen(error, seen) {
156
159
  };
157
160
  const cause = error.cause;
158
161
  if (cause !== void 0) out.cause = cause instanceof Error ? serializeWithSeen(cause, seen) : cause;
159
- for (const key of Object.keys(error)) {
162
+ for (const [key, value] of Object.entries(error)) {
160
163
  if (key === "name" || key === "message" || key === "cause") continue;
161
- out[key] = error[key];
164
+ out[key] = value;
162
165
  }
163
166
  return out;
164
167
  }
@@ -168,13 +171,11 @@ function serializeWithSeen(error, seen) {
168
171
  * the original `name`, and re-attaches any custom own properties.
169
172
  */
170
173
  function reviveDumpError(stored) {
171
- const cause = stored.cause instanceof Error ? stored.cause : isPlainErrorShape(stored.cause) ? reviveDumpError(stored.cause) : stored.cause;
172
- const error = cause !== void 0 ? new Error(stored.message, { cause }) : new Error(stored.message);
173
- error.name = stored.name;
174
- for (const key of Object.keys(stored)) {
175
- if (key === "name" || key === "message" || key === "cause") continue;
176
- error[key] = stored[key];
177
- }
174
+ const { name, message, cause: rawCause, ...extras } = stored;
175
+ const cause = rawCause instanceof Error ? rawCause : isPlainErrorShape(rawCause) ? reviveDumpError(rawCause) : rawCause;
176
+ const error = cause !== void 0 ? new Error(message, { cause }) : new Error(message);
177
+ error.name = name;
178
+ Object.assign(error, extras);
178
179
  return error;
179
180
  }
180
181
  function isPlainErrorShape(value) {
@@ -191,15 +192,77 @@ function getDumpFallbackKey(functionName) {
191
192
  async function resolveGetter(valueOrGetter) {
192
193
  return typeof valueOrGetter === "function" ? await valueOrGetter() : valueOrGetter;
193
194
  }
195
+ /** Run tasks sequentially (`concurrency <= 1`) or through a `p-limit` pool. */
196
+ async function runDumpTasks(tasks, concurrency) {
197
+ if (concurrency <= 1) {
198
+ const out = [];
199
+ for (const task of tasks) out.push(await task());
200
+ return out;
201
+ }
202
+ const limit = pLimit(concurrency);
203
+ return Promise.all(tasks.map((task) => limit(task)));
204
+ }
194
205
  /**
195
- * Collects pre-computed dumps by executing functions with their defined input combinations.
196
- * Static functions without dump config automatically get `{ inputs: [[]] }`.
197
- *
198
- * @example
199
- * ```ts
200
- * const store = await dumpFunctions([greet], context, { concurrency: 10 })
201
- * ```
206
+ * Resolve one definition's handler + dump config and register it in the store.
207
+ * Returns `undefined` for definitions that carry no dump.
208
+ */
209
+ async function resolveDumpTask(definition, context, store) {
210
+ if (definition.type === "event" || definition.type === "action") return void 0;
211
+ const setupResult = definition.setup ? await Promise.resolve(definition.setup(context)) : {};
212
+ const handler = setupResult.handler || definition.handler;
213
+ if (!handler) throw diagnostics.DF0024({ name: definition.name });
214
+ let dump = setupResult.dump ?? definition.dump;
215
+ if (!dump && definition.type === "static") dump = { inputs: [[]] };
216
+ if (!dump && definition.snapshot) dump = async (_ctx, h) => {
217
+ const output = await Promise.resolve(h());
218
+ return {
219
+ records: [{
220
+ inputs: [],
221
+ output
222
+ }],
223
+ fallback: output
224
+ };
225
+ };
226
+ if (!dump) return void 0;
227
+ if (typeof dump === "function") dump = await Promise.resolve(dump(context, handler));
228
+ store.definitions[definition.name] = {
229
+ name: definition.name,
230
+ type: definition.type
231
+ };
232
+ return {
233
+ handler,
234
+ dump,
235
+ definition
236
+ };
237
+ }
238
+ /**
239
+ * Write a resolved dump's pre-defined records and fallback into the store, and
240
+ * return the per-input execution tasks that fill in the rest.
202
241
  */
242
+ function collectDumpTasks(resolution, store) {
243
+ const { definition, handler, dump } = resolution;
244
+ const { inputs, records, fallback } = dump;
245
+ if (records) for (const record of records) store.records[getDumpRecordKey(definition.name, record.inputs)] = record;
246
+ if ("fallback" in dump) store.records[getDumpFallbackKey(definition.name)] = {
247
+ inputs: [],
248
+ output: fallback
249
+ };
250
+ if (!inputs) return [];
251
+ return inputs.map((input) => async () => {
252
+ const recordKey = getDumpRecordKey(definition.name, input);
253
+ try {
254
+ store.records[recordKey] = {
255
+ inputs: input,
256
+ output: await Promise.resolve(handler(...input))
257
+ };
258
+ } catch (error) {
259
+ store.records[recordKey] = {
260
+ inputs: input,
261
+ error: serializeDumpError(error)
262
+ };
263
+ }
264
+ });
265
+ }
203
266
  async function dumpFunctions(definitions, context, options = {}) {
204
267
  validateDefinitions(definitions);
205
268
  const concurrency = options.concurrency === true ? 5 : options.concurrency === false || options.concurrency == null ? 1 : options.concurrency;
@@ -207,79 +270,7 @@ async function dumpFunctions(definitions, context, options = {}) {
207
270
  definitions: {},
208
271
  records: {}
209
272
  };
210
- const tasksResolutions = definitions.map((definition) => async () => {
211
- if (definition.type === "event" || definition.type === "action") return;
212
- const setupResult = definition.setup ? await Promise.resolve(definition.setup(context)) : {};
213
- const handler = setupResult.handler || definition.handler;
214
- if (!handler) throw diagnostics.DF0024({ name: definition.name });
215
- let dump = setupResult.dump ?? definition.dump;
216
- if (!dump && definition.type === "static") dump = { inputs: [[]] };
217
- if (!dump && definition.snapshot) dump = async (_ctx, h) => {
218
- const output = await Promise.resolve(h(...[]));
219
- return {
220
- records: [{
221
- inputs: [],
222
- output
223
- }],
224
- fallback: output
225
- };
226
- };
227
- if (!dump) return;
228
- if (typeof dump === "function") dump = await Promise.resolve(dump(context, handler));
229
- store.definitions[definition.name] = {
230
- name: definition.name,
231
- type: definition.type
232
- };
233
- return {
234
- handler,
235
- dump,
236
- definition
237
- };
238
- });
239
- let functionsToDump = [];
240
- if (concurrency <= 1) for (const task of tasksResolutions) {
241
- const resolution = await task();
242
- if (resolution) functionsToDump.push(resolution);
243
- }
244
- else {
245
- const limit = pLimit(concurrency);
246
- functionsToDump = (await Promise.all(tasksResolutions.map((task) => limit(task)))).filter((x) => !!x);
247
- }
248
- const dumpTasks = [];
249
- for (const { definition, handler, dump } of functionsToDump) {
250
- const { inputs, records, fallback } = dump;
251
- if (records) for (const record of records) {
252
- const recordKey = getDumpRecordKey(definition.name, record.inputs);
253
- store.records[recordKey] = record;
254
- }
255
- if ("fallback" in dump) {
256
- const fallbackKey = getDumpFallbackKey(definition.name);
257
- store.records[fallbackKey] = {
258
- inputs: [],
259
- output: fallback
260
- };
261
- }
262
- if (inputs) for (const input of inputs) dumpTasks.push(async () => {
263
- const recordKey = getDumpRecordKey(definition.name, input);
264
- try {
265
- const output = await Promise.resolve(handler(...input));
266
- store.records[recordKey] = {
267
- inputs: input,
268
- output
269
- };
270
- } catch (error) {
271
- store.records[recordKey] = {
272
- inputs: input,
273
- error: serializeDumpError(error)
274
- };
275
- }
276
- });
277
- }
278
- if (concurrency <= 1) for (const task of dumpTasks) await task();
279
- else {
280
- const limit = pLimit(concurrency);
281
- await Promise.all(dumpTasks.map((task) => limit(task)));
282
- }
273
+ await runDumpTasks((await runDumpTasks(definitions.map((definition) => () => resolveDumpTask(definition, context, store)), concurrency)).filter((x) => !!x).flatMap((resolution) => collectDumpTasks(resolution, store)), concurrency);
283
274
  return store;
284
275
  }
285
276
  /**
@@ -359,6 +350,54 @@ function makeQueryFallbackPath(name) {
359
350
  async function resolveRecord(record) {
360
351
  return typeof record === "function" ? await record() : record;
361
352
  }
353
+ async function collectStaticDefinition(definition, context, serialization, files) {
354
+ const handler = await getRpcHandler(definition, context);
355
+ const path = makeStaticPath(definition.name);
356
+ files[path] = {
357
+ serialization,
358
+ fnName: definition.name,
359
+ data: { output: await Promise.resolve(handler()) }
360
+ };
361
+ return {
362
+ type: "static",
363
+ path,
364
+ serialization
365
+ };
366
+ }
367
+ async function collectQueryDefinition(definition, context, serialization, files) {
368
+ const store = await dumpFunctions([definition], context);
369
+ if (!(definition.name in store.definitions)) return void 0;
370
+ const queryEntry = {
371
+ type: "query",
372
+ records: {},
373
+ serialization
374
+ };
375
+ const prefix = `${definition.name}---`;
376
+ for (const [recordKey, recordOrGetter] of Object.entries(store.records)) {
377
+ if (!recordKey.startsWith(prefix)) continue;
378
+ const key = recordKey.slice(prefix.length);
379
+ const record = await resolveRecord(recordOrGetter);
380
+ if (key === "fallback") {
381
+ const path = makeQueryFallbackPath(definition.name);
382
+ files[path] = {
383
+ serialization,
384
+ fnName: definition.name,
385
+ data: record
386
+ };
387
+ queryEntry.fallback = path;
388
+ } else {
389
+ const path = makeQueryRecordPath(definition.name, key);
390
+ files[path] = {
391
+ serialization,
392
+ fnName: definition.name,
393
+ data: record
394
+ };
395
+ queryEntry.records[key] = path;
396
+ }
397
+ }
398
+ if (!Object.keys(queryEntry.records).length && !queryEntry.fallback) return void 0;
399
+ return queryEntry;
400
+ }
362
401
  async function collectStaticRpcDump(definitions, context) {
363
402
  const manifest = {};
364
403
  const files = {};
@@ -366,53 +405,12 @@ async function collectStaticRpcDump(definitions, context) {
366
405
  const type = definition.type ?? "query";
367
406
  const serialization = definition.jsonSerializable === true ? "json" : "structured-clone";
368
407
  if (type === "static") {
369
- const handler = await getRpcHandler(definition, context);
370
- const path = makeStaticPath(definition.name);
371
- files[path] = {
372
- serialization,
373
- fnName: definition.name,
374
- data: { output: await Promise.resolve(handler()) }
375
- };
376
- manifest[definition.name] = {
377
- type: "static",
378
- path,
379
- serialization
380
- };
408
+ manifest[definition.name] = await collectStaticDefinition(definition, context, serialization, files);
381
409
  continue;
382
410
  }
383
411
  if (type !== "query") continue;
384
- const store = await dumpFunctions([definition], context);
385
- if (!(definition.name in store.definitions)) continue;
386
- const queryEntry = {
387
- type: "query",
388
- records: {},
389
- serialization
390
- };
391
- const prefix = `${definition.name}---`;
392
- for (const [recordKey, recordOrGetter] of Object.entries(store.records)) {
393
- if (!recordKey.startsWith(prefix)) continue;
394
- const key = recordKey.slice(prefix.length);
395
- const record = await resolveRecord(recordOrGetter);
396
- if (key === "fallback") {
397
- const path = makeQueryFallbackPath(definition.name);
398
- files[path] = {
399
- serialization,
400
- fnName: definition.name,
401
- data: record
402
- };
403
- queryEntry.fallback = path;
404
- } else {
405
- const path = makeQueryRecordPath(definition.name, key);
406
- files[path] = {
407
- serialization,
408
- fnName: definition.name,
409
- data: record
410
- };
411
- queryEntry.records[key] = path;
412
- }
413
- }
414
- if (!Object.keys(queryEntry.records).length && !queryEntry.fallback) continue;
415
- manifest[definition.name] = queryEntry;
412
+ const queryEntry = await collectQueryDefinition(definition, context, serialization, files);
413
+ if (queryEntry) manifest[definition.name] = queryEntry;
416
414
  }
417
415
  return {
418
416
  manifest,
@@ -420,4 +418,22 @@ async function collectStaticRpcDump(definitions, context) {
420
418
  };
421
419
  }
422
420
  //#endregion
423
- export { reviveDumpError as a, getDefinitionsWithDumps as i, createClientFromDump as n, serializeDumpError as o, dumpFunctions as r, collectStaticRpcDump as t };
421
+ //#region src/rpc/dump/write.ts
422
+ /**
423
+ * Write a collected static RPC dump to disk: every sharded record file plus
424
+ * the `__rpc-dump/index.json` manifest, under `outDir`. Shared by the build
425
+ * adapter (`createBuild`) and `@devframes/hub`'s `buildHub`.
426
+ */
427
+ async function writeStaticRpcDump(dump, outDir, options = {}) {
428
+ const indent = options.pretty ? 2 : void 0;
429
+ await fs.mkdir(resolve(outDir, DEVFRAME_RPC_DUMP_DIRNAME), { recursive: true });
430
+ for (const [filepath, file] of Object.entries(dump.files)) {
431
+ const fullpath = resolve(outDir, filepath);
432
+ await fs.mkdir(dirname(fullpath), { recursive: true });
433
+ const text = file.serialization === "structured-clone" ? structuredCloneStringify(file.data) : strictJsonStringify(file.data, file.fnName);
434
+ await fs.writeFile(fullpath, file.serialization === "json" && indent != null ? JSON.stringify(JSON.parse(text), null, indent) : text, "utf-8");
435
+ }
436
+ await fs.writeFile(resolve(outDir, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME), JSON.stringify(dump.manifest, null, 2), "utf-8");
437
+ }
438
+ //#endregion
439
+ export { getDefinitionsWithDumps as a, dumpFunctions as i, collectStaticRpcDump as n, reviveDumpError as o, createClientFromDump as r, serializeDumpError as s, writeStaticRpcDump as t };
@@ -1,4 +1,4 @@
1
- import { v as RpcFunctionDefinitionAny } from "./types-CMdPYhOx.mjs";
1
+ import { v as RpcFunctionDefinitionAny } from "./types-Dq4i156u.mjs";
2
2
  import { ChannelOptions } from "birpc";
3
3
  //#region src/rpc/transports/ws-client.d.ts
4
4
  interface WsRpcChannelOptions {
@@ -10,7 +10,7 @@ interface WsRpcChannelOptions {
10
10
  /**
11
11
  * RPC function definitions (or just the `jsonSerializable` flag per
12
12
  * method) used to dispatch the per-call wire serializer. Pass an
13
- * empty / partial map on clients that don't have the full registry —
13
+ * empty / partial map on clients that don't have the full registry;
14
14
  * encoding falls back to structured-clone (the safer superset) and
15
15
  * decoding still routes correctly via the wire prefix.
16
16
  */
@@ -20,7 +20,7 @@ interface WsRpcChannelOptions {
20
20
  * Build a birpc `ChannelOptions` object backed by a browser `WebSocket`.
21
21
  * Pass the result straight to `createRpcClient`'s `channel` option.
22
22
  *
23
- * Also returns `close()`, closing the underlying socket — mirroring the server transport's
23
+ * Also returns `close()`, closing the underlying socket, mirroring the server transport's
24
24
  * existing `WsRpcTransport.close()`. `birpc`'s own `ChannelOptions` has no teardown of its own.
25
25
  */
26
26
  declare function createWsRpcChannel(options: WsRpcChannelOptions): ChannelOptions & {
@@ -1,4 +1,4 @@
1
- import { v as RpcFunctionDefinitionAny } from "./types-CMdPYhOx.mjs";
1
+ import { v as RpcFunctionDefinitionAny } from "./types-Dq4i156u.mjs";
2
2
  import { BirpcGroup, ChannelOptions } from "birpc";
3
3
  import { Hooks, Peer } from "crossws";
4
4
  import { NodeAdapter } from "crossws/adapters/node";
@@ -16,8 +16,8 @@ import { Duplex } from "node:stream";
16
16
  */
17
17
  type DevframeRpcTransportKind = 'websocket' | 'sse';
18
18
  /**
19
- * Structural view of the connect-time HTTP request behind an RPC connection
20
- * — the WS upgrade request, or the request opening an SSE stream. Shaped to
19
+ * Structural view of the connect-time HTTP request behind an RPC connection:
20
+ * the WS upgrade request, or the request opening an SSE stream. Shaped to
21
21
  * match both the web `Request` a crossws peer exposes and a plain
22
22
  * `node:http` request wrapper, so auth hooks can read the bearer-token
23
23
  * query param and the `Origin` header without caring which transport (or
@@ -38,7 +38,7 @@ interface DevframeRpcConnectionRequest {
38
38
  * (`onPeerConnect` / `onPeerDisconnect`, {@link DevframeAuthHandler.onConnect}).
39
39
  */
40
40
  interface DevframeRpcConnection {
41
- /** Session id — the same value as the session meta's `id`. */
41
+ /** Session id, the same value as the session meta's `id`. */
42
42
  id: number;
43
43
  /** The transport carrying this connection. */
44
44
  transport: DevframeRpcTransportKind;
@@ -49,7 +49,7 @@ interface DevframeRpcConnection {
49
49
  /** Terminate the connection from the server side. */
50
50
  close?: (code?: number, reason?: string) => void;
51
51
  /**
52
- * The crossws peer backing a `websocket` connection — the WS-specific
52
+ * The crossws peer backing a `websocket` connection: the WS-specific
53
53
  * escape hatch (pub/sub, raw socket access). Absent on other transports.
54
54
  */
55
55
  peer?: Peer;
@@ -81,7 +81,7 @@ interface WsRpcTransportOptions {
81
81
  * Attach to an existing HTTP(S) server, sharing its port. Combine with
82
82
  * `path` to bind the WS endpoint to a single route so it coexists with
83
83
  * other upgrade handlers on the same server (e.g. a Vite dev server's HMR
84
- * socket). The shared server's lifecycle is owned by the caller — closing
84
+ * socket). The shared server's lifecycle is owned by the caller; closing
85
85
  * this transport detaches the upgrade listener without closing the server.
86
86
  */
87
87
  server?: Server | Server$1;
@@ -130,7 +130,7 @@ interface WsRpcTransportOptions {
130
130
  * dispatch between strict-JSON and structured-clone encoding based
131
131
  * on each function's `jsonSerializable` flag.
132
132
  *
133
- * When omitted, all messages fall back to structured-clone — safe but
133
+ * When omitted, all messages fall back to structured-clone: safe but
134
134
  * loses dev-time validation for `jsonSerializable: true` declarations.
135
135
  */
136
136
  definitions?: ReadonlyMap<string, Pick<RpcFunctionDefinitionAny, 'jsonSerializable'>>;
@@ -164,7 +164,7 @@ interface WsOriginRegistry {
164
164
  declare function createWsOriginRegistry(options?: CreateWsOriginRegistryOptions): WsOriginRegistry;
165
165
  interface WsRpcTransport {
166
166
  /**
167
- * The crossws node adapter driving the socket — exposes the connected
167
+ * The crossws node adapter driving the socket; exposes the connected
168
168
  * `peers` and pub/sub. See https://crossws.h3.dev.
169
169
  */
170
170
  ws: NodeAdapter;
@@ -175,15 +175,15 @@ interface WsRpcTransport {
175
175
  /**
176
176
  * Complete a `node:http` `upgrade` event on this transport, applying the
177
177
  * same `path` filter and origin gate the transport's own listener uses.
178
- * Wire it into a host server directly — `server.on('upgrade',
179
- * transport.handleUpgrade)` — or call it from an existing listener.
178
+ * Wire it into a host server directly (`server.on('upgrade',
179
+ * transport.handleUpgrade)`) or call it from an existing listener.
180
180
  */
181
181
  handleUpgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => void;
182
182
  /**
183
183
  * Route a server's `upgrade` events to this transport, returning a detach
184
184
  * function. Use it to bind an `unbound` transport once the host server
185
185
  * exists; {@link WsRpcTransport.close} detaches every server attached this
186
- * way (without closing them — the caller owns their lifecycle).
186
+ * way (without closing them, since the caller owns their lifecycle).
187
187
  */
188
188
  attach: (server: Server | Server$1) => () => void;
189
189
  /** Remove the upgrade listener from a shared `server` (a no-op otherwise). */
@@ -195,36 +195,12 @@ interface WsRpcTransport {
195
195
  */
196
196
  close: () => Promise<void>;
197
197
  }
198
- /**
199
- * Whether `hostname` names a loopback host: `localhost` (or any `*.localhost`
200
- * subdomain), the IPv6 loopback `::1`, or an IPv4 literal inside the
201
- * `127.0.0.0/8` loopback block.
202
- *
203
- * The IPv4 case is matched **structurally** — the whole hostname must be a
204
- * canonical dotted-decimal IPv4 literal whose first octet is `127`. A bare
205
- * `startsWith('127.')` prefix check would also accept an attacker-controlled
206
- * DNS name that merely *begins* with `127.` (`127.attacker.example`,
207
- * `127.0.0.1.attacker.example`), letting a cross-origin browser page defeat
208
- * the loopback origin gate that guards the RPC/MCP surface (a DNS-rebinding /
209
- * cross-site WebSocket-hijacking bypass). Requiring a real IPv4 literal keeps
210
- * genuine loopback addresses (`127.0.0.1`, `127.5.5.5`) allowed while rejecting
211
- * those DNS names.
212
- */
213
- declare function isLoopbackHostname(hostname: string): boolean;
214
- /**
215
- * Default origin policy for a localhost dev tool: allow requests with no
216
- * `Origin` header (native, non-browser clients), allow any loopback origin
217
- * (so cross-port localhost dev setups keep working), and allow explicitly
218
- * configured origins. Everything else — a real remote page in the dev's
219
- * browser — is rejected.
220
- */
221
- declare function isAllowedOrigin(origin: string | undefined, allowedOrigins: readonly string[]): boolean;
222
198
  /**
223
199
  * The per-peer lifecycle hooks driving a devframe RPC WebSocket, shaped for
224
200
  * any [crossws](https://crossws.h3.dev) adapter. {@link attachWsRpcTransport}
225
201
  * feeds them to the Node adapter; runtime-specific attachments (e.g. Bun's
226
202
  * fetch-upgrade adapter) reuse the same hooks so every transport speaks the
227
- * identical wire protocol — one birpc channel per peer, per-method
203
+ * identical wire protocol: one birpc channel per peer, per-method
228
204
  * `jsonSerializable` dispatch between strict JSON and structured-clone.
229
205
  */
230
206
  declare function createWsRpcPeerHooks<ClientFunctions extends object, ServerFunctions extends object>(rpcGroup: BirpcGroup<ClientFunctions, ServerFunctions, false>, options?: Pick<WsRpcTransportOptions, 'onConnected' | 'onDisconnected' | 'definitions' | 'serialize' | 'deserialize'>): Partial<Hooks>;
@@ -240,4 +216,4 @@ declare function createWsRpcPeerHooks<ClientFunctions extends object, ServerFunc
240
216
  */
241
217
  declare function attachWsRpcTransport<ClientFunctions extends object, ServerFunctions extends object>(rpcGroup: BirpcGroup<ClientFunctions, ServerFunctions, false>, options?: WsRpcTransportOptions): WsRpcTransport;
242
218
  //#endregion
243
- export { attachWsRpcTransport as a, isAllowedOrigin as c, DevframeRpcConnection as d, DevframeRpcConnectionRequest as f, WsRpcTransportOptions as i, isLoopbackHostname as l, WsOriginRegistry as n, createWsOriginRegistry as o, DevframeRpcTransportKind as p, WsRpcTransport as r, createWsRpcPeerHooks as s, CreateWsOriginRegistryOptions as t, DevframeNodeRpcSessionMeta as u };
219
+ export { attachWsRpcTransport as a, DevframeNodeRpcSessionMeta as c, DevframeRpcTransportKind as d, WsRpcTransportOptions as i, DevframeRpcConnection as l, WsOriginRegistry as n, createWsOriginRegistry as o, WsRpcTransport as r, createWsRpcPeerHooks as s, CreateWsOriginRegistryOptions as t, DevframeRpcConnectionRequest as u };