devframe 0.9.1 → 0.9.3
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.
- package/dist/{_shared-DqbKCAew.d.mts → _shared-BPovvJW8.d.mts} +1 -1
- package/dist/adapters/build.d.mts +11 -2
- package/dist/adapters/build.mjs +46 -9
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +1 -1
- package/dist/adapters/dev.d.mts +4 -4
- package/dist/adapters/dev.mjs +1 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/embedded.mjs +2 -1
- package/dist/adapters/initiate.d.mts +3 -3
- package/dist/adapters/initiate.mjs +1 -1
- package/dist/adapters/mcp.d.mts +1 -1
- package/dist/adapters/mcp.mjs +1 -1
- package/dist/cli/main.mjs +2 -2
- package/dist/client/index.d.mts +5 -5
- package/dist/client/index.mjs +2 -2
- package/dist/{context-CXhjygJU.mjs → context-BUJGvlje.mjs} +1 -1
- package/dist/{context-dqyXS6o3.d.mts → context-CO7eZHJy.d.mts} +1 -1
- package/dist/{context-Bdkakk8S.mjs → context-CQW0lSw8.mjs} +40 -20
- package/dist/{devframe-BlLEZR-x.d.mts → context-_i51nYOs.d.mts} +1033 -970
- package/dist/{dev-C8VHtuPP.mjs → dev-lpIvwagf.mjs} +10 -9
- package/dist/{diagnostics-Di8ytitn.mjs → diagnostics-BXWW3VK-.mjs} +3 -3
- package/dist/hash-DdPnc4k3.mjs +360 -0
- package/dist/{host-agent-BDdcOXhG.mjs → host-agent-B84av916.mjs} +1 -1
- package/dist/{http-BbHCU5Zs.mjs → http-BKiqHcXX.mjs} +6 -5
- package/dist/{index-BKFT9-jA.d.mts → index-CEvd-YXD.d.mts} +2 -2
- package/dist/{index-tdrk1bzV.d.mts → index-DOg4S5wk.d.mts} +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/{instance-registry-CHunhMi5.mjs → instance-registry-BjlMQbFB.mjs} +1 -1
- package/dist/{instance-shell-DbDoozNM.mjs → instance-shell-B-uevnK-.mjs} +4 -4
- package/dist/{instance-shell-0iw-cQ5V.d.mts → instance-shell-BHvBIGP4.d.mts} +3 -3
- package/dist/internal/index.d.mts +8 -8
- package/dist/internal/index.mjs +5 -5
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +1 -1
- package/dist/node/index.d.mts +12 -3
- package/dist/node/index.mjs +2 -2
- package/dist/recipes/common-rpc-functions.d.mts +13 -3
- package/dist/recipes/common-rpc-functions.mjs +10 -0
- package/dist/recipes/interactive-auth.d.mts +2 -2
- package/dist/recipes/interactive-auth.mjs +1 -1
- package/dist/{remote-assets-DRfY2RjX.mjs → remote-assets-DezQmPQU.mjs} +13 -3
- package/dist/rpc/dump.d.mts +1 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +2 -2
- package/dist/rpc/index.mjs +1 -1
- package/dist/rpc/transports/sse-client.d.mts +1 -1
- package/dist/rpc/transports/sse-server.d.mts +2 -2
- package/dist/rpc/transports/ws-bun.d.mts +2 -2
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-deno.d.mts +26 -0
- package/dist/rpc/transports/ws-deno.mjs +30 -0
- package/dist/rpc/transports/ws-server.d.mts +1 -1
- package/dist/{rpc-core-CR8o34O9.d.mts → rpc-core-CDZoGYiD.d.mts} +3 -3
- package/dist/{rpc-core-Dru9uoM0.mjs → rpc-core-DlHd276T.mjs} +3 -10
- package/dist/{shared-state-fRRbRUtD.mjs → shared-state-DoXutg_U.mjs} +1 -1
- package/dist/{sse-client-CFBOIaUi.d.mts → sse-client-CdGD_B8_.d.mts} +1 -1
- package/dist/{static-BPZ1_OPA.mjs → static-DV_qUSRS.mjs} +1 -1
- package/dist/{storage-D4a99Ung.mjs → storage-BXNVhKyR.mjs} +1 -1
- package/dist/types/index.d.mts +5 -5
- package/dist/{types-Djq7CBXh.d.mts → types-BBLeGfpt.d.mts} +1 -1
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/hash.mjs +1 -1
- package/dist/utils/remote-assets.d.mts +7 -1
- package/dist/utils/remote-assets.mjs +1 -1
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/validation-YrZH6atx.mjs +311 -0
- package/dist/{ws-client-DhjNyQo3.d.mts → ws-client-Bo2t6hES.d.mts} +1 -1
- package/dist/{ws-server-D1d3QM9f.d.mts → ws-server-DLtEoLdR.d.mts} +1 -1
- package/package.json +4 -3
- package/skills/devframe/SKILL.md +1 -1
- package/dist/hash-KtDZYXDN.mjs +0 -266
- package/dist/validation-CpXFB6Dz.mjs +0 -266
|
@@ -1,50 +1,11 @@
|
|
|
1
|
-
import { d as RpcFunctionsCollectorBase } from "./index-
|
|
2
|
-
import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-
|
|
3
|
-
import { d as DevframeRpcConnection, u as DevframeNodeRpcSessionMeta } from "./ws-server-
|
|
4
|
-
import { m as defineDiagnostics } from "./nostics-SPGDgHEP.mjs";
|
|
1
|
+
import { d as RpcFunctionsCollectorBase } from "./index-DOg4S5wk.mjs";
|
|
2
|
+
import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-BBLeGfpt.mjs";
|
|
3
|
+
import { d as DevframeRpcConnection, u as DevframeNodeRpcSessionMeta } from "./ws-server-DLtEoLdR.mjs";
|
|
5
4
|
import { a as RemoteAssetsStore, o as StaticAssetsSource } from "./remote-assets-Bg4gCUZ_.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
|
+
import { m as defineDiagnostics } from "./nostics-SPGDgHEP.mjs";
|
|
8
6
|
import { BirpcReturn } from "birpc";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* Schema map for typed CLI flags. Keys are flag names in camelCase —
|
|
12
|
-
* this matches CAC's parsed-flag output ( `--no-open` → `noOpen` ). Each
|
|
13
|
-
* value is any [Standard Schema](https://standardschema.dev/) validator
|
|
14
|
-
* (valibot, zod, arktype, devframe's built-in `s`, …) used to both (a)
|
|
15
|
-
* derive the CAC option type when the flag is registered and (b) validate
|
|
16
|
-
* the parsed value before it's forwarded to `setup(ctx, { flags })`.
|
|
17
|
-
*/
|
|
18
|
-
type CliFlagsSchema = Record<string, StandardSchemaV1>;
|
|
19
|
-
/**
|
|
20
|
-
* Identity helper that preserves the literal schema-map type — use this
|
|
21
|
-
* so `InferCliFlags<typeof myFlags>` resolves to the right object shape.
|
|
22
|
-
*
|
|
23
|
-
* ```ts
|
|
24
|
-
* const appFlags = defineCliFlags({
|
|
25
|
-
* depth: v.pipe(v.number(), v.integer()),
|
|
26
|
-
* config: v.optional(v.string()),
|
|
27
|
-
* })
|
|
28
|
-
*
|
|
29
|
-
* defineDevframe({
|
|
30
|
-
* cli: { flags: appFlags },
|
|
31
|
-
* setup(ctx, info) {
|
|
32
|
-
* const flags = info.flags as InferCliFlags<typeof appFlags>
|
|
33
|
-
* flags.depth // number
|
|
34
|
-
* flags.config // string | undefined
|
|
35
|
-
* },
|
|
36
|
-
* })
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
declare function defineCliFlags<T extends CliFlagsSchema>(flags: T): T;
|
|
40
|
-
/** Extract the parsed-output type from a {@link CliFlagsSchema}. */
|
|
41
|
-
type InferCliFlags<T extends CliFlagsSchema> = { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]>; };
|
|
42
|
-
/** Validate the raw cac-parsed bag against a {@link CliFlagsSchema}. */
|
|
43
|
-
declare function parseCliFlags(schema: CliFlagsSchema, raw: Record<string, unknown>): {
|
|
44
|
-
flags: Record<string, unknown>;
|
|
45
|
-
issues?: string[];
|
|
46
|
-
};
|
|
47
|
-
//#endregion
|
|
7
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
8
|
+
import { CAC } from "cac";
|
|
48
9
|
//#region src/types/events.d.ts
|
|
49
10
|
interface EventsMap {
|
|
50
11
|
[event: string]: any;
|
|
@@ -313,235 +274,144 @@ interface DevframeAgentHost {
|
|
|
313
274
|
getResource: (id: string) => AgentResource | undefined;
|
|
314
275
|
}
|
|
315
276
|
//#endregion
|
|
316
|
-
//#region src/
|
|
317
|
-
type ImmutablePrimitive = undefined | null | boolean | string | number | Function;
|
|
318
|
-
type Immutable<T> = T extends ImmutablePrimitive ? T : T extends Array<infer U> ? ImmutableArray<U> : T extends Map<infer K, infer V> ? ImmutableMap<K, V> : T extends Set<infer M> ? ImmutableSet<M> : ImmutableObject<T>;
|
|
319
|
-
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
|
320
|
-
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
|
|
321
|
-
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
|
|
322
|
-
type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]>; };
|
|
277
|
+
//#region src/adapters/flags.d.ts
|
|
323
278
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
279
|
+
* Schema map for typed CLI flags. Keys are flag names in camelCase —
|
|
280
|
+
* this matches CAC's parsed-flag output ( `--no-open` → `noOpen` ). Each
|
|
281
|
+
* value is any [Standard Schema](https://standardschema.dev/) validator
|
|
282
|
+
* (valibot, zod, arktype, devframe's built-in `s`, …) used to both (a)
|
|
283
|
+
* derive the CAC option type when the flag is registered and (b) validate
|
|
284
|
+
* the parsed value before it's forwarded to `setup(ctx, { flags })`.
|
|
326
285
|
*/
|
|
327
|
-
|
|
328
|
-
op: 'add' | 'remove' | 'replace';
|
|
329
|
-
path: readonly (string | number)[];
|
|
330
|
-
value?: unknown;
|
|
331
|
-
}
|
|
286
|
+
type CliFlagsSchema = Record<string, StandardSchemaV1>;
|
|
332
287
|
/**
|
|
333
|
-
*
|
|
288
|
+
* Identity helper that preserves the literal schema-map type — use this
|
|
289
|
+
* so `InferCliFlags<typeof myFlags>` resolves to the right object shape.
|
|
290
|
+
*
|
|
291
|
+
* ```ts
|
|
292
|
+
* const appFlags = defineCliFlags({
|
|
293
|
+
* depth: v.pipe(v.number(), v.integer()),
|
|
294
|
+
* config: v.optional(v.string()),
|
|
295
|
+
* })
|
|
296
|
+
*
|
|
297
|
+
* defineDevframe({
|
|
298
|
+
* cli: { flags: appFlags },
|
|
299
|
+
* setup(ctx, info) {
|
|
300
|
+
* const flags = info.flags as InferCliFlags<typeof appFlags>
|
|
301
|
+
* flags.depth // number
|
|
302
|
+
* flags.config // string | undefined
|
|
303
|
+
* },
|
|
304
|
+
* })
|
|
305
|
+
* ```
|
|
334
306
|
*/
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
307
|
+
declare function defineCliFlags<T extends CliFlagsSchema>(flags: T): T;
|
|
308
|
+
/** Extract the parsed-output type from a {@link CliFlagsSchema}. */
|
|
309
|
+
type InferCliFlags<T extends CliFlagsSchema> = { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]>; };
|
|
310
|
+
/** Validate the raw cac-parsed bag against a {@link CliFlagsSchema}. */
|
|
311
|
+
declare function parseCliFlags(schema: CliFlagsSchema, raw: Record<string, unknown>): {
|
|
312
|
+
flags: Record<string, unknown>;
|
|
313
|
+
issues?: string[];
|
|
314
|
+
};
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/node/auth/handler.d.ts
|
|
317
|
+
/**
|
|
318
|
+
* A ready-made pre-auth RPC handler, as produced by
|
|
319
|
+
* `devframe/recipes/interactive-auth`'s `createInteractiveAuth`. Bundles
|
|
320
|
+
* everything a host adapter needs to wire an authenticated server:
|
|
321
|
+
* the handshake RPC functions, the resolver gate, the connect-time trust
|
|
322
|
+
* hook, and the startup banner.
|
|
323
|
+
*
|
|
324
|
+
* `initDevframe` / `initHub` accept one of these directly via their `auth` option —
|
|
325
|
+
* see {@link https://devfra.me | devframe}'s server docs — or a host can
|
|
326
|
+
* wire the four pieces itself against a lower-level transport.
|
|
327
|
+
*/
|
|
328
|
+
interface DevframeAuthHandler {
|
|
344
329
|
/**
|
|
345
|
-
*
|
|
330
|
+
* `anonymous:devframe:auth` + `anonymous:devframe:auth:exchange` (the
|
|
331
|
+
* handshake) and `devframe:auth:revoke` (self-revoke) — register these on
|
|
332
|
+
* the RPC host (e.g. `rpcHost.register(fn)` for each).
|
|
346
333
|
*/
|
|
347
|
-
|
|
334
|
+
rpcFunctions: RpcFunctionDefinitionAny[];
|
|
348
335
|
/**
|
|
349
|
-
*
|
|
336
|
+
* Resolver gate: whether `methodName` is callable given `session`'s
|
|
337
|
+
* current trust state. Defaults to allowing any `anonymous:`-prefixed
|
|
338
|
+
* method (see `isAnonymousRpcMethod`) plus anything once the session is
|
|
339
|
+
* trusted.
|
|
350
340
|
*/
|
|
351
|
-
|
|
341
|
+
authorize: (methodName: string, session: DevframeNodeRpcSession) => boolean;
|
|
352
342
|
/**
|
|
353
|
-
*
|
|
343
|
+
* Connect-time trust: reads a bearer token off the connection's initial
|
|
344
|
+
* request (a static/pre-shared token from `clientAuthTokens`, or a token
|
|
345
|
+
* minted by the code exchange) and, when valid, marks the session
|
|
346
|
+
* trusted immediately — before the client's own handshake call.
|
|
354
347
|
*/
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
interface SharedStateEvents<T> {
|
|
358
|
-
updated: (fullState: T, patches: SharedStatePatch[] | undefined, syncId: string) => void;
|
|
359
|
-
}
|
|
360
|
-
interface SharedStateOptions<T> {
|
|
348
|
+
onConnect: (connection: DevframeRpcConnection, session: DevframeNodeRpcSession) => void;
|
|
361
349
|
/**
|
|
362
|
-
*
|
|
350
|
+
* Print the current one-time code and its magic-link URL. Devframe stays
|
|
351
|
+
* headless — call this yourself once the server is listening. Safe to
|
|
352
|
+
* call repeatedly; it only prints once per code.
|
|
363
353
|
*/
|
|
364
|
-
|
|
354
|
+
printBanner: () => void;
|
|
365
355
|
/**
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
356
|
+
* Rewrite a URL the CLI is about to open in the browser (`--open`) so the
|
|
357
|
+
* tab lands already authenticated — e.g. appending the current OTP as a
|
|
358
|
+
* query param. Called with the fully-resolved target URL; return it
|
|
359
|
+
* unchanged to opt out. Optional: a handler that doesn't need this (e.g.
|
|
360
|
+
* one gated by a pre-shared bearer token instead of an interactive code)
|
|
361
|
+
* can omit it and the URL opens as-is.
|
|
369
362
|
*/
|
|
370
|
-
|
|
363
|
+
buildOpenUrl?: (url: string) => string;
|
|
371
364
|
}
|
|
372
|
-
declare function createSharedState<T extends object>(options: SharedStateOptions<T>): SharedState<T>;
|
|
373
365
|
//#endregion
|
|
374
|
-
//#region src/
|
|
366
|
+
//#region src/types/diagnostics.d.ts
|
|
375
367
|
/**
|
|
376
|
-
*
|
|
377
|
-
*
|
|
368
|
+
* The shared diagnostics lookup exposed by the host. A `Proxy` that resolves
|
|
369
|
+
* any registered code name to its `nostics` handle (a callable that builds
|
|
370
|
+
* a diagnostic and routes it through registered reporters). Typed loosely
|
|
371
|
+
* because it spans heterogeneous definitions registered by different
|
|
372
|
+
* integrations.
|
|
378
373
|
*/
|
|
379
|
-
|
|
380
|
-
name: string;
|
|
381
|
-
message: string;
|
|
382
|
-
}
|
|
374
|
+
type DevframeDiagnosticsLogger = Record<string, any>;
|
|
383
375
|
/**
|
|
384
|
-
*
|
|
376
|
+
* Host for structured diagnostics — a thin layer over `nostics` that lets
|
|
377
|
+
* integrations register their own coded errors/warnings into a shared
|
|
378
|
+
* registry without taking a direct dependency on `nostics`.
|
|
385
379
|
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
380
|
+
* Typical usage from a plugin's `setup(ctx)`:
|
|
381
|
+
*
|
|
382
|
+
* ```ts
|
|
383
|
+
* const myDiagnostics = ctx.diagnostics.defineDiagnostics({
|
|
384
|
+
* docsBase: 'https://example.com/errors',
|
|
385
|
+
* codes: {
|
|
386
|
+
* MYP0001: { why: 'Something went wrong' },
|
|
387
|
+
* },
|
|
388
|
+
* })
|
|
389
|
+
* ctx.diagnostics.register(myDiagnostics)
|
|
390
|
+
*
|
|
391
|
+
* // Through the shared lookup (loose typing):
|
|
392
|
+
* throw ctx.diagnostics.logger.MYP0001()
|
|
393
|
+
*
|
|
394
|
+
* // Or directly on the typed handle returned from `defineDiagnostics`:
|
|
395
|
+
* throw myDiagnostics.MYP0001()
|
|
396
|
+
* ```
|
|
389
397
|
*/
|
|
390
|
-
interface
|
|
391
|
-
seq: number;
|
|
392
|
-
chunk: T;
|
|
393
|
-
}
|
|
394
|
-
interface StreamSinkEvents<T> {
|
|
395
|
-
/** Fired for each `write()`. The RPC layer subscribes and broadcasts. */
|
|
396
|
-
chunk: (seq: number, chunk: T) => void;
|
|
397
|
-
/** Terminal — fired exactly once per sink lifetime. */
|
|
398
|
-
end: (error?: StreamErrorPayload) => void;
|
|
399
|
-
}
|
|
400
|
-
interface CreateStreamSinkOptions {
|
|
401
|
-
id?: string;
|
|
398
|
+
interface DevframeDiagnosticsHost {
|
|
402
399
|
/**
|
|
403
|
-
*
|
|
404
|
-
* `
|
|
400
|
+
* Proxy-backed lookup of every registered diagnostic handle by code name.
|
|
401
|
+
* Resolves to a `nostics` `DiagnosticHandle` — a callable that builds a
|
|
402
|
+
* diagnostic and routes it through registered reporters; prefix with
|
|
403
|
+
* `throw` to raise. Loosely typed — for autocompletion, keep a reference
|
|
404
|
+
* to the typed result of `defineDiagnostics()` instead.
|
|
405
405
|
*/
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* Server-side producer handle. Two equivalent surfaces share one piece of
|
|
410
|
-
* state: the imperative `write/error/close` triple, and a `WritableStream<T>`
|
|
411
|
-
* for `pipeTo`-style consumption.
|
|
412
|
-
*/
|
|
413
|
-
interface StreamSink<T> {
|
|
414
|
-
/** Stable id used by clients to subscribe. */
|
|
415
|
-
readonly id: string;
|
|
406
|
+
readonly logger: DevframeDiagnosticsLogger;
|
|
416
407
|
/**
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
*
|
|
408
|
+
* Register additional diagnostic definitions with this host. After
|
|
409
|
+
* registration, codes from the new definition are reachable via
|
|
410
|
+
* `host.logger.CODE`. Plugins that want shared output formatting should
|
|
411
|
+
* build their diagnostics via `host.defineDiagnostics()` first — that
|
|
412
|
+
* factory pre-wires the host's ANSI console reporter.
|
|
420
413
|
*/
|
|
421
|
-
|
|
422
|
-
/** `true` after `close()` / `error()`. Further writes throw. */
|
|
423
|
-
readonly closed: boolean;
|
|
424
|
-
/** Last allocated sequence number. `0` until the first write. */
|
|
425
|
-
readonly lastSeq: number;
|
|
426
|
-
write: (chunk: T) => void;
|
|
427
|
-
error: (reason: unknown) => void;
|
|
428
|
-
close: () => void;
|
|
429
|
-
/** External-cancel path. Aborts the signal so handlers can short-circuit. */
|
|
430
|
-
abort: (reason?: unknown) => void;
|
|
431
|
-
/** `WritableStream<T>` adapter — same in-memory state as the imperative API. */
|
|
432
|
-
readonly writable: WritableStream<T>;
|
|
433
|
-
/**
|
|
434
|
-
* Internal — RPC layer subscribes to receive chunk/end notifications.
|
|
435
|
-
* Not part of the public contract; do not call directly.
|
|
436
|
-
*
|
|
437
|
-
* @internal
|
|
438
|
-
*/
|
|
439
|
-
readonly events: EventEmitter<StreamSinkEvents<T>>;
|
|
440
|
-
/**
|
|
441
|
-
* Internal — replay buffer. RPC layer reads on (re)subscribe to feed
|
|
442
|
-
* missed chunks before going live.
|
|
443
|
-
*
|
|
444
|
-
* @internal
|
|
445
|
-
*/
|
|
446
|
-
readonly buffer: ReadonlyArray<BufferedChunk<T>>;
|
|
447
|
-
}
|
|
448
|
-
interface CreateStreamReaderOptions {
|
|
449
|
-
id?: string;
|
|
450
|
-
/**
|
|
451
|
-
* Maximum number of buffered chunks held client-side while the consumer
|
|
452
|
-
* isn't draining. On overflow, the oldest chunk is dropped.
|
|
453
|
-
*/
|
|
454
|
-
highWaterMark?: number;
|
|
455
|
-
/**
|
|
456
|
-
* Called when the chunk queue overflows the high-water mark. The RPC
|
|
457
|
-
* layer wires this to a coded warning; the primitive itself is
|
|
458
|
-
* RPC-agnostic.
|
|
459
|
-
*/
|
|
460
|
-
onOverflow?: (dropped: number) => void;
|
|
461
|
-
/** Called when the consumer cancels — the RPC layer sends `:cancel` upstream. */
|
|
462
|
-
onCancel?: () => void;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Client-side consumer handle. Both an `AsyncIterable<T>` (for `for await`)
|
|
466
|
-
* and exposes `readable: ReadableStream<T>` (for `pipeTo`). Pick one — they
|
|
467
|
-
* share a single internal queue, so concurrent draining will race.
|
|
468
|
-
*/
|
|
469
|
-
interface StreamReader<T> extends AsyncIterable<T> {
|
|
470
|
-
readonly id: string;
|
|
471
|
-
readonly cancelled: boolean;
|
|
472
|
-
readonly done: boolean;
|
|
473
|
-
/** Highest `seq` observed. Used for replay on reconnect. */
|
|
474
|
-
readonly lastSeenSeq: number;
|
|
475
|
-
/** `ReadableStream<T>` adapter for `pipeTo`-style consumption. */
|
|
476
|
-
readonly readable: ReadableStream<T>;
|
|
477
|
-
cancel: () => void;
|
|
478
|
-
/** @internal */
|
|
479
|
-
_push: (seq: number, chunk: T) => void;
|
|
480
|
-
/** @internal */
|
|
481
|
-
_end: (error?: StreamErrorPayload) => void;
|
|
482
|
-
}
|
|
483
|
-
/**
|
|
484
|
-
* Build a server-side stream sink. RPC-agnostic — the RPC host wires
|
|
485
|
-
* `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
|
|
486
|
-
* for late or reconnecting subscribers.
|
|
487
|
-
*/
|
|
488
|
-
declare function createStreamSink<T>(options?: CreateStreamSinkOptions): StreamSink<T>;
|
|
489
|
-
/**
|
|
490
|
-
* Build a client-side stream reader. RPC-agnostic — the RPC host calls
|
|
491
|
-
* `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
|
|
492
|
-
* terminal frame. Consumers iterate with `for await` or pipe `readable`.
|
|
493
|
-
*/
|
|
494
|
-
declare function createStreamReader<T>(options?: CreateStreamReaderOptions): StreamReader<T>;
|
|
495
|
-
//#endregion
|
|
496
|
-
//#region src/types/diagnostics.d.ts
|
|
497
|
-
/**
|
|
498
|
-
* The shared diagnostics lookup exposed by the host. A `Proxy` that resolves
|
|
499
|
-
* any registered code name to its `nostics` handle (a callable that builds
|
|
500
|
-
* a diagnostic and routes it through registered reporters). Typed loosely
|
|
501
|
-
* because it spans heterogeneous definitions registered by different
|
|
502
|
-
* integrations.
|
|
503
|
-
*/
|
|
504
|
-
type DevframeDiagnosticsLogger = Record<string, any>;
|
|
505
|
-
/**
|
|
506
|
-
* Host for structured diagnostics — a thin layer over `nostics` that lets
|
|
507
|
-
* integrations register their own coded errors/warnings into a shared
|
|
508
|
-
* registry without taking a direct dependency on `nostics`.
|
|
509
|
-
*
|
|
510
|
-
* Typical usage from a plugin's `setup(ctx)`:
|
|
511
|
-
*
|
|
512
|
-
* ```ts
|
|
513
|
-
* const myDiagnostics = ctx.diagnostics.defineDiagnostics({
|
|
514
|
-
* docsBase: 'https://example.com/errors',
|
|
515
|
-
* codes: {
|
|
516
|
-
* MYP0001: { why: 'Something went wrong' },
|
|
517
|
-
* },
|
|
518
|
-
* })
|
|
519
|
-
* ctx.diagnostics.register(myDiagnostics)
|
|
520
|
-
*
|
|
521
|
-
* // Through the shared lookup (loose typing):
|
|
522
|
-
* throw ctx.diagnostics.logger.MYP0001()
|
|
523
|
-
*
|
|
524
|
-
* // Or directly on the typed handle returned from `defineDiagnostics`:
|
|
525
|
-
* throw myDiagnostics.MYP0001()
|
|
526
|
-
* ```
|
|
527
|
-
*/
|
|
528
|
-
interface DevframeDiagnosticsHost {
|
|
529
|
-
/**
|
|
530
|
-
* Proxy-backed lookup of every registered diagnostic handle by code name.
|
|
531
|
-
* Resolves to a `nostics` `DiagnosticHandle` — a callable that builds a
|
|
532
|
-
* diagnostic and routes it through registered reporters; prefix with
|
|
533
|
-
* `throw` to raise. Loosely typed — for autocompletion, keep a reference
|
|
534
|
-
* to the typed result of `defineDiagnostics()` instead.
|
|
535
|
-
*/
|
|
536
|
-
readonly logger: DevframeDiagnosticsLogger;
|
|
537
|
-
/**
|
|
538
|
-
* Register additional diagnostic definitions with this host. After
|
|
539
|
-
* registration, codes from the new definition are reachable via
|
|
540
|
-
* `host.logger.CODE`. Plugins that want shared output formatting should
|
|
541
|
-
* build their diagnostics via `host.defineDiagnostics()` first — that
|
|
542
|
-
* factory pre-wires the host's ANSI console reporter.
|
|
543
|
-
*/
|
|
544
|
-
register: (definitions: Record<string, unknown>) => void;
|
|
414
|
+
register: (definitions: Record<string, unknown>) => void;
|
|
545
415
|
/**
|
|
546
416
|
* Build a typed diagnostics object with the host's ANSI console reporter
|
|
547
417
|
* pre-wired. The same `devframe/utils/nostics` `defineDiagnostics` every
|
|
@@ -620,123 +490,314 @@ interface DevframeHost {
|
|
|
620
490
|
*/
|
|
621
491
|
type DevframeStorageScope = 'workspace' | 'project' | 'global';
|
|
622
492
|
//#endregion
|
|
623
|
-
//#region src/types/
|
|
624
|
-
|
|
493
|
+
//#region src/types/rpc-augments.d.ts
|
|
494
|
+
/**
|
|
495
|
+
* To be extended
|
|
496
|
+
*/
|
|
497
|
+
interface DevframeRpcClientFunctions {
|
|
625
498
|
/**
|
|
499
|
+
* Server→client notification that this connection's auth token has been
|
|
500
|
+
* revoked. The client drops to untrusted on receipt. Broadcast by
|
|
501
|
+
* `revokeActiveConnectionsForToken`.
|
|
502
|
+
*
|
|
626
503
|
* @internal
|
|
627
504
|
*/
|
|
628
|
-
|
|
629
|
-
baseUrl: string;
|
|
630
|
-
source: StaticAssetsSource;
|
|
631
|
-
}[];
|
|
505
|
+
'devframe:auth:revoked': () => Promise<void>;
|
|
632
506
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
* - In `build` mode, it will copy the static files to the dist directory
|
|
507
|
+
* Streaming chunk pushed from server to subscribed clients. Wired by
|
|
508
|
+
* `RpcStreamingHost`; do not register manually.
|
|
636
509
|
*
|
|
637
|
-
*
|
|
638
|
-
* declaration served through devframe's caching CDN back-proxy.
|
|
510
|
+
* @internal
|
|
639
511
|
*/
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
//#endregion
|
|
643
|
-
//#region src/types/scope.d.ts
|
|
644
|
-
type AnyRpcFn = (...args: any[]) => any;
|
|
645
|
-
/**
|
|
646
|
-
* Resolve a scoped bare name `T` back to its fully-qualified entry in
|
|
647
|
-
* `Registry` (`<NS>:<T>`). Indexing the plain registry behind a `keyof`
|
|
648
|
-
* guard keeps TypeScript from rejecting a generic index into the
|
|
649
|
-
* key-remapped `Scoped*Functions<NS>` maps (TS2536) once a plugin augments
|
|
650
|
-
* the registry, while preserving each name's argument/return typing.
|
|
651
|
-
*/
|
|
652
|
-
type ScopedRpcFn<Registry, NS extends string, T extends string> = `${NS}:${T}` extends keyof Registry ? Extract<Registry[`${NS}:${T}`], AnyRpcFn> : AnyRpcFn;
|
|
653
|
-
/**
|
|
654
|
-
* Augmentable registry mapping a scope namespace to the shape of its
|
|
655
|
-
* persisted settings. Tools augment it so `ctx.scope('my-plugin')`
|
|
656
|
-
* gets a fully-typed `settings.global` / `settings.project`:
|
|
657
|
-
*
|
|
658
|
-
* ```ts
|
|
659
|
-
* declare module 'devframe' {
|
|
660
|
-
* interface DevframeSettingsRegistry {
|
|
661
|
-
* 'my-plugin': { theme: 'light' | 'dark', recentFiles: string[] }
|
|
662
|
-
* }
|
|
663
|
-
* }
|
|
664
|
-
* ```
|
|
665
|
-
*/
|
|
666
|
-
interface DevframeSettingsRegistry {}
|
|
667
|
-
/**
|
|
668
|
-
* Resolve the settings shape for a namespace from
|
|
669
|
-
* {@link DevframeSettingsRegistry}, falling back to an open record when
|
|
670
|
-
* the namespace hasn't been augmented.
|
|
671
|
-
*/
|
|
672
|
-
type SettingsForNamespace<NS extends string> = NS extends keyof DevframeSettingsRegistry ? DevframeSettingsRegistry[NS] extends Record<string, any> ? DevframeSettingsRegistry[NS] : Record<string, any> : Record<string, any>;
|
|
673
|
-
/**
|
|
674
|
-
* A persisted key-value settings store for one scope (`global` or
|
|
675
|
-
* `project`). Backed by a file on the node side and by the shared-state
|
|
676
|
-
* sync protocol on the client, so a `set` on either side propagates to
|
|
677
|
-
* every connected peer and survives restarts.
|
|
678
|
-
*
|
|
679
|
-
* Every method is async because the underlying shared state is resolved
|
|
680
|
-
* lazily on first access.
|
|
681
|
-
*/
|
|
682
|
-
interface DevframeSettingsStore<T extends Record<string, any> = Record<string, any>> {
|
|
683
|
-
/** Read a single setting. Resolves to `undefined` when unset. */
|
|
684
|
-
get: <K extends keyof T>(key: K) => Promise<T[K] | undefined>;
|
|
685
|
-
/** Write a single setting. */
|
|
686
|
-
set: <K extends keyof T>(key: K, value: T[K]) => Promise<void>;
|
|
687
|
-
/** Remove a single setting. */
|
|
688
|
-
delete: <K extends keyof T>(key: K) => Promise<void>;
|
|
689
|
-
/** Read the whole settings object (immutable snapshot). */
|
|
690
|
-
all: () => Promise<Readonly<T>>;
|
|
512
|
+
'devframe:streaming:chunk': (channel: string, id: string, seq: number, chunk: any) => Promise<void>;
|
|
691
513
|
/**
|
|
692
|
-
*
|
|
693
|
-
*
|
|
514
|
+
* Streaming terminator pushed from server to subscribed clients. Wired by
|
|
515
|
+
* `RpcStreamingHost`; do not register manually.
|
|
516
|
+
*
|
|
517
|
+
* @internal
|
|
694
518
|
*/
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* Map the shared-state registry down to the bare keys owned by a
|
|
722
|
-
* namespace, so scoped `sharedState('messages')` is typed without the
|
|
723
|
-
* `my-plugin:` prefix.
|
|
724
|
-
*/
|
|
725
|
-
type ScopedSharedStates<NS extends string> = { [K in keyof DevframeRpcSharedStates as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcSharedStates[K]; };
|
|
726
|
-
/**
|
|
727
|
-
* Broadcast options for a scoped client method (bare name).
|
|
728
|
-
*/
|
|
729
|
-
interface ScopedBroadcastOptions<METHOD, Args extends any[]> {
|
|
730
|
-
method: METHOD;
|
|
731
|
-
args: Args;
|
|
732
|
-
optional?: boolean;
|
|
733
|
-
event?: boolean;
|
|
519
|
+
'devframe:streaming:end': (channel: string, id: string, error?: {
|
|
520
|
+
name: string;
|
|
521
|
+
message: string;
|
|
522
|
+
}) => Promise<void>;
|
|
523
|
+
/**
|
|
524
|
+
* Server→client cancel for an in-flight upload. Wired by
|
|
525
|
+
* `RpcStreamingHost`; do not register manually.
|
|
526
|
+
*
|
|
527
|
+
* @internal
|
|
528
|
+
*/
|
|
529
|
+
'devframe:streaming:upload-cancel': (channel: string, id: string) => Promise<void>;
|
|
530
|
+
/**
|
|
531
|
+
* Full shared-state snapshot pushed from server to subscribed clients.
|
|
532
|
+
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
533
|
+
*
|
|
534
|
+
* @internal
|
|
535
|
+
*/
|
|
536
|
+
'devframe:rpc:client-state:updated': (key: string, fullState: any, syncId: string) => Promise<void>;
|
|
537
|
+
/**
|
|
538
|
+
* Incremental shared-state patch pushed from server to subscribed clients.
|
|
539
|
+
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
540
|
+
*
|
|
541
|
+
* @internal
|
|
542
|
+
*/
|
|
543
|
+
'devframe:rpc:client-state:patch': (key: string, patches: any[], syncId: string) => Promise<void>;
|
|
734
544
|
}
|
|
735
545
|
/**
|
|
736
|
-
*
|
|
737
|
-
* auto-prefixed with `<namespace>:`.
|
|
546
|
+
* To be extended
|
|
738
547
|
*/
|
|
739
|
-
interface
|
|
548
|
+
interface DevframeRpcServerFunctions {
|
|
549
|
+
/**
|
|
550
|
+
* Authenticate a connection with a previously-issued bearer token; resolves
|
|
551
|
+
* whether the connection is now trusted. The interactive handler is provided
|
|
552
|
+
* by the host adapter (e.g. Vite DevTools); the standalone server registers
|
|
553
|
+
* an auto-trust noop when `auth: false`.
|
|
554
|
+
*
|
|
555
|
+
* Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
|
|
556
|
+
* reachable before the connection is trusted — this is the *only* rule the
|
|
557
|
+
* pre-trust gate applies, no per-method allowlist.
|
|
558
|
+
*
|
|
559
|
+
* @internal
|
|
560
|
+
*/
|
|
561
|
+
'anonymous:devframe:auth': (params: {
|
|
562
|
+
authToken: string;
|
|
563
|
+
ua: string;
|
|
564
|
+
origin: string;
|
|
565
|
+
}) => Promise<{
|
|
566
|
+
isTrusted: boolean;
|
|
567
|
+
}>;
|
|
568
|
+
/**
|
|
569
|
+
* Exchange a one-time authentication code (shown by the dev server) for a fresh,
|
|
570
|
+
* node-issued bearer token, returning the token on success or `null`. The
|
|
571
|
+
* handler is provided by the host adapter on top of `exchangeTempAuthCode`.
|
|
572
|
+
*
|
|
573
|
+
* Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
|
|
574
|
+
* reachable before the connection is trusted.
|
|
575
|
+
*
|
|
576
|
+
* @internal
|
|
577
|
+
*/
|
|
578
|
+
'anonymous:devframe:auth:exchange': (params: {
|
|
579
|
+
code: string;
|
|
580
|
+
ua: string;
|
|
581
|
+
origin: string;
|
|
582
|
+
}) => Promise<{
|
|
583
|
+
authToken: string | null;
|
|
584
|
+
}>;
|
|
585
|
+
/**
|
|
586
|
+
* Self-revoke: the caller asks the server to revoke its own bearer token
|
|
587
|
+
* (if any) and drop to untrusted. Requires an already-trusted caller, so
|
|
588
|
+
* unlike the two handshake methods above it does **not** carry the
|
|
589
|
+
* `anonymous:` prefix. Registered by `recipes/interactive-auth`.
|
|
590
|
+
*
|
|
591
|
+
* @internal
|
|
592
|
+
*/
|
|
593
|
+
'devframe:auth:revoke': () => Promise<void>;
|
|
594
|
+
/**
|
|
595
|
+
* Subscribe a client to a shared-state key. Wired by
|
|
596
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
597
|
+
*
|
|
598
|
+
* @internal
|
|
599
|
+
*/
|
|
600
|
+
'devframe:rpc:server-state:subscribe': (key: string) => Promise<void>;
|
|
601
|
+
/**
|
|
602
|
+
* Read the current value for a shared-state key. Wired by
|
|
603
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
604
|
+
*
|
|
605
|
+
* @internal
|
|
606
|
+
*/
|
|
607
|
+
'devframe:rpc:server-state:get': (key: string) => Promise<any>;
|
|
608
|
+
/**
|
|
609
|
+
* Replace a shared-state value (from the client). Wired by
|
|
610
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
611
|
+
*
|
|
612
|
+
* @internal
|
|
613
|
+
*/
|
|
614
|
+
'devframe:rpc:server-state:set': (key: string, value: any, syncId: string) => Promise<void>;
|
|
615
|
+
/**
|
|
616
|
+
* Apply a patch to a shared-state value (from the client). Wired by
|
|
617
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
618
|
+
*
|
|
619
|
+
* @internal
|
|
620
|
+
*/
|
|
621
|
+
'devframe:rpc:server-state:patch': (key: string, patches: any[], syncId: string) => Promise<void>;
|
|
622
|
+
/**
|
|
623
|
+
* Client→server streaming subscription with optional replay cursor.
|
|
624
|
+
* Wired by `RpcStreamingHost`; do not register manually.
|
|
625
|
+
*
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
628
|
+
'devframe:streaming:subscribe': (channel: string, id: string, opts?: {
|
|
629
|
+
afterSeq?: number;
|
|
630
|
+
}) => Promise<void>;
|
|
631
|
+
/**
|
|
632
|
+
* Client→server streaming unsubscribe. Wired by `RpcStreamingHost`;
|
|
633
|
+
* do not register manually.
|
|
634
|
+
*
|
|
635
|
+
* @internal
|
|
636
|
+
*/
|
|
637
|
+
'devframe:streaming:unsubscribe': (channel: string, id: string) => Promise<void>;
|
|
638
|
+
/**
|
|
639
|
+
* Client→server streaming cancellation request. Wired by
|
|
640
|
+
* `RpcStreamingHost`; do not register manually.
|
|
641
|
+
*
|
|
642
|
+
* @internal
|
|
643
|
+
*/
|
|
644
|
+
'devframe:streaming:cancel': (channel: string, id: string) => Promise<void>;
|
|
645
|
+
/**
|
|
646
|
+
* Client→server upload chunk. Wired by `RpcStreamingHost`; do not
|
|
647
|
+
* register manually.
|
|
648
|
+
*
|
|
649
|
+
* @internal
|
|
650
|
+
*/
|
|
651
|
+
'devframe:streaming:upload-chunk': (channel: string, id: string, seq: number, chunk: any) => Promise<void>;
|
|
652
|
+
/**
|
|
653
|
+
* Client→server upload terminator. Wired by `RpcStreamingHost`; do not
|
|
654
|
+
* register manually.
|
|
655
|
+
*
|
|
656
|
+
* @internal
|
|
657
|
+
*/
|
|
658
|
+
'devframe:streaming:upload-end': (channel: string, id: string, error?: {
|
|
659
|
+
name: string;
|
|
660
|
+
message: string;
|
|
661
|
+
}) => Promise<void>;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* To be extended
|
|
665
|
+
*/
|
|
666
|
+
interface DevframeRpcSharedStates {
|
|
667
|
+
/**
|
|
668
|
+
* Wire-service advertisements — package name → `{ package, version,
|
|
669
|
+
* scope, meta }` for every installed {@link import('./services').DevframeServiceDefinition}.
|
|
670
|
+
* Written by the node services host at the `ready()` barrier; clients read
|
|
671
|
+
* it through `client.services` (or subscribe to it directly for
|
|
672
|
+
* reactivity). Read-only from the browser.
|
|
673
|
+
*/
|
|
674
|
+
'devframe:services': DevframeServicesState;
|
|
675
|
+
}
|
|
676
|
+
//#endregion
|
|
677
|
+
//#region src/types/views.d.ts
|
|
678
|
+
interface DevframeViewHost {
|
|
679
|
+
/**
|
|
680
|
+
* @internal
|
|
681
|
+
*/
|
|
682
|
+
buildStaticDirs: {
|
|
683
|
+
baseUrl: string;
|
|
684
|
+
source: StaticAssetsSource;
|
|
685
|
+
}[];
|
|
686
|
+
/**
|
|
687
|
+
* Helper to host static files
|
|
688
|
+
* - In `dev` mode, it will register middleware to `viteServer.middlewares` to host the static files
|
|
689
|
+
* - In `build` mode, it will copy the static files to the dist directory
|
|
690
|
+
*
|
|
691
|
+
* Accepts a local dist directory, or a {@link StaticAssetsSource} remote
|
|
692
|
+
* declaration served through devframe's caching CDN back-proxy.
|
|
693
|
+
*
|
|
694
|
+
* `defaultResolveFrom` overrides, for this call only, the context's own
|
|
695
|
+
* `importMetaUrl` as the default `resolveFrom` for a remote source that
|
|
696
|
+
* doesn't set one. A shared host that mounts assets on behalf of another
|
|
697
|
+
* devframe (a hub installing a plugin) passes that plugin's `importMetaUrl`
|
|
698
|
+
* so the assets resolve against the plugin's dependency graph rather than
|
|
699
|
+
* the host's.
|
|
700
|
+
*/
|
|
701
|
+
hostStatic: (baseUrl: string, source: StaticAssetsSource, defaultResolveFrom?: string | null) => void;
|
|
702
|
+
}
|
|
703
|
+
//#endregion
|
|
704
|
+
//#region src/types/scope.d.ts
|
|
705
|
+
type AnyRpcFn = (...args: any[]) => any;
|
|
706
|
+
/**
|
|
707
|
+
* Resolve a scoped bare name `T` back to its fully-qualified entry in
|
|
708
|
+
* `Registry` (`<NS>:<T>`). Indexing the plain registry behind a `keyof`
|
|
709
|
+
* guard keeps TypeScript from rejecting a generic index into the
|
|
710
|
+
* key-remapped `Scoped*Functions<NS>` maps (TS2536) once a plugin augments
|
|
711
|
+
* the registry, while preserving each name's argument/return typing.
|
|
712
|
+
*/
|
|
713
|
+
type ScopedRpcFn<Registry, NS extends string, T extends string> = `${NS}:${T}` extends keyof Registry ? Extract<Registry[`${NS}:${T}`], AnyRpcFn> : AnyRpcFn;
|
|
714
|
+
/**
|
|
715
|
+
* Augmentable registry mapping a scope namespace to the shape of its
|
|
716
|
+
* persisted settings. Tools augment it so `ctx.scope('my-plugin')`
|
|
717
|
+
* gets a fully-typed `settings.global` / `settings.project`:
|
|
718
|
+
*
|
|
719
|
+
* ```ts
|
|
720
|
+
* declare module 'devframe' {
|
|
721
|
+
* interface DevframeSettingsRegistry {
|
|
722
|
+
* 'my-plugin': { theme: 'light' | 'dark', recentFiles: string[] }
|
|
723
|
+
* }
|
|
724
|
+
* }
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
interface DevframeSettingsRegistry {}
|
|
728
|
+
/**
|
|
729
|
+
* Resolve the settings shape for a namespace from
|
|
730
|
+
* {@link DevframeSettingsRegistry}, falling back to an open record when
|
|
731
|
+
* the namespace hasn't been augmented.
|
|
732
|
+
*/
|
|
733
|
+
type SettingsForNamespace<NS extends string> = NS extends keyof DevframeSettingsRegistry ? DevframeSettingsRegistry[NS] extends Record<string, any> ? DevframeSettingsRegistry[NS] : Record<string, any> : Record<string, any>;
|
|
734
|
+
/**
|
|
735
|
+
* A persisted key-value settings store for one scope (`global` or
|
|
736
|
+
* `project`). Backed by a file on the node side and by the shared-state
|
|
737
|
+
* sync protocol on the client, so a `set` on either side propagates to
|
|
738
|
+
* every connected peer and survives restarts.
|
|
739
|
+
*
|
|
740
|
+
* Every method is async because the underlying shared state is resolved
|
|
741
|
+
* lazily on first access.
|
|
742
|
+
*/
|
|
743
|
+
interface DevframeSettingsStore<T extends Record<string, any> = Record<string, any>> {
|
|
744
|
+
/** Read a single setting. Resolves to `undefined` when unset. */
|
|
745
|
+
get: <K extends keyof T>(key: K) => Promise<T[K] | undefined>;
|
|
746
|
+
/** Write a single setting. */
|
|
747
|
+
set: <K extends keyof T>(key: K, value: T[K]) => Promise<void>;
|
|
748
|
+
/** Remove a single setting. */
|
|
749
|
+
delete: <K extends keyof T>(key: K) => Promise<void>;
|
|
750
|
+
/** Read the whole settings object (immutable snapshot). */
|
|
751
|
+
all: () => Promise<Readonly<T>>;
|
|
752
|
+
/**
|
|
753
|
+
* Subscribe to settings changes. Resolves to an unsubscribe function.
|
|
754
|
+
* Fires on every local or remote mutation with the full snapshot.
|
|
755
|
+
*/
|
|
756
|
+
onChange: (fn: (value: Readonly<T>) => void) => Promise<() => void>;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* The two settings scopes available on a scoped context.
|
|
760
|
+
*
|
|
761
|
+
* - `project` — per-workspace state, persisted under the host's
|
|
762
|
+
* `workspace` storage dir. Project-local settings.
|
|
763
|
+
* - `global` — per-user state, persisted under the host's `global`
|
|
764
|
+
* storage dir. Machine-wide preferences.
|
|
765
|
+
*/
|
|
766
|
+
interface DevframeSettings<T extends Record<string, any> = Record<string, any>> {
|
|
767
|
+
global: DevframeSettingsStore<T>;
|
|
768
|
+
project: DevframeSettingsStore<T>;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Map the server-side RPC registry down to the bare names owned by a
|
|
772
|
+
* namespace, so a scoped `call('get-cwd')` is typed without the
|
|
773
|
+
* `my-plugin:` prefix.
|
|
774
|
+
*/
|
|
775
|
+
type ScopedServerFunctions<NS extends string> = { [K in keyof DevframeRpcServerFunctions as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcServerFunctions[K]; };
|
|
776
|
+
/**
|
|
777
|
+
* Map the client-side RPC registry down to the bare names owned by a
|
|
778
|
+
* namespace, used by scoped `broadcast`.
|
|
779
|
+
*/
|
|
780
|
+
type ScopedClientFunctions<NS extends string> = { [K in keyof DevframeRpcClientFunctions as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcClientFunctions[K]; };
|
|
781
|
+
/**
|
|
782
|
+
* Map the shared-state registry down to the bare keys owned by a
|
|
783
|
+
* namespace, so scoped `sharedState('messages')` is typed without the
|
|
784
|
+
* `my-plugin:` prefix.
|
|
785
|
+
*/
|
|
786
|
+
type ScopedSharedStates<NS extends string> = { [K in keyof DevframeRpcSharedStates as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcSharedStates[K]; };
|
|
787
|
+
/**
|
|
788
|
+
* Broadcast options for a scoped client method (bare name).
|
|
789
|
+
*/
|
|
790
|
+
interface ScopedBroadcastOptions<METHOD, Args extends any[]> {
|
|
791
|
+
method: METHOD;
|
|
792
|
+
args: Args;
|
|
793
|
+
optional?: boolean;
|
|
794
|
+
event?: boolean;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Node-side streaming host scoped to a namespace. Channel names are
|
|
798
|
+
* auto-prefixed with `<namespace>:`.
|
|
799
|
+
*/
|
|
800
|
+
interface DevframeScopedStreamingHost {
|
|
740
801
|
/**
|
|
741
802
|
* Register a streaming channel. The bare `name` is auto-prefixed with
|
|
742
803
|
* the scope namespace (`<namespace>:<name>`); pass an already-qualified
|
|
@@ -981,310 +1042,701 @@ interface DevframeServicesHost {
|
|
|
981
1042
|
/** Ids of every currently-provided service. */
|
|
982
1043
|
keys: () => string[];
|
|
983
1044
|
/**
|
|
984
|
-
* Install a **wire service** (see {@link DevframeServiceDefinition}).
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
1045
|
+
* Install a **wire service** (see {@link DevframeServiceDefinition}). The
|
|
1046
|
+
* common path is declarative — list services on `DevframeDefinition.services`
|
|
1047
|
+
* (or `initHub({ services })`) and the adapter installs them for you before
|
|
1048
|
+
* `setup` runs. Call `install()` directly only for the dynamic escape hatch:
|
|
1049
|
+
* a service configured at runtime from data unknown until then.
|
|
1050
|
+
*
|
|
1051
|
+
* Before the pre-setup ready fires, installs are queued and their option
|
|
1052
|
+
* sets deep-merged, constructing each service **once**. After it, an install
|
|
1053
|
+
* constructs immediately; installing an already-installed package returns
|
|
1054
|
+
* the existing API (a warning — `DF0066` — when the late install carried
|
|
1055
|
+
* options, since they're ignored). The returned promise resolves with the
|
|
1056
|
+
* node API (or `undefined` when an optional descriptor's package can't be
|
|
1057
|
+
* imported).
|
|
993
1058
|
*
|
|
994
1059
|
* `resolveFrom` is where a descriptor's package resolves **from**: a path
|
|
995
|
-
* or file URL (e.g. `
|
|
996
|
-
*
|
|
997
|
-
*
|
|
998
|
-
* `workspaceRoot`.
|
|
1060
|
+
* or file URL (e.g. the declaring devframe's `importMetaUrl`), or an npm
|
|
1061
|
+
* package name, so its declared services resolve against the declarer's own
|
|
1062
|
+
* dependencies. Falls back to the context's `workspaceRoot` then `cwd`.
|
|
999
1063
|
*/
|
|
1000
1064
|
install: <API = unknown, Options = any>(input: DevframeServiceInput<API, Options>, options?: {
|
|
1001
1065
|
resolveFrom?: string | null;
|
|
1002
1066
|
}) => Promise<API | undefined>;
|
|
1003
1067
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1010
|
-
* service fails to import or
|
|
1068
|
+
* Construct every queued service — importing descriptor packages, merging
|
|
1069
|
+
* option sets, `provide()`-ing each node API under its package name, and
|
|
1070
|
+
* advertising it to clients via the `devframe:services` shared state.
|
|
1071
|
+
* Idempotent. **Internal**: the adapters call it once, before running any
|
|
1072
|
+
* `setup`, so services are ready for `setup` to consume; application code
|
|
1073
|
+
* uses declarative `services` (or `install()` for the dynamic case) and
|
|
1074
|
+
* never calls this. Rejects when a `required` service fails to import or
|
|
1075
|
+
* misses its version range.
|
|
1011
1076
|
*/
|
|
1012
1077
|
ready: () => Promise<void>;
|
|
1013
1078
|
}
|
|
1014
1079
|
//#endregion
|
|
1015
|
-
//#region src/types/
|
|
1080
|
+
//#region src/types/devframe.d.ts
|
|
1016
1081
|
/**
|
|
1017
|
-
*
|
|
1082
|
+
* Classification of how a devframe is being deployed. Hosted adapters
|
|
1083
|
+
* (`vite`, `embedded`) share their origin with a host app and must
|
|
1084
|
+
* namespace their mount path under `/__<id>/`. Standalone adapters
|
|
1085
|
+
* (`cli`, `build`) own the origin and default to `/`.
|
|
1018
1086
|
*/
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1087
|
+
type DevframeDeploymentKind = 'standalone' | 'hosted';
|
|
1088
|
+
/**
|
|
1089
|
+
* How a hub deduplicates devframes that share an `id` when more than one
|
|
1090
|
+
* is mounted onto the same hub. See {@link DevframeDefinition.duplicationStrategy}.
|
|
1091
|
+
*
|
|
1092
|
+
* - `'warn'` (default) — keep the first registration, drop later
|
|
1093
|
+
* duplicates, and emit a warning diagnostic (`DF8105`).
|
|
1094
|
+
* - `'silent'` — drop later duplicates without warning.
|
|
1095
|
+
* - `'throw'` — throw when a duplicate is mounted.
|
|
1096
|
+
* - `'duplicate'` — let every instance coexist under a disambiguated
|
|
1097
|
+
* dock id.
|
|
1098
|
+
*/
|
|
1099
|
+
type DevframeDuplicationStrategy = 'warn' | 'silent' | 'throw' | 'duplicate';
|
|
1100
|
+
/**
|
|
1101
|
+
* Controls where the browser opens the RPC WebSocket — advertised in
|
|
1102
|
+
* `__connection.json` and used to bind the dev server. The three shapes map
|
|
1103
|
+
* to the three connection scenarios; precedence is `url` > `port` > `route`:
|
|
1104
|
+
*
|
|
1105
|
+
* 1. **Same server, different route** (default) — leave `port`/`url` unset.
|
|
1106
|
+
* The socket shares the HTTP server's port and binds to `route`
|
|
1107
|
+
* (`__ws`). The client connects to its own origin, so the link
|
|
1108
|
+
* survives a reverse proxy that rewrites the host/port/subpath.
|
|
1109
|
+
*
|
|
1110
|
+
* 2. **Different port** — set `port`. The socket binds on its own port on the
|
|
1111
|
+
* same host; the client targets `ws(s)://<page-host>:<port>/<route>`.
|
|
1112
|
+
*
|
|
1113
|
+
* 3. **Remote, different origin** — set `url` to a full `ws://`/`wss://`
|
|
1114
|
+
* endpoint (e.g. a public tunnel or relay). The client uses it verbatim.
|
|
1115
|
+
*/
|
|
1116
|
+
interface DevframeWsOptions {
|
|
1028
1117
|
/**
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
* @internal
|
|
1118
|
+
* Upgrade route segment the socket binds to and is advertised at, relative
|
|
1119
|
+
* to the SPA base. Default: `__ws`.
|
|
1033
1120
|
*/
|
|
1034
|
-
|
|
1121
|
+
route?: string;
|
|
1035
1122
|
/**
|
|
1036
|
-
*
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1039
|
-
* @internal
|
|
1123
|
+
* Bind the socket on its own port instead of sharing the HTTP port. The
|
|
1124
|
+
* browser connects to this port on the page's hostname. Implies
|
|
1125
|
+
* {@link DevframeWsOptions.sidecar}.
|
|
1040
1126
|
*/
|
|
1041
|
-
|
|
1042
|
-
name: string;
|
|
1043
|
-
message: string;
|
|
1044
|
-
}) => Promise<void>;
|
|
1127
|
+
port?: number;
|
|
1045
1128
|
/**
|
|
1046
|
-
*
|
|
1047
|
-
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1129
|
+
* Start a side-car WebSocket server on a free port, for hosts whose request
|
|
1130
|
+
* handlers can't accept upgrades (Next.js route handlers, Nitro, Rsbuild).
|
|
1131
|
+
* The resolved port is advertised in `__connection.json`, so the browser
|
|
1132
|
+
* finds it without any further wiring. Set `port` instead to pin it.
|
|
1050
1133
|
*/
|
|
1051
|
-
|
|
1134
|
+
sidecar?: boolean;
|
|
1052
1135
|
/**
|
|
1053
|
-
*
|
|
1054
|
-
*
|
|
1055
|
-
*
|
|
1056
|
-
* @internal
|
|
1136
|
+
* Advertise a fixed, fully-qualified endpoint on another origin (a full
|
|
1137
|
+
* `ws://`/`wss://` URL). Takes precedence over `port`/`route` in the meta.
|
|
1057
1138
|
*/
|
|
1058
|
-
|
|
1139
|
+
url?: string;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Controls the SSE RPC endpoint — the WebSocket-free transport for hosts
|
|
1143
|
+
* and proxies where the upgrade isn't available. It rides whatever HTTP
|
|
1144
|
+
* surface serves the instance (the same one serving `__connection.json`),
|
|
1145
|
+
* so a relative `route` always resolves; there is no port plumbing of its
|
|
1146
|
+
* own. Enabled by default alongside the WebSocket; pass `sse: false` to
|
|
1147
|
+
* disable, or `ws: false` to run SSE-only (`backend: 'sse'`).
|
|
1148
|
+
*/
|
|
1149
|
+
interface DevframeSseOptions {
|
|
1059
1150
|
/**
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
* @internal
|
|
1151
|
+
* Route segment the SSE endpoint binds to and is advertised at, relative
|
|
1152
|
+
* to the SPA base. Default: `__sse`.
|
|
1064
1153
|
*/
|
|
1065
|
-
|
|
1154
|
+
route?: string;
|
|
1066
1155
|
}
|
|
1067
1156
|
/**
|
|
1068
|
-
*
|
|
1157
|
+
* Configuration for the route-based MCP server mounted alongside the dev
|
|
1158
|
+
* server (opt-in via {@link DevframeCliOptions.mcp}). The endpoint speaks
|
|
1159
|
+
* the MCP Streamable-HTTP transport over the same origin as the SPA,
|
|
1160
|
+
* exposing the definition's `ctx.agent` tools + shared-state resources to
|
|
1161
|
+
* external MCP clients connected to the *running* server.
|
|
1069
1162
|
*/
|
|
1070
|
-
interface
|
|
1163
|
+
interface McpRouteOptions {
|
|
1071
1164
|
/**
|
|
1072
|
-
*
|
|
1073
|
-
*
|
|
1074
|
-
* by the host adapter (e.g. Vite DevTools); the standalone server registers
|
|
1075
|
-
* an auto-trust noop when `auth: false`.
|
|
1076
|
-
*
|
|
1077
|
-
* Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
|
|
1078
|
-
* reachable before the connection is trusted — this is the *only* rule the
|
|
1079
|
-
* pre-trust gate applies, no per-method allowlist.
|
|
1080
|
-
*
|
|
1081
|
-
* @internal
|
|
1165
|
+
* Route segment the MCP endpoint binds to, relative to the SPA base.
|
|
1166
|
+
* Default: `__mcp` (i.e. `/__mcp` standalone, `/__<id>/__mcp` hosted).
|
|
1082
1167
|
*/
|
|
1083
|
-
|
|
1084
|
-
authToken: string;
|
|
1085
|
-
ua: string;
|
|
1086
|
-
origin: string;
|
|
1087
|
-
}) => Promise<{
|
|
1088
|
-
isTrusted: boolean;
|
|
1089
|
-
}>;
|
|
1168
|
+
path?: string;
|
|
1090
1169
|
/**
|
|
1091
|
-
*
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
*
|
|
1096
|
-
* reachable before the connection is trusted.
|
|
1170
|
+
* Extra `Origin` header values to accept beyond the loopback default
|
|
1171
|
+
* (`localhost`/`127.0.0.1`/`::1` and any `Origin`-less native client).
|
|
1172
|
+
* Add your LAN/tunnel origin here when reaching the endpoint from another
|
|
1173
|
+
* host, mirroring the WS transport's origin gate. Pass `false` to disable
|
|
1174
|
+
* origin checking entirely (not recommended). Default: loopback-only.
|
|
1097
1175
|
*
|
|
1098
|
-
*
|
|
1176
|
+
* This is the endpoint's DNS-rebinding protection — the shared
|
|
1177
|
+
* `isAllowedOrigin` gate the WS upgrade already uses, applied as external
|
|
1178
|
+
* middleware (the approach the MCP SDK now recommends over its own
|
|
1179
|
+
* deprecated `allowedHosts`/`allowedOrigins` transport flags).
|
|
1099
1180
|
*/
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1181
|
+
allowedOrigins?: readonly string[] | false;
|
|
1182
|
+
}
|
|
1183
|
+
interface DevframeCliOptions {
|
|
1184
|
+
/** Binary name; default: the devframe's `id`. */
|
|
1185
|
+
command?: string;
|
|
1186
|
+
/** Preferred port for the dev server (default 9999). */
|
|
1187
|
+
port?: number;
|
|
1188
|
+
/** Port scan range, forwarded to `get-port-please`. */
|
|
1189
|
+
portRange?: [number, number];
|
|
1190
|
+
/** Prefer a random open port. */
|
|
1191
|
+
random?: boolean;
|
|
1192
|
+
/** Default host to bind to; `--host` overrides. */
|
|
1193
|
+
host?: string;
|
|
1107
1194
|
/**
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
* `anonymous:` prefix. Registered by `recipes/interactive-auth`.
|
|
1112
|
-
*
|
|
1113
|
-
* @internal
|
|
1195
|
+
* Auto-open the browser when the dev server starts.
|
|
1196
|
+
* `true` opens the resolved origin; a string opens that relative path.
|
|
1197
|
+
* The `--open` / `--no-open` flags override this.
|
|
1114
1198
|
*/
|
|
1115
|
-
|
|
1199
|
+
open?: boolean | string;
|
|
1116
1200
|
/**
|
|
1117
|
-
*
|
|
1118
|
-
* `RpcSharedStateHost`; do not register manually.
|
|
1201
|
+
* Authentication for the standalone dev server.
|
|
1119
1202
|
*
|
|
1120
|
-
*
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1203
|
+
* - `undefined` / `true` — the standalone adapters (`cli` /
|
|
1204
|
+
* served `build`) auto-wire devframe's interactive OTP auth
|
|
1205
|
+
* (`createInteractiveAuth`): an untrusted client can only reach
|
|
1206
|
+
* `anonymous:` methods until it exchanges the printed one-time code.
|
|
1207
|
+
* The adapter prints the code + magic-link banner once the server is
|
|
1208
|
+
* listening.
|
|
1209
|
+
* - `false` — no gate, for trusted single-user localhost tools where an
|
|
1210
|
+
* auth round-trip only gets in the way (the built-in plugins set this).
|
|
1211
|
+
* The `--no-auth` CLI flag maps here for one-off runs.
|
|
1212
|
+
* - A {@link DevframeAuthHandler} — a custom handler (e.g. a tuned
|
|
1213
|
+
* `createInteractiveAuth`, or an entirely different scheme) passed
|
|
1214
|
+
* straight through to the RPC transport binding.
|
|
1126
1215
|
*
|
|
1127
|
-
*
|
|
1128
|
-
|
|
1129
|
-
'devframe:rpc:server-state:get': (key: string) => Promise<any>;
|
|
1130
|
-
/**
|
|
1131
|
-
* Replace a shared-state value (from the client). Wired by
|
|
1132
|
-
* `RpcSharedStateHost`; do not register manually.
|
|
1216
|
+
* Hosted adapters (`vite`, `embedded`) ignore this and defer to the host's
|
|
1217
|
+
* auth; `@vitejs/devtools` honors the equivalent `devtools.clientAuth`.
|
|
1133
1218
|
*
|
|
1134
|
-
* @
|
|
1219
|
+
* @default true
|
|
1135
1220
|
*/
|
|
1136
|
-
|
|
1221
|
+
auth?: boolean | DevframeAuthHandler;
|
|
1137
1222
|
/**
|
|
1138
|
-
*
|
|
1139
|
-
* `
|
|
1223
|
+
* Expose a route-based MCP server alongside the dev server, speaking the
|
|
1224
|
+
* MCP Streamable-HTTP transport at `/__mcp` (relative to the base path).
|
|
1225
|
+
* It surfaces the same `ctx.agent` tools + shared-state resources as the
|
|
1226
|
+
* stdio `mcp` command, but against the live, running server.
|
|
1140
1227
|
*
|
|
1141
|
-
*
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
* Client→server streaming subscription with optional replay cursor.
|
|
1146
|
-
* Wired by `RpcStreamingHost`; do not register manually.
|
|
1228
|
+
* - `false` / omitted (default) — no MCP route is mounted.
|
|
1229
|
+
* - `true` — mount at the default `__mcp` route with the loopback-only
|
|
1230
|
+
* origin gate.
|
|
1231
|
+
* - {@link McpRouteOptions} — customise the route path / allowed origins.
|
|
1147
1232
|
*
|
|
1148
|
-
*
|
|
1233
|
+
* The `--mcp` / `--no-mcp` CLI flags override this per run.
|
|
1149
1234
|
*/
|
|
1150
|
-
|
|
1151
|
-
afterSeq?: number;
|
|
1152
|
-
}) => Promise<void>;
|
|
1235
|
+
mcp?: boolean | McpRouteOptions;
|
|
1153
1236
|
/**
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1157
|
-
*
|
|
1237
|
+
* Author's SPA dist — served as the devframe's UI. A local directory, or
|
|
1238
|
+
* a {@link StaticAssetsSource} remote declaration (`{ package, version }`)
|
|
1239
|
+
* served through devframe's caching CDN back-proxy so the assets need not
|
|
1240
|
+
* ship inside the node package.
|
|
1158
1241
|
*/
|
|
1159
|
-
|
|
1242
|
+
distDir?: StaticAssetsSource;
|
|
1160
1243
|
/**
|
|
1161
|
-
*
|
|
1162
|
-
* `
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1244
|
+
* How the browser reaches the RPC WebSocket. Defaults to sharing the HTTP
|
|
1245
|
+
* port on the `__ws` route. See {@link DevframeWsOptions} for the
|
|
1246
|
+
* different-port and remote-origin variants. Pass `false` to serve no
|
|
1247
|
+
* WebSocket at all — clients connect over SSE instead (`backend: 'sse'`).
|
|
1165
1248
|
*/
|
|
1166
|
-
|
|
1249
|
+
ws?: DevframeWsOptions | false;
|
|
1167
1250
|
/**
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
* @internal
|
|
1251
|
+
* How the browser reaches the SSE RPC endpoint — enabled by default
|
|
1252
|
+
* alongside the WebSocket as the more portable transport. Pass `false`
|
|
1253
|
+
* to disable, or a {@link DevframeSseOptions} to change its route.
|
|
1172
1254
|
*/
|
|
1173
|
-
|
|
1255
|
+
sse?: boolean | DevframeSseOptions;
|
|
1174
1256
|
/**
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
*
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1257
|
+
* Capability-side CAC hook. Called with the CAC instance after the
|
|
1258
|
+
* adapter registers its built-in commands (`build` / `mcp`)
|
|
1259
|
+
* but before `createCac`'s own `configureCli` caller. Use this to
|
|
1260
|
+
* contribute tool-specific flags and subcommands from the definition
|
|
1261
|
+
* itself.
|
|
1262
|
+
*/
|
|
1263
|
+
configure?: (cli: CAC) => void;
|
|
1264
|
+
/**
|
|
1265
|
+
* Typed CLI flags for the default `dev` command, backed by any
|
|
1266
|
+
* [Standard Schema](https://standardschema.dev/) validator (valibot,
|
|
1267
|
+
* zod, arktype, or devframe's built-in `s`). The adapter registers
|
|
1268
|
+
* matching `--kebab-key` options on CAC, validates the parsed values,
|
|
1269
|
+
* and forwards the typed bag to `setup(ctx, { flags })`.
|
|
1270
|
+
*
|
|
1271
|
+
* Use {@link defineCliFlags} to preserve the literal schema-map
|
|
1272
|
+
* shape, and {@link InferCliFlags} to recover the typed output at the
|
|
1273
|
+
* call site:
|
|
1274
|
+
*
|
|
1275
|
+
* ```ts
|
|
1276
|
+
* const appFlags = defineCliFlags({
|
|
1277
|
+
* depth: v.pipe(v.number(), v.integer()),
|
|
1278
|
+
* config: v.optional(v.string()),
|
|
1279
|
+
* })
|
|
1280
|
+
*
|
|
1281
|
+
* defineDevframe({
|
|
1282
|
+
* cli: { flags: appFlags },
|
|
1283
|
+
* setup(ctx, info) {
|
|
1284
|
+
* const flags = info.flags as InferCliFlags<typeof appFlags>
|
|
1285
|
+
* },
|
|
1286
|
+
* })
|
|
1287
|
+
* ```
|
|
1288
|
+
*/
|
|
1289
|
+
flags?: CliFlagsSchema;
|
|
1184
1290
|
}
|
|
1185
1291
|
/**
|
|
1186
|
-
*
|
|
1292
|
+
* Default dock attributes for the iframe entry a hub synthesizes when it
|
|
1293
|
+
* mounts this devframe. Framework-neutral metadata only — the hub layer
|
|
1294
|
+
* (`ctx.install`) merges these beneath its per-mount `dock` overrides,
|
|
1295
|
+
* which in turn sit beneath the locked, derived `id` / `type` / `url`.
|
|
1296
|
+
*
|
|
1297
|
+
* Every field is optional. `title` / `icon` default to the definition's
|
|
1298
|
+
* `name` / `icon` when omitted here; the rest are unset by default.
|
|
1299
|
+
* Standalone adapters (`cli` / `build`) ignore this entirely.
|
|
1187
1300
|
*/
|
|
1188
|
-
interface
|
|
1301
|
+
interface DevframeDockDefaults {
|
|
1302
|
+
/** Dock entry title. Defaults to the definition's `name`. */
|
|
1303
|
+
title?: string;
|
|
1304
|
+
/** Dock entry icon. Defaults to the definition's `icon`. */
|
|
1305
|
+
icon?: string | {
|
|
1306
|
+
light: string;
|
|
1307
|
+
dark: string;
|
|
1308
|
+
};
|
|
1189
1309
|
/**
|
|
1190
|
-
*
|
|
1191
|
-
*
|
|
1192
|
-
* Written by the node services host at the `ready()` barrier; clients read
|
|
1193
|
-
* it through `client.services` (or subscribe to it directly for
|
|
1194
|
-
* reactivity). Read-only from the browser.
|
|
1310
|
+
* Sort weight within the dock; higher sorts earlier.
|
|
1311
|
+
* @default 0
|
|
1195
1312
|
*/
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1313
|
+
defaultOrder?: number;
|
|
1314
|
+
/**
|
|
1315
|
+
* Category the entry groups under in the dock.
|
|
1316
|
+
* @default 'default'
|
|
1317
|
+
*/
|
|
1318
|
+
category?: string;
|
|
1319
|
+
/**
|
|
1320
|
+
* Conditional-visibility expression (same syntax as command `when`
|
|
1321
|
+
* clauses). Set to `'false'` to hide the entry unconditionally.
|
|
1322
|
+
*/
|
|
1323
|
+
when?: string;
|
|
1324
|
+
/**
|
|
1325
|
+
* Render-only visibility expression, same syntax as {@link when}. Hides the
|
|
1326
|
+
* entry's own dock-bar button when it evaluates to `false` while leaving it
|
|
1327
|
+
* registered and reachable (activation, RPC lookups, etc.) — unlike `when`,
|
|
1328
|
+
* which is the general relevance switch for the entry as a whole.
|
|
1329
|
+
*/
|
|
1330
|
+
visibility?: string;
|
|
1331
|
+
/** Badge text rendered on the dock icon (e.g. an unread count). */
|
|
1332
|
+
badge?: string;
|
|
1333
|
+
/** Id of the dock group this entry collapses under, if any. */
|
|
1334
|
+
groupId?: string;
|
|
1203
1335
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1336
|
+
/**
|
|
1337
|
+
* Runtime information threaded into `setup(ctx, info)`. Adapters
|
|
1338
|
+
* populate the fields that make sense for their deployment. In
|
|
1339
|
+
* particular, `createCac` fills `flags` with the parsed CAC bag.
|
|
1340
|
+
*/
|
|
1341
|
+
interface DevframeSetupInfo {
|
|
1342
|
+
/** Parsed CLI flags, populated by the CLI adapter. */
|
|
1343
|
+
flags?: Record<string, unknown>;
|
|
1210
1344
|
}
|
|
1211
|
-
|
|
1345
|
+
interface DevframeDefinition {
|
|
1346
|
+
id: string;
|
|
1347
|
+
name: string;
|
|
1348
|
+
/** Semver of the tool, surfaced in hub UIs and diagnostics. */
|
|
1349
|
+
version: string;
|
|
1350
|
+
/** npm package name the devframe ships in (e.g. `@scope/my-tool`). */
|
|
1351
|
+
packageName: string;
|
|
1212
1352
|
/**
|
|
1213
|
-
*
|
|
1353
|
+
* `import.meta.url` of the module that defines this devframe. **Always
|
|
1354
|
+
* provide it** (`importMetaUrl: import.meta.url`): it is the resolution base
|
|
1355
|
+
* for the tool's own dependency graph, which lets the host resolve
|
|
1356
|
+
* everything against the plugin's own installed packages rather than the
|
|
1357
|
+
* consuming app's.
|
|
1358
|
+
*
|
|
1359
|
+
* - **Remote assets** — becomes the default `resolveFrom` for any remote
|
|
1360
|
+
* {@link StaticAssetsSource} the devframe hosts (its `cli.distDir`, and
|
|
1361
|
+
* every `ctx.views.hostStatic` call) that doesn't set one explicitly, so a
|
|
1362
|
+
* locally installed copy of the assets package is served with zero
|
|
1363
|
+
* network. A per-asset `resolveFrom` still wins, and an explicit
|
|
1364
|
+
* `resolveFrom: null` still opts out.
|
|
1365
|
+
* - **Service dependencies** — becomes the base the host resolves declared
|
|
1366
|
+
* {@link DevframeServiceInput | services} from, so a plugin can ship a
|
|
1367
|
+
* service package as its own dependency instead of asking users to install
|
|
1368
|
+
* it.
|
|
1369
|
+
*
|
|
1370
|
+
* Optional for backward compatibility; omitting it falls back to
|
|
1371
|
+
* runtime-directory resolution and disables the zero-network installed-copy
|
|
1372
|
+
* fast paths above.
|
|
1373
|
+
*/
|
|
1374
|
+
importMetaUrl?: string;
|
|
1375
|
+
/** Project homepage or documentation URL. */
|
|
1376
|
+
homepage: string;
|
|
1377
|
+
/** One-line summary of what the tool does. */
|
|
1378
|
+
description: string;
|
|
1379
|
+
icon?: string | {
|
|
1380
|
+
light: string;
|
|
1381
|
+
dark: string;
|
|
1382
|
+
};
|
|
1383
|
+
/**
|
|
1384
|
+
* Default dock attributes applied when a hub mounts this devframe as an
|
|
1385
|
+
* iframe dock entry. Consulted only by the hub install path (`ctx.install`),
|
|
1386
|
+
* which merge these beneath the per-mount `dock` overrides; standalone
|
|
1387
|
+
* adapters (`cli` / `build`) ignore it.
|
|
1214
1388
|
*
|
|
1215
|
-
*
|
|
1389
|
+
* @see {@link DevframeDockDefaults}
|
|
1216
1390
|
*/
|
|
1217
|
-
|
|
1391
|
+
dock?: DevframeDockDefaults;
|
|
1218
1392
|
/**
|
|
1219
|
-
*
|
|
1393
|
+
* Mount path override. Defaults depend on the adapter:
|
|
1394
|
+
* `/` for standalone (`cli` / `build`), `/__<id>/` for hosted
|
|
1395
|
+
* (`vite` / `embedded`).
|
|
1220
1396
|
*/
|
|
1221
|
-
|
|
1397
|
+
basePath?: string;
|
|
1222
1398
|
/**
|
|
1223
|
-
*
|
|
1399
|
+
* How a hub reacts when another devframe sharing this one's `id` is
|
|
1400
|
+
* mounted onto the same hub. Consulted only by hub adapters
|
|
1401
|
+
* (`ctx.install`); standalone adapters (`cli` / `build`)
|
|
1402
|
+
* ignore it.
|
|
1224
1403
|
*
|
|
1225
|
-
*
|
|
1404
|
+
* @default 'warn'
|
|
1226
1405
|
*/
|
|
1227
|
-
|
|
1406
|
+
duplicationStrategy?: DevframeDuplicationStrategy;
|
|
1228
1407
|
/**
|
|
1229
|
-
*
|
|
1408
|
+
* Declares which runtimes meaningfully support this devframe. Adapters
|
|
1409
|
+
* act on a `false` before doing any work:
|
|
1410
|
+
*
|
|
1411
|
+
* - `build: false` — `createCac` skips registering the `build` subcommand,
|
|
1412
|
+
* and `createBuild` refuses (throws `DF0042`) unless `{ force: true }`.
|
|
1413
|
+
* Useful for a devframe whose value is inherently live (e.g. it manages
|
|
1414
|
+
* real files on disk), so a static export would only ever produce a
|
|
1415
|
+
* broken, write-less shell of the tool.
|
|
1416
|
+
* - `dev: false` — `createDevServer` refuses (throws `DF0058`) unless
|
|
1417
|
+
* `{ force: true }`. Useful for a devframe that only makes sense as a
|
|
1418
|
+
* static export (e.g. a report generator with nothing to serve live).
|
|
1230
1419
|
*/
|
|
1231
|
-
|
|
1420
|
+
capabilities?: {
|
|
1421
|
+
dev?: boolean;
|
|
1422
|
+
build?: boolean;
|
|
1423
|
+
};
|
|
1232
1424
|
/**
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1425
|
+
* Wire services this devframe consumes (see `DevframeServiceDefinition`).
|
|
1426
|
+
* Each entry is either a declarative descriptor
|
|
1427
|
+
* (`{ package, version?, required?, options? }` — the host imports the
|
|
1428
|
+
* package's default-export factory, resolving it against **this plugin's
|
|
1429
|
+
* own dependencies**) or a ready `DevframeServiceDefinition` (the factory
|
|
1430
|
+
* was already called). The adapter queues these before `setup(ctx)` runs
|
|
1431
|
+
* and constructs each service once at the `ctx.services.ready()` barrier,
|
|
1432
|
+
* merging option sets across every declarer. Missing services are skipped
|
|
1433
|
+
* unless marked `required` — clients feature-detect via
|
|
1434
|
+
* `client.services.has(pkg)` and degrade.
|
|
1237
1435
|
*/
|
|
1238
|
-
|
|
1436
|
+
services?: DevframeServiceInput[];
|
|
1437
|
+
/** RPC-level configuration for this devframe (see {@link DevframeRpcOptions}). */
|
|
1438
|
+
rpc?: DevframeRpcOptions;
|
|
1439
|
+
/** Server-side setup — the primary entrypoint. Runs in every runtime. */
|
|
1440
|
+
setup: (ctx: DevframeNodeContext, info?: DevframeSetupInfo) => void | Promise<void>;
|
|
1441
|
+
cli?: DevframeCliOptions;
|
|
1442
|
+
}
|
|
1443
|
+
interface DevframeRpcOptions {
|
|
1444
|
+
/**
|
|
1445
|
+
* Opt an RPC function into the static-build snapshot **without owning its
|
|
1446
|
+
* definition** — the mechanism a devframe uses to bake a wire service's
|
|
1447
|
+
* RPC (e.g. `@devframes/service-git`'s `status`/`log`/`show`) into its
|
|
1448
|
+
* `build` export, since the service itself defines no `dump`/`snapshot`.
|
|
1449
|
+
*
|
|
1450
|
+
* Each entry is either a bare method id (bakes the no-argument call, like
|
|
1451
|
+
* `snapshot: true`) or `{ method, inputs }` where `inputs` is the list of
|
|
1452
|
+
* argument-tuples to bake — or an async provider given the node context
|
|
1453
|
+
* (so it can enumerate at build time, e.g. read commit hashes via the
|
|
1454
|
+
* service's node API). `createBuild` resolves these after setup and
|
|
1455
|
+
* executes the target's own handler per tuple; the first tuple's result
|
|
1456
|
+
* becomes the fallback so any call variant resolves to a baked value.
|
|
1457
|
+
*/
|
|
1458
|
+
snapshot?: DevframeSnapshotRpcEntry[];
|
|
1459
|
+
}
|
|
1460
|
+
/** Argument-tuples to bake for a {@link DevframeSnapshotRpcEntry}, or a provider that computes them at build time. */
|
|
1461
|
+
type DevframeSnapshotRpcInputs = readonly (readonly unknown[])[] | ((ctx: DevframeNodeContext) => readonly (readonly unknown[])[] | Promise<readonly (readonly unknown[])[]>);
|
|
1462
|
+
/**
|
|
1463
|
+
* One {@link DevframeRpcOptions.snapshot} entry: a bare method id (bakes
|
|
1464
|
+
* the no-argument call) or a method plus the argument-tuples to bake.
|
|
1465
|
+
*/
|
|
1466
|
+
type DevframeSnapshotRpcEntry = string | {
|
|
1467
|
+
method: string;
|
|
1468
|
+
inputs: DevframeSnapshotRpcInputs;
|
|
1239
1469
|
};
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1470
|
+
//#endregion
|
|
1471
|
+
//#region src/utils/shared-state.d.ts
|
|
1472
|
+
type ImmutablePrimitive = undefined | null | boolean | string | number | Function;
|
|
1473
|
+
type Immutable<T> = T extends ImmutablePrimitive ? T : T extends Array<infer U> ? ImmutableArray<U> : T extends Map<infer K, infer V> ? ImmutableMap<K, V> : T extends Set<infer M> ? ImmutableSet<M> : ImmutableObject<T>;
|
|
1474
|
+
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
|
1475
|
+
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
|
|
1476
|
+
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
|
|
1477
|
+
type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]>; };
|
|
1478
|
+
/**
|
|
1479
|
+
* Serializable patch describing a single mutation to a `SharedState`.
|
|
1480
|
+
* Structurally compatible with JSON-Patch and is safe to send over RPC.
|
|
1481
|
+
*/
|
|
1482
|
+
interface SharedStatePatch {
|
|
1483
|
+
op: 'add' | 'remove' | 'replace';
|
|
1484
|
+
path: readonly (string | number)[];
|
|
1485
|
+
value?: unknown;
|
|
1243
1486
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1487
|
+
/**
|
|
1488
|
+
* State host that is immutable by default with explicit mutate.
|
|
1489
|
+
*/
|
|
1490
|
+
interface SharedState<T> {
|
|
1247
1491
|
/**
|
|
1248
|
-
*
|
|
1249
|
-
* `get(key, ...)` creates a fresh entry (not on subsequent gets).
|
|
1250
|
-
* Useful for protocol adapters (e.g. MCP) that surface shared state
|
|
1251
|
-
* as dynamic resources.
|
|
1492
|
+
* Get the current state. Immutable.
|
|
1252
1493
|
*/
|
|
1253
|
-
|
|
1494
|
+
value: () => Immutable<T>;
|
|
1254
1495
|
/**
|
|
1255
|
-
*
|
|
1256
|
-
* `true` when a state was removed, `false` when the key was unknown.
|
|
1257
|
-
* Used by short-lived states (e.g. a disposed JSON-render view) to avoid
|
|
1258
|
-
* leaking listeners and lingering entries for the context lifetime.
|
|
1496
|
+
* Subscribe to state changes.
|
|
1259
1497
|
*/
|
|
1260
|
-
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Options for `RpcStreamingHost.create()`.
|
|
1264
|
-
*/
|
|
1265
|
-
interface RpcStreamingChannelOptions {
|
|
1498
|
+
on: EventEmitter<SharedStateEvents<T>>['on'];
|
|
1266
1499
|
/**
|
|
1267
|
-
*
|
|
1268
|
-
* replay-on-resubscribe. `0` (default) disables replay; on reconnect
|
|
1269
|
-
* the consumer only sees chunks that arrive after subscribing.
|
|
1270
|
-
*
|
|
1271
|
-
* The buffer is per stream id, not per channel — each `channel.start()`
|
|
1272
|
-
* gets its own.
|
|
1500
|
+
* Mutate the state.
|
|
1273
1501
|
*/
|
|
1274
|
-
|
|
1502
|
+
mutate: (fn: (state: T) => void, syncId?: string) => void;
|
|
1275
1503
|
/**
|
|
1276
|
-
*
|
|
1277
|
-
* last subscriber leaves (or if no subscriber ever arrived). During
|
|
1278
|
-
* this window, late subscribers can still join and replay the buffer
|
|
1279
|
-
* + receive the `end` frame.
|
|
1280
|
-
*
|
|
1281
|
-
* Defaults to `30_000` (30 s) when `replayWindow > 0`, else `0`
|
|
1282
|
-
* (immediate free). Set to `0` to opt out, or higher for longer
|
|
1283
|
-
* post-mortem replay.
|
|
1504
|
+
* Apply patches to the state.
|
|
1284
1505
|
*/
|
|
1285
|
-
|
|
1506
|
+
patch: (patches: SharedStatePatch[], syncId?: string) => void;
|
|
1507
|
+
/**
|
|
1508
|
+
* Sync IDs that have been applied to the state.
|
|
1509
|
+
*/
|
|
1510
|
+
syncIds: Set<string>;
|
|
1286
1511
|
}
|
|
1287
|
-
|
|
1512
|
+
interface SharedStateEvents<T> {
|
|
1513
|
+
updated: (fullState: T, patches: SharedStatePatch[] | undefined, syncId: string) => void;
|
|
1514
|
+
}
|
|
1515
|
+
interface SharedStateOptions<T> {
|
|
1516
|
+
/**
|
|
1517
|
+
* Initial state.
|
|
1518
|
+
*/
|
|
1519
|
+
initialValue: T;
|
|
1520
|
+
/**
|
|
1521
|
+
* Enable patches.
|
|
1522
|
+
*
|
|
1523
|
+
* @default false
|
|
1524
|
+
*/
|
|
1525
|
+
enablePatches?: boolean;
|
|
1526
|
+
}
|
|
1527
|
+
declare function createSharedState<T extends object>(options: SharedStateOptions<T>): SharedState<T>;
|
|
1528
|
+
//#endregion
|
|
1529
|
+
//#region src/utils/streaming-channel.d.ts
|
|
1530
|
+
/**
|
|
1531
|
+
* Serialized error shape sent over the wire when a stream ends with a failure.
|
|
1532
|
+
* Stays JSON-safe so the strict-JSON encoder can carry it without coercion.
|
|
1533
|
+
*/
|
|
1534
|
+
interface StreamErrorPayload {
|
|
1535
|
+
name: string;
|
|
1536
|
+
message: string;
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Single buffered chunk in the server-side ring buffer.
|
|
1540
|
+
*
|
|
1541
|
+
* Sequence numbers start at 1 and increment per write. Subscribers track
|
|
1542
|
+
* `lastSeenSeq` and ask for `afterSeq` on resubscribe so the server can
|
|
1543
|
+
* replay any chunks the client missed during a brief disconnect.
|
|
1544
|
+
*/
|
|
1545
|
+
interface BufferedChunk<T> {
|
|
1546
|
+
seq: number;
|
|
1547
|
+
chunk: T;
|
|
1548
|
+
}
|
|
1549
|
+
interface StreamSinkEvents<T> {
|
|
1550
|
+
/** Fired for each `write()`. The RPC layer subscribes and broadcasts. */
|
|
1551
|
+
chunk: (seq: number, chunk: T) => void;
|
|
1552
|
+
/** Terminal — fired exactly once per sink lifetime. */
|
|
1553
|
+
end: (error?: StreamErrorPayload) => void;
|
|
1554
|
+
}
|
|
1555
|
+
interface CreateStreamSinkOptions {
|
|
1556
|
+
id?: string;
|
|
1557
|
+
/**
|
|
1558
|
+
* Size of the per-stream ring buffer kept for replay-on-resubscribe.
|
|
1559
|
+
* `0` (default) disables replay.
|
|
1560
|
+
*/
|
|
1561
|
+
replayWindow?: number;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Server-side producer handle. Two equivalent surfaces share one piece of
|
|
1565
|
+
* state: the imperative `write/error/close` triple, and a `WritableStream<T>`
|
|
1566
|
+
* for `pipeTo`-style consumption.
|
|
1567
|
+
*/
|
|
1568
|
+
interface StreamSink<T> {
|
|
1569
|
+
/** Stable id used by clients to subscribe. */
|
|
1570
|
+
readonly id: string;
|
|
1571
|
+
/**
|
|
1572
|
+
* Aborts when the consumer cancels (server-side) or when the transport
|
|
1573
|
+
* loses every subscriber. Producers should poll `signal.aborted` and exit
|
|
1574
|
+
* cleanly.
|
|
1575
|
+
*/
|
|
1576
|
+
readonly signal: AbortSignal;
|
|
1577
|
+
/** `true` after `close()` / `error()`. Further writes throw. */
|
|
1578
|
+
readonly closed: boolean;
|
|
1579
|
+
/** Last allocated sequence number. `0` until the first write. */
|
|
1580
|
+
readonly lastSeq: number;
|
|
1581
|
+
write: (chunk: T) => void;
|
|
1582
|
+
error: (reason: unknown) => void;
|
|
1583
|
+
close: () => void;
|
|
1584
|
+
/** External-cancel path. Aborts the signal so handlers can short-circuit. */
|
|
1585
|
+
abort: (reason?: unknown) => void;
|
|
1586
|
+
/** `WritableStream<T>` adapter — same in-memory state as the imperative API. */
|
|
1587
|
+
readonly writable: WritableStream<T>;
|
|
1588
|
+
/**
|
|
1589
|
+
* Internal — RPC layer subscribes to receive chunk/end notifications.
|
|
1590
|
+
* Not part of the public contract; do not call directly.
|
|
1591
|
+
*
|
|
1592
|
+
* @internal
|
|
1593
|
+
*/
|
|
1594
|
+
readonly events: EventEmitter<StreamSinkEvents<T>>;
|
|
1595
|
+
/**
|
|
1596
|
+
* Internal — replay buffer. RPC layer reads on (re)subscribe to feed
|
|
1597
|
+
* missed chunks before going live.
|
|
1598
|
+
*
|
|
1599
|
+
* @internal
|
|
1600
|
+
*/
|
|
1601
|
+
readonly buffer: ReadonlyArray<BufferedChunk<T>>;
|
|
1602
|
+
}
|
|
1603
|
+
interface CreateStreamReaderOptions {
|
|
1604
|
+
id?: string;
|
|
1605
|
+
/**
|
|
1606
|
+
* Maximum number of buffered chunks held client-side while the consumer
|
|
1607
|
+
* isn't draining. On overflow, the oldest chunk is dropped.
|
|
1608
|
+
*/
|
|
1609
|
+
highWaterMark?: number;
|
|
1610
|
+
/**
|
|
1611
|
+
* Called when the chunk queue overflows the high-water mark. The RPC
|
|
1612
|
+
* layer wires this to a coded warning; the primitive itself is
|
|
1613
|
+
* RPC-agnostic.
|
|
1614
|
+
*/
|
|
1615
|
+
onOverflow?: (dropped: number) => void;
|
|
1616
|
+
/** Called when the consumer cancels — the RPC layer sends `:cancel` upstream. */
|
|
1617
|
+
onCancel?: () => void;
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Client-side consumer handle. Both an `AsyncIterable<T>` (for `for await`)
|
|
1621
|
+
* and exposes `readable: ReadableStream<T>` (for `pipeTo`). Pick one — they
|
|
1622
|
+
* share a single internal queue, so concurrent draining will race.
|
|
1623
|
+
*/
|
|
1624
|
+
interface StreamReader<T> extends AsyncIterable<T> {
|
|
1625
|
+
readonly id: string;
|
|
1626
|
+
readonly cancelled: boolean;
|
|
1627
|
+
readonly done: boolean;
|
|
1628
|
+
/** Highest `seq` observed. Used for replay on reconnect. */
|
|
1629
|
+
readonly lastSeenSeq: number;
|
|
1630
|
+
/** `ReadableStream<T>` adapter for `pipeTo`-style consumption. */
|
|
1631
|
+
readonly readable: ReadableStream<T>;
|
|
1632
|
+
cancel: () => void;
|
|
1633
|
+
/** @internal */
|
|
1634
|
+
_push: (seq: number, chunk: T) => void;
|
|
1635
|
+
/** @internal */
|
|
1636
|
+
_end: (error?: StreamErrorPayload) => void;
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Build a server-side stream sink. RPC-agnostic — the RPC host wires
|
|
1640
|
+
* `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
|
|
1641
|
+
* for late or reconnecting subscribers.
|
|
1642
|
+
*/
|
|
1643
|
+
declare function createStreamSink<T>(options?: CreateStreamSinkOptions): StreamSink<T>;
|
|
1644
|
+
/**
|
|
1645
|
+
* Build a client-side stream reader. RPC-agnostic — the RPC host calls
|
|
1646
|
+
* `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
|
|
1647
|
+
* terminal frame. Consumers iterate with `for await` or pipe `readable`.
|
|
1648
|
+
*/
|
|
1649
|
+
declare function createStreamReader<T>(options?: CreateStreamReaderOptions): StreamReader<T>;
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region src/types/rpc.d.ts
|
|
1652
|
+
interface DevframeNodeRpcSession {
|
|
1653
|
+
meta: DevframeNodeRpcSessionMeta;
|
|
1654
|
+
rpc: BirpcReturn<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>;
|
|
1655
|
+
}
|
|
1656
|
+
interface RpcBroadcastOptions<METHOD, Args extends any[]> {
|
|
1657
|
+
method: METHOD;
|
|
1658
|
+
args: Args;
|
|
1659
|
+
optional?: boolean;
|
|
1660
|
+
event?: boolean;
|
|
1661
|
+
filter?: (client: BirpcReturn<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>) => boolean | void;
|
|
1662
|
+
}
|
|
1663
|
+
type RpcFunctionsHost = RpcFunctionsCollectorBase<DevframeRpcServerFunctions, DevframeNodeContext> & {
|
|
1664
|
+
/**
|
|
1665
|
+
* Invoke a locally registered server RPC function directly.
|
|
1666
|
+
*
|
|
1667
|
+
* This bypasses transport and is useful for server-side cross-function calls.
|
|
1668
|
+
*/
|
|
1669
|
+
invokeLocal: <T extends keyof DevframeRpcServerFunctions, Args extends Parameters<DevframeRpcServerFunctions[T]>>(method: T, ...args: Args) => Promise<Awaited<ReturnType<DevframeRpcServerFunctions[T]>>>;
|
|
1670
|
+
/**
|
|
1671
|
+
* Broadcast a message to all connected clients
|
|
1672
|
+
*/
|
|
1673
|
+
broadcast: <T extends keyof DevframeRpcClientFunctions, Args extends Parameters<DevframeRpcClientFunctions[T]>>(options: RpcBroadcastOptions<T, Args>) => Promise<void>;
|
|
1674
|
+
/**
|
|
1675
|
+
* Get the current RPC client
|
|
1676
|
+
*
|
|
1677
|
+
* Available in RPC functions to get the current RPC client
|
|
1678
|
+
*/
|
|
1679
|
+
getCurrentRpcSession: () => DevframeNodeRpcSession | undefined;
|
|
1680
|
+
/**
|
|
1681
|
+
* The shared state host
|
|
1682
|
+
*/
|
|
1683
|
+
sharedState: RpcSharedStateHost;
|
|
1684
|
+
/**
|
|
1685
|
+
* The streaming channel host. Provides per-channel `start()` /
|
|
1686
|
+
* `pipeFrom()` producers; clients consume via `rpc.streaming.subscribe()`.
|
|
1687
|
+
*
|
|
1688
|
+
* @see RpcStreamingHost
|
|
1689
|
+
*/
|
|
1690
|
+
streaming: RpcStreamingHost;
|
|
1691
|
+
};
|
|
1692
|
+
interface RpcSharedStateGetOptions<T> {
|
|
1693
|
+
sharedState?: SharedState<T>;
|
|
1694
|
+
initialValue?: T;
|
|
1695
|
+
}
|
|
1696
|
+
interface RpcSharedStateHost {
|
|
1697
|
+
get: <T extends object = any>(key: string, options?: RpcSharedStateGetOptions<T>) => Promise<SharedState<T>>;
|
|
1698
|
+
keys: () => string[];
|
|
1699
|
+
/**
|
|
1700
|
+
* Subscribe to new shared-state keys becoming available. Fires when
|
|
1701
|
+
* `get(key, ...)` creates a fresh entry (not on subsequent gets).
|
|
1702
|
+
* Useful for protocol adapters (e.g. MCP) that surface shared state
|
|
1703
|
+
* as dynamic resources.
|
|
1704
|
+
*/
|
|
1705
|
+
onKeyAdded: (fn: (key: string) => void) => () => void;
|
|
1706
|
+
/**
|
|
1707
|
+
* Unregister a shared state and drop its broadcast listeners. Returns
|
|
1708
|
+
* `true` when a state was removed, `false` when the key was unknown.
|
|
1709
|
+
* Used by short-lived states (e.g. a disposed JSON-render view) to avoid
|
|
1710
|
+
* leaking listeners and lingering entries for the context lifetime.
|
|
1711
|
+
*/
|
|
1712
|
+
delete: (key: string) => boolean;
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Options for `RpcStreamingHost.create()`.
|
|
1716
|
+
*/
|
|
1717
|
+
interface RpcStreamingChannelOptions {
|
|
1718
|
+
/**
|
|
1719
|
+
* Size of the per-stream ring buffer kept on the server for
|
|
1720
|
+
* replay-on-resubscribe. `0` (default) disables replay; on reconnect
|
|
1721
|
+
* the consumer only sees chunks that arrive after subscribing.
|
|
1722
|
+
*
|
|
1723
|
+
* The buffer is per stream id, not per channel — each `channel.start()`
|
|
1724
|
+
* gets its own.
|
|
1725
|
+
*/
|
|
1726
|
+
replayWindow?: number;
|
|
1727
|
+
/**
|
|
1728
|
+
* Milliseconds a closed stream is retained on the server after its
|
|
1729
|
+
* last subscriber leaves (or if no subscriber ever arrived). During
|
|
1730
|
+
* this window, late subscribers can still join and replay the buffer
|
|
1731
|
+
* + receive the `end` frame.
|
|
1732
|
+
*
|
|
1733
|
+
* Defaults to `30_000` (30 s) when `replayWindow > 0`, else `0`
|
|
1734
|
+
* (immediate free). Set to `0` to opt out, or higher for longer
|
|
1735
|
+
* post-mortem replay.
|
|
1736
|
+
*/
|
|
1737
|
+
closedStreamRetention?: number;
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1288
1740
|
* Channel handle returned by `ctx.rpc.streaming.create(name, opts)`. A
|
|
1289
1741
|
* channel owns a wire namespace; calling `start()` produces individual
|
|
1290
1742
|
* streams keyed by id.
|
|
@@ -1610,393 +2062,4 @@ interface ConnectionMeta {
|
|
|
1610
2062
|
*/
|
|
1611
2063
|
interface DevframeConnectionConfigsRegistry {}
|
|
1612
2064
|
//#endregion
|
|
1613
|
-
|
|
1614
|
-
/**
|
|
1615
|
-
* A ready-made pre-auth RPC handler, as produced by
|
|
1616
|
-
* `devframe/recipes/interactive-auth`'s `createInteractiveAuth`. Bundles
|
|
1617
|
-
* everything a host adapter needs to wire an authenticated server:
|
|
1618
|
-
* the handshake RPC functions, the resolver gate, the connect-time trust
|
|
1619
|
-
* hook, and the startup banner.
|
|
1620
|
-
*
|
|
1621
|
-
* `initDevframe` / `initHub` accept one of these directly via their `auth` option —
|
|
1622
|
-
* see {@link https://devfra.me | devframe}'s server docs — or a host can
|
|
1623
|
-
* wire the four pieces itself against a lower-level transport.
|
|
1624
|
-
*/
|
|
1625
|
-
interface DevframeAuthHandler {
|
|
1626
|
-
/**
|
|
1627
|
-
* `anonymous:devframe:auth` + `anonymous:devframe:auth:exchange` (the
|
|
1628
|
-
* handshake) and `devframe:auth:revoke` (self-revoke) — register these on
|
|
1629
|
-
* the RPC host (e.g. `rpcHost.register(fn)` for each).
|
|
1630
|
-
*/
|
|
1631
|
-
rpcFunctions: RpcFunctionDefinitionAny[];
|
|
1632
|
-
/**
|
|
1633
|
-
* Resolver gate: whether `methodName` is callable given `session`'s
|
|
1634
|
-
* current trust state. Defaults to allowing any `anonymous:`-prefixed
|
|
1635
|
-
* method (see `isAnonymousRpcMethod`) plus anything once the session is
|
|
1636
|
-
* trusted.
|
|
1637
|
-
*/
|
|
1638
|
-
authorize: (methodName: string, session: DevframeNodeRpcSession) => boolean;
|
|
1639
|
-
/**
|
|
1640
|
-
* Connect-time trust: reads a bearer token off the connection's initial
|
|
1641
|
-
* request (a static/pre-shared token from `clientAuthTokens`, or a token
|
|
1642
|
-
* minted by the code exchange) and, when valid, marks the session
|
|
1643
|
-
* trusted immediately — before the client's own handshake call.
|
|
1644
|
-
*/
|
|
1645
|
-
onConnect: (connection: DevframeRpcConnection, session: DevframeNodeRpcSession) => void;
|
|
1646
|
-
/**
|
|
1647
|
-
* Print the current one-time code and its magic-link URL. Devframe stays
|
|
1648
|
-
* headless — call this yourself once the server is listening. Safe to
|
|
1649
|
-
* call repeatedly; it only prints once per code.
|
|
1650
|
-
*/
|
|
1651
|
-
printBanner: () => void;
|
|
1652
|
-
/**
|
|
1653
|
-
* Rewrite a URL the CLI is about to open in the browser (`--open`) so the
|
|
1654
|
-
* tab lands already authenticated — e.g. appending the current OTP as a
|
|
1655
|
-
* query param. Called with the fully-resolved target URL; return it
|
|
1656
|
-
* unchanged to opt out. Optional: a handler that doesn't need this (e.g.
|
|
1657
|
-
* one gated by a pre-shared bearer token instead of an interactive code)
|
|
1658
|
-
* can omit it and the URL opens as-is.
|
|
1659
|
-
*/
|
|
1660
|
-
buildOpenUrl?: (url: string) => string;
|
|
1661
|
-
}
|
|
1662
|
-
//#endregion
|
|
1663
|
-
//#region src/types/devframe.d.ts
|
|
1664
|
-
/**
|
|
1665
|
-
* Classification of how a devframe is being deployed. Hosted adapters
|
|
1666
|
-
* (`vite`, `embedded`) share their origin with a host app and must
|
|
1667
|
-
* namespace their mount path under `/__<id>/`. Standalone adapters
|
|
1668
|
-
* (`cli`, `build`) own the origin and default to `/`.
|
|
1669
|
-
*/
|
|
1670
|
-
type DevframeDeploymentKind = 'standalone' | 'hosted';
|
|
1671
|
-
/**
|
|
1672
|
-
* How a hub deduplicates devframes that share an `id` when more than one
|
|
1673
|
-
* is mounted onto the same hub. See {@link DevframeDefinition.duplicationStrategy}.
|
|
1674
|
-
*
|
|
1675
|
-
* - `'warn'` (default) — keep the first registration, drop later
|
|
1676
|
-
* duplicates, and emit a warning diagnostic (`DF8105`).
|
|
1677
|
-
* - `'silent'` — drop later duplicates without warning.
|
|
1678
|
-
* - `'throw'` — throw when a duplicate is mounted.
|
|
1679
|
-
* - `'duplicate'` — let every instance coexist under a disambiguated
|
|
1680
|
-
* dock id.
|
|
1681
|
-
*/
|
|
1682
|
-
type DevframeDuplicationStrategy = 'warn' | 'silent' | 'throw' | 'duplicate';
|
|
1683
|
-
/**
|
|
1684
|
-
* Controls where the browser opens the RPC WebSocket — advertised in
|
|
1685
|
-
* `__connection.json` and used to bind the dev server. The three shapes map
|
|
1686
|
-
* to the three connection scenarios; precedence is `url` > `port` > `route`:
|
|
1687
|
-
*
|
|
1688
|
-
* 1. **Same server, different route** (default) — leave `port`/`url` unset.
|
|
1689
|
-
* The socket shares the HTTP server's port and binds to `route`
|
|
1690
|
-
* (`__ws`). The client connects to its own origin, so the link
|
|
1691
|
-
* survives a reverse proxy that rewrites the host/port/subpath.
|
|
1692
|
-
*
|
|
1693
|
-
* 2. **Different port** — set `port`. The socket binds on its own port on the
|
|
1694
|
-
* same host; the client targets `ws(s)://<page-host>:<port>/<route>`.
|
|
1695
|
-
*
|
|
1696
|
-
* 3. **Remote, different origin** — set `url` to a full `ws://`/`wss://`
|
|
1697
|
-
* endpoint (e.g. a public tunnel or relay). The client uses it verbatim.
|
|
1698
|
-
*/
|
|
1699
|
-
interface DevframeWsOptions {
|
|
1700
|
-
/**
|
|
1701
|
-
* Upgrade route segment the socket binds to and is advertised at, relative
|
|
1702
|
-
* to the SPA base. Default: `__ws`.
|
|
1703
|
-
*/
|
|
1704
|
-
route?: string;
|
|
1705
|
-
/**
|
|
1706
|
-
* Bind the socket on its own port instead of sharing the HTTP port. The
|
|
1707
|
-
* browser connects to this port on the page's hostname. Implies
|
|
1708
|
-
* {@link DevframeWsOptions.sidecar}.
|
|
1709
|
-
*/
|
|
1710
|
-
port?: number;
|
|
1711
|
-
/**
|
|
1712
|
-
* Start a side-car WebSocket server on a free port, for hosts whose request
|
|
1713
|
-
* handlers can't accept upgrades (Next.js route handlers, Nitro, Rsbuild).
|
|
1714
|
-
* The resolved port is advertised in `__connection.json`, so the browser
|
|
1715
|
-
* finds it without any further wiring. Set `port` instead to pin it.
|
|
1716
|
-
*/
|
|
1717
|
-
sidecar?: boolean;
|
|
1718
|
-
/**
|
|
1719
|
-
* Advertise a fixed, fully-qualified endpoint on another origin (a full
|
|
1720
|
-
* `ws://`/`wss://` URL). Takes precedence over `port`/`route` in the meta.
|
|
1721
|
-
*/
|
|
1722
|
-
url?: string;
|
|
1723
|
-
}
|
|
1724
|
-
/**
|
|
1725
|
-
* Controls the SSE RPC endpoint — the WebSocket-free transport for hosts
|
|
1726
|
-
* and proxies where the upgrade isn't available. It rides whatever HTTP
|
|
1727
|
-
* surface serves the instance (the same one serving `__connection.json`),
|
|
1728
|
-
* so a relative `route` always resolves; there is no port plumbing of its
|
|
1729
|
-
* own. Enabled by default alongside the WebSocket; pass `sse: false` to
|
|
1730
|
-
* disable, or `ws: false` to run SSE-only (`backend: 'sse'`).
|
|
1731
|
-
*/
|
|
1732
|
-
interface DevframeSseOptions {
|
|
1733
|
-
/**
|
|
1734
|
-
* Route segment the SSE endpoint binds to and is advertised at, relative
|
|
1735
|
-
* to the SPA base. Default: `__sse`.
|
|
1736
|
-
*/
|
|
1737
|
-
route?: string;
|
|
1738
|
-
}
|
|
1739
|
-
/**
|
|
1740
|
-
* Configuration for the route-based MCP server mounted alongside the dev
|
|
1741
|
-
* server (opt-in via {@link DevframeCliOptions.mcp}). The endpoint speaks
|
|
1742
|
-
* the MCP Streamable-HTTP transport over the same origin as the SPA,
|
|
1743
|
-
* exposing the definition's `ctx.agent` tools + shared-state resources to
|
|
1744
|
-
* external MCP clients connected to the *running* server.
|
|
1745
|
-
*/
|
|
1746
|
-
interface McpRouteOptions {
|
|
1747
|
-
/**
|
|
1748
|
-
* Route segment the MCP endpoint binds to, relative to the SPA base.
|
|
1749
|
-
* Default: `__mcp` (i.e. `/__mcp` standalone, `/__<id>/__mcp` hosted).
|
|
1750
|
-
*/
|
|
1751
|
-
path?: string;
|
|
1752
|
-
/**
|
|
1753
|
-
* Extra `Origin` header values to accept beyond the loopback default
|
|
1754
|
-
* (`localhost`/`127.0.0.1`/`::1` and any `Origin`-less native client).
|
|
1755
|
-
* Add your LAN/tunnel origin here when reaching the endpoint from another
|
|
1756
|
-
* host, mirroring the WS transport's origin gate. Pass `false` to disable
|
|
1757
|
-
* origin checking entirely (not recommended). Default: loopback-only.
|
|
1758
|
-
*
|
|
1759
|
-
* This is the endpoint's DNS-rebinding protection — the shared
|
|
1760
|
-
* `isAllowedOrigin` gate the WS upgrade already uses, applied as external
|
|
1761
|
-
* middleware (the approach the MCP SDK now recommends over its own
|
|
1762
|
-
* deprecated `allowedHosts`/`allowedOrigins` transport flags).
|
|
1763
|
-
*/
|
|
1764
|
-
allowedOrigins?: readonly string[] | false;
|
|
1765
|
-
}
|
|
1766
|
-
interface DevframeCliOptions {
|
|
1767
|
-
/** Binary name; default: the devframe's `id`. */
|
|
1768
|
-
command?: string;
|
|
1769
|
-
/** Preferred port for the dev server (default 9999). */
|
|
1770
|
-
port?: number;
|
|
1771
|
-
/** Port scan range, forwarded to `get-port-please`. */
|
|
1772
|
-
portRange?: [number, number];
|
|
1773
|
-
/** Prefer a random open port. */
|
|
1774
|
-
random?: boolean;
|
|
1775
|
-
/** Default host to bind to; `--host` overrides. */
|
|
1776
|
-
host?: string;
|
|
1777
|
-
/**
|
|
1778
|
-
* Auto-open the browser when the dev server starts.
|
|
1779
|
-
* `true` opens the resolved origin; a string opens that relative path.
|
|
1780
|
-
* The `--open` / `--no-open` flags override this.
|
|
1781
|
-
*/
|
|
1782
|
-
open?: boolean | string;
|
|
1783
|
-
/**
|
|
1784
|
-
* Authentication for the standalone dev server.
|
|
1785
|
-
*
|
|
1786
|
-
* - `undefined` / `true` — the standalone adapters (`cli` /
|
|
1787
|
-
* served `build`) auto-wire devframe's interactive OTP auth
|
|
1788
|
-
* (`createInteractiveAuth`): an untrusted client can only reach
|
|
1789
|
-
* `anonymous:` methods until it exchanges the printed one-time code.
|
|
1790
|
-
* The adapter prints the code + magic-link banner once the server is
|
|
1791
|
-
* listening.
|
|
1792
|
-
* - `false` — no gate, for trusted single-user localhost tools where an
|
|
1793
|
-
* auth round-trip only gets in the way (the built-in plugins set this).
|
|
1794
|
-
* The `--no-auth` CLI flag maps here for one-off runs.
|
|
1795
|
-
* - A {@link DevframeAuthHandler} — a custom handler (e.g. a tuned
|
|
1796
|
-
* `createInteractiveAuth`, or an entirely different scheme) passed
|
|
1797
|
-
* straight through to the RPC transport binding.
|
|
1798
|
-
*
|
|
1799
|
-
* Hosted adapters (`vite`, `embedded`) ignore this and defer to the host's
|
|
1800
|
-
* auth; `@vitejs/devtools` honors the equivalent `devtools.clientAuth`.
|
|
1801
|
-
*
|
|
1802
|
-
* @default true
|
|
1803
|
-
*/
|
|
1804
|
-
auth?: boolean | DevframeAuthHandler;
|
|
1805
|
-
/**
|
|
1806
|
-
* Expose a route-based MCP server alongside the dev server, speaking the
|
|
1807
|
-
* MCP Streamable-HTTP transport at `/__mcp` (relative to the base path).
|
|
1808
|
-
* It surfaces the same `ctx.agent` tools + shared-state resources as the
|
|
1809
|
-
* stdio `mcp` command, but against the live, running server.
|
|
1810
|
-
*
|
|
1811
|
-
* - `false` / omitted (default) — no MCP route is mounted.
|
|
1812
|
-
* - `true` — mount at the default `__mcp` route with the loopback-only
|
|
1813
|
-
* origin gate.
|
|
1814
|
-
* - {@link McpRouteOptions} — customise the route path / allowed origins.
|
|
1815
|
-
*
|
|
1816
|
-
* The `--mcp` / `--no-mcp` CLI flags override this per run.
|
|
1817
|
-
*/
|
|
1818
|
-
mcp?: boolean | McpRouteOptions;
|
|
1819
|
-
/**
|
|
1820
|
-
* Author's SPA dist — served as the devframe's UI. A local directory, or
|
|
1821
|
-
* a {@link StaticAssetsSource} remote declaration (`{ package, version }`)
|
|
1822
|
-
* served through devframe's caching CDN back-proxy so the assets need not
|
|
1823
|
-
* ship inside the node package.
|
|
1824
|
-
*/
|
|
1825
|
-
distDir?: StaticAssetsSource;
|
|
1826
|
-
/**
|
|
1827
|
-
* How the browser reaches the RPC WebSocket. Defaults to sharing the HTTP
|
|
1828
|
-
* port on the `__ws` route. See {@link DevframeWsOptions} for the
|
|
1829
|
-
* different-port and remote-origin variants. Pass `false` to serve no
|
|
1830
|
-
* WebSocket at all — clients connect over SSE instead (`backend: 'sse'`).
|
|
1831
|
-
*/
|
|
1832
|
-
ws?: DevframeWsOptions | false;
|
|
1833
|
-
/**
|
|
1834
|
-
* How the browser reaches the SSE RPC endpoint — enabled by default
|
|
1835
|
-
* alongside the WebSocket as the more portable transport. Pass `false`
|
|
1836
|
-
* to disable, or a {@link DevframeSseOptions} to change its route.
|
|
1837
|
-
*/
|
|
1838
|
-
sse?: boolean | DevframeSseOptions;
|
|
1839
|
-
/**
|
|
1840
|
-
* Capability-side CAC hook. Called with the CAC instance after the
|
|
1841
|
-
* adapter registers its built-in commands (`build` / `mcp`)
|
|
1842
|
-
* but before `createCac`'s own `configureCli` caller. Use this to
|
|
1843
|
-
* contribute tool-specific flags and subcommands from the definition
|
|
1844
|
-
* itself.
|
|
1845
|
-
*/
|
|
1846
|
-
configure?: (cli: CAC) => void;
|
|
1847
|
-
/**
|
|
1848
|
-
* Typed CLI flags for the default `dev` command, backed by any
|
|
1849
|
-
* [Standard Schema](https://standardschema.dev/) validator (valibot,
|
|
1850
|
-
* zod, arktype, or devframe's built-in `s`). The adapter registers
|
|
1851
|
-
* matching `--kebab-key` options on CAC, validates the parsed values,
|
|
1852
|
-
* and forwards the typed bag to `setup(ctx, { flags })`.
|
|
1853
|
-
*
|
|
1854
|
-
* Use {@link defineCliFlags} to preserve the literal schema-map
|
|
1855
|
-
* shape, and {@link InferCliFlags} to recover the typed output at the
|
|
1856
|
-
* call site:
|
|
1857
|
-
*
|
|
1858
|
-
* ```ts
|
|
1859
|
-
* const appFlags = defineCliFlags({
|
|
1860
|
-
* depth: v.pipe(v.number(), v.integer()),
|
|
1861
|
-
* config: v.optional(v.string()),
|
|
1862
|
-
* })
|
|
1863
|
-
*
|
|
1864
|
-
* defineDevframe({
|
|
1865
|
-
* cli: { flags: appFlags },
|
|
1866
|
-
* setup(ctx, info) {
|
|
1867
|
-
* const flags = info.flags as InferCliFlags<typeof appFlags>
|
|
1868
|
-
* },
|
|
1869
|
-
* })
|
|
1870
|
-
* ```
|
|
1871
|
-
*/
|
|
1872
|
-
flags?: CliFlagsSchema;
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Default dock attributes for the iframe entry a hub synthesizes when it
|
|
1876
|
-
* mounts this devframe. Framework-neutral metadata only — the hub layer
|
|
1877
|
-
* (`ctx.install`) merges these beneath its per-mount `dock` overrides,
|
|
1878
|
-
* which in turn sit beneath the locked, derived `id` / `type` / `url`.
|
|
1879
|
-
*
|
|
1880
|
-
* Every field is optional. `title` / `icon` default to the definition's
|
|
1881
|
-
* `name` / `icon` when omitted here; the rest are unset by default.
|
|
1882
|
-
* Standalone adapters (`cli` / `build`) ignore this entirely.
|
|
1883
|
-
*/
|
|
1884
|
-
interface DevframeDockDefaults {
|
|
1885
|
-
/** Dock entry title. Defaults to the definition's `name`. */
|
|
1886
|
-
title?: string;
|
|
1887
|
-
/** Dock entry icon. Defaults to the definition's `icon`. */
|
|
1888
|
-
icon?: string | {
|
|
1889
|
-
light: string;
|
|
1890
|
-
dark: string;
|
|
1891
|
-
};
|
|
1892
|
-
/**
|
|
1893
|
-
* Sort weight within the dock; higher sorts earlier.
|
|
1894
|
-
* @default 0
|
|
1895
|
-
*/
|
|
1896
|
-
defaultOrder?: number;
|
|
1897
|
-
/**
|
|
1898
|
-
* Category the entry groups under in the dock.
|
|
1899
|
-
* @default 'default'
|
|
1900
|
-
*/
|
|
1901
|
-
category?: string;
|
|
1902
|
-
/**
|
|
1903
|
-
* Conditional-visibility expression (same syntax as command `when`
|
|
1904
|
-
* clauses). Set to `'false'` to hide the entry unconditionally.
|
|
1905
|
-
*/
|
|
1906
|
-
when?: string;
|
|
1907
|
-
/**
|
|
1908
|
-
* Render-only visibility expression, same syntax as {@link when}. Hides the
|
|
1909
|
-
* entry's own dock-bar button when it evaluates to `false` while leaving it
|
|
1910
|
-
* registered and reachable (activation, RPC lookups, etc.) — unlike `when`,
|
|
1911
|
-
* which is the general relevance switch for the entry as a whole.
|
|
1912
|
-
*/
|
|
1913
|
-
visibility?: string;
|
|
1914
|
-
/** Badge text rendered on the dock icon (e.g. an unread count). */
|
|
1915
|
-
badge?: string;
|
|
1916
|
-
/** Id of the dock group this entry collapses under, if any. */
|
|
1917
|
-
groupId?: string;
|
|
1918
|
-
}
|
|
1919
|
-
/**
|
|
1920
|
-
* Runtime information threaded into `setup(ctx, info)`. Adapters
|
|
1921
|
-
* populate the fields that make sense for their deployment. In
|
|
1922
|
-
* particular, `createCac` fills `flags` with the parsed CAC bag.
|
|
1923
|
-
*/
|
|
1924
|
-
interface DevframeSetupInfo {
|
|
1925
|
-
/** Parsed CLI flags, populated by the CLI adapter. */
|
|
1926
|
-
flags?: Record<string, unknown>;
|
|
1927
|
-
}
|
|
1928
|
-
interface DevframeDefinition {
|
|
1929
|
-
id: string;
|
|
1930
|
-
name: string;
|
|
1931
|
-
/** Semver of the tool, surfaced in hub UIs and diagnostics. */
|
|
1932
|
-
version: string;
|
|
1933
|
-
/** npm package name the devframe ships in (e.g. `@scope/my-tool`). */
|
|
1934
|
-
packageName: string;
|
|
1935
|
-
/** Project homepage or documentation URL. */
|
|
1936
|
-
homepage: string;
|
|
1937
|
-
/** One-line summary of what the tool does. */
|
|
1938
|
-
description: string;
|
|
1939
|
-
icon?: string | {
|
|
1940
|
-
light: string;
|
|
1941
|
-
dark: string;
|
|
1942
|
-
};
|
|
1943
|
-
/**
|
|
1944
|
-
* Default dock attributes applied when a hub mounts this devframe as an
|
|
1945
|
-
* iframe dock entry. Consulted only by the hub install path (`ctx.install`),
|
|
1946
|
-
* which merge these beneath the per-mount `dock` overrides; standalone
|
|
1947
|
-
* adapters (`cli` / `build`) ignore it.
|
|
1948
|
-
*
|
|
1949
|
-
* @see {@link DevframeDockDefaults}
|
|
1950
|
-
*/
|
|
1951
|
-
dock?: DevframeDockDefaults;
|
|
1952
|
-
/**
|
|
1953
|
-
* Mount path override. Defaults depend on the adapter:
|
|
1954
|
-
* `/` for standalone (`cli` / `build`), `/__<id>/` for hosted
|
|
1955
|
-
* (`vite` / `embedded`).
|
|
1956
|
-
*/
|
|
1957
|
-
basePath?: string;
|
|
1958
|
-
/**
|
|
1959
|
-
* How a hub reacts when another devframe sharing this one's `id` is
|
|
1960
|
-
* mounted onto the same hub. Consulted only by hub adapters
|
|
1961
|
-
* (`ctx.install`); standalone adapters (`cli` / `build`)
|
|
1962
|
-
* ignore it.
|
|
1963
|
-
*
|
|
1964
|
-
* @default 'warn'
|
|
1965
|
-
*/
|
|
1966
|
-
duplicationStrategy?: DevframeDuplicationStrategy;
|
|
1967
|
-
/**
|
|
1968
|
-
* Declares which runtimes meaningfully support this devframe. Adapters
|
|
1969
|
-
* act on a `false` before doing any work:
|
|
1970
|
-
*
|
|
1971
|
-
* - `build: false` — `createCac` skips registering the `build` subcommand,
|
|
1972
|
-
* and `createBuild` refuses (throws `DF0042`) unless `{ force: true }`.
|
|
1973
|
-
* Useful for a devframe whose value is inherently live (e.g. it manages
|
|
1974
|
-
* real files on disk), so a static export would only ever produce a
|
|
1975
|
-
* broken, write-less shell of the tool.
|
|
1976
|
-
* - `dev: false` — `createDevServer` refuses (throws `DF0058`) unless
|
|
1977
|
-
* `{ force: true }`. Useful for a devframe that only makes sense as a
|
|
1978
|
-
* static export (e.g. a report generator with nothing to serve live).
|
|
1979
|
-
*/
|
|
1980
|
-
capabilities?: {
|
|
1981
|
-
dev?: boolean;
|
|
1982
|
-
build?: boolean;
|
|
1983
|
-
};
|
|
1984
|
-
/**
|
|
1985
|
-
* Wire services this devframe consumes (see `DevframeServiceDefinition`).
|
|
1986
|
-
* Each entry is either a declarative descriptor
|
|
1987
|
-
* (`{ package, version?, required?, options? }` — the host imports the
|
|
1988
|
-
* package's default-export factory, resolving it against **this plugin's
|
|
1989
|
-
* own dependencies**) or a ready `DevframeServiceDefinition` (the factory
|
|
1990
|
-
* was already called). The adapter queues these before `setup(ctx)` runs
|
|
1991
|
-
* and constructs each service once at the `ctx.services.ready()` barrier,
|
|
1992
|
-
* merging option sets across every declarer. Missing services are skipped
|
|
1993
|
-
* unless marked `required` — clients feature-detect via
|
|
1994
|
-
* `client.services.has(pkg)` and degrade.
|
|
1995
|
-
*/
|
|
1996
|
-
services?: DevframeServiceInput[];
|
|
1997
|
-
/** Server-side setup — the primary entrypoint. Runs in every runtime. */
|
|
1998
|
-
setup: (ctx: DevframeNodeContext, info?: DevframeSetupInfo) => void | Promise<void>;
|
|
1999
|
-
cli?: DevframeCliOptions;
|
|
2000
|
-
}
|
|
2001
|
-
//#endregion
|
|
2002
|
-
export { DevframeStorageScope as $, DevframeServiceId as A, DevframeAgentHost as At, DevframeScopedNodeRpc as B, RpcStreamingChannelOptions as C, AgentResource as Ct, DevframeRpcSharedStates as D, AgentToolInput as Dt, DevframeRpcServerFunctions as E, AgentTool as Et, DevframeServicesHost as F, CliFlagsSchema as Ft, ScopedBroadcastOptions as G, DevframeSettings as H, DevframeServicesRegistry as I, InferCliFlags as It, ScopedServerFunctions as J, ScopedClientFunctions as K, DevframeServicesScopeRegistry as L, defineCliFlags as Lt, DevframeServiceMeta as M, EventEmitter as Mt, DevframeServiceOf as N, EventUnsubscribe as Nt, DevframeServiceDefinition as O, AgentToolProvider as Ot, DevframeServiceScopeOf as P, EventsMap as Pt, DevframeHost as Q, DevframeServicesState as R, parseCliFlags as Rt, RpcStreamingChannel as S, AgentManifest as St, DevframeRpcClientFunctions as T, AgentResourceInput as Tt, DevframeSettingsRegistry as U, DevframeScopedStreamingHost as V, DevframeSettingsStore as W, SettingsForNamespace as X, ScopedSharedStates as Y, DevframeViewHost as Z, DevframeNodeRpcSession as _, SharedStateEvents as _t, DevframeDuplicationStrategy as a, StreamErrorPayload as at, RpcSharedStateGetOptions as b, createSharedState as bt, DevframeWsOptions as c, StreamSinkEvents as ct, ConnectionMeta as d, Immutable as dt, DevframeDiagnosticsHost as et, ConnectionMetaSse as f, ImmutableArray as ft, DevframeNodeContext as g, SharedState as gt, DevframeConnectionConfigsRegistry as h, ImmutableSet as ht, DevframeDockDefaults as i, CreateStreamSinkOptions as it, DevframeServiceInput as j, DevframeAgentHostEvents as jt, DevframeServiceDescriptor as k, AgentToolProviderHandle as kt, McpRouteOptions as l, createStreamReader as lt, DevframeCapabilities as m, ImmutableObject as mt, DevframeDefinition as n, BufferedChunk as nt, DevframeSetupInfo as o, StreamReader as ot, ConnectionMetaWebsocket as p, ImmutableMap as pt, ScopedRpcFn as q, DevframeDeploymentKind as r, CreateStreamReaderOptions as rt, DevframeSseOptions as s, StreamSink as st, DevframeCliOptions as t, DevframeDiagnosticsLogger as tt, DevframeAuthHandler as u, createStreamSink as ut, RpcBroadcastOptions as v, SharedStateOptions as vt, RpcStreamingHost as w, AgentResourceContent as wt, RpcSharedStateHost as x, AgentHandle as xt, RpcFunctionsHost as y, SharedStatePatch as yt, DevframeScopedNodeContext as z };
|
|
2065
|
+
export { DevframeServicesHost as $, SharedStateEvents as A, AgentResource as At, DevframeSetupInfo as B, EventUnsubscribe as Bt, createStreamSink as C, DevframeAuthHandler as Ct, ImmutableObject as D, parseCliFlags as Dt, ImmutableMap as E, defineCliFlags as Et, DevframeDefinition as F, AgentToolProvider as Ft, McpRouteOptions as G, DevframeSnapshotRpcInputs as H, DevframeDeploymentKind as I, AgentToolProviderHandle as It, DevframeServiceId as J, DevframeServiceDefinition as K, DevframeDockDefaults as L, DevframeAgentHost as Lt, SharedStatePatch as M, AgentResourceInput as Mt, createSharedState as N, AgentTool as Nt, ImmutableSet as O, AgentHandle as Ot, DevframeCliOptions as P, AgentToolInput as Pt, DevframeServiceScopeOf as Q, DevframeDuplicationStrategy as R, DevframeAgentHostEvents as Rt, createStreamReader as S, DevframeDiagnosticsLogger as St, ImmutableArray as T, InferCliFlags as Tt, DevframeSseOptions as U, DevframeSnapshotRpcEntry as V, EventsMap as Vt, DevframeWsOptions as W, DevframeServiceMeta as X, DevframeServiceInput as Y, DevframeServiceOf as Z, CreateStreamSinkOptions as _, DevframeRpcServerFunctions as _t, DevframeConnectionConfigsRegistry as a, DevframeScopedStreamingHost as at, StreamSink as b, DevframeStorageScope as bt, RpcBroadcastOptions as c, DevframeSettingsStore as ct, RpcSharedStateHost as d, ScopedRpcFn as dt, DevframeServicesRegistry as et, RpcStreamingChannel as f, ScopedServerFunctions as ft, CreateStreamReaderOptions as g, DevframeRpcClientFunctions as gt, BufferedChunk as h, DevframeViewHost as ht, DevframeCapabilities as i, DevframeScopedNodeRpc as it, SharedStateOptions as j, AgentResourceContent as jt, SharedState as k, AgentManifest as kt, RpcFunctionsHost as l, ScopedBroadcastOptions as lt, RpcStreamingHost as m, SettingsForNamespace as mt, ConnectionMetaSse as n, DevframeServicesState as nt, DevframeNodeContext as o, DevframeSettings as ot, RpcStreamingChannelOptions as p, ScopedSharedStates as pt, DevframeServiceDescriptor as q, ConnectionMetaWebsocket as r, DevframeScopedNodeContext as rt, DevframeNodeRpcSession as s, DevframeSettingsRegistry as st, ConnectionMeta as t, DevframeServicesScopeRegistry as tt, RpcSharedStateGetOptions as u, ScopedClientFunctions as ut, StreamErrorPayload as v, DevframeRpcSharedStates as vt, Immutable as w, CliFlagsSchema as wt, StreamSinkEvents as x, DevframeDiagnosticsHost as xt, StreamReader as y, DevframeHost as yt, DevframeRpcOptions as z, EventEmitter as zt };
|