devframe 0.9.0-beta.9 → 0.9.1
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-BM3PdYli.mjs +68 -0
- package/dist/{_shared-BPmX9fqK.d.mts → _shared-DqbKCAew.d.mts} +1 -1
- package/dist/adapters/build.d.mts +1 -1
- package/dist/adapters/build.mjs +8 -6
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +4 -3
- package/dist/adapters/dev.d.mts +3 -3
- package/dist/adapters/dev.mjs +2 -2
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/embedded.mjs +1 -0
- package/dist/adapters/initiate.d.mts +2 -2
- 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 +48 -2
- package/dist/client/index.mjs +131 -22
- package/dist/constants.d.mts +74 -2
- package/dist/constants.mjs +9 -2
- package/dist/{context-CEe4PBz1.mjs → context-Bdkakk8S.mjs} +319 -21
- package/dist/{context-xdynGSLP.mjs → context-CXhjygJU.mjs} +2 -2
- package/dist/{context-B3YUu_95.d.mts → context-dqyXS6o3.d.mts} +1 -1
- package/dist/{dev-Chvlqs7I.mjs → dev-C8VHtuPP.mjs} +12 -9
- package/dist/{devframe-DJMg0WLe.d.mts → devframe-BlLEZR-x.d.mts} +741 -588
- package/dist/{diagnostics-DOQPnQmX.mjs → diagnostics-Di8ytitn.mjs} +24 -0
- package/dist/{_shared-CKZPvSrN.mjs → dist-CZXfGEkd.mjs} +2 -68
- package/dist/events-DKfSmoTj.mjs +65 -0
- package/dist/{host-agent-xLCSe65D.mjs → host-agent-BDdcOXhG.mjs} +14 -13
- package/dist/{http-8Kpkap4_.mjs → http-BbHCU5Zs.mjs} +9 -6
- package/dist/{index-CB-fSUyn.d.mts → index-BKFT9-jA.d.mts} +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/{instance-registry-WvQkt42E.mjs → instance-registry-CHunhMi5.mjs} +4 -4
- package/dist/{instance-shell-CljiKq1c.d.mts → instance-shell-0iw-cQ5V.d.mts} +2 -2
- package/dist/{instance-shell-XvN5USg4.mjs → instance-shell-DbDoozNM.mjs} +5 -5
- package/dist/internal/index.d.mts +46 -7
- package/dist/internal/index.mjs +8 -8
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/auth.mjs +2 -2
- package/dist/node/hub-internals.d.mts +2 -2
- package/dist/node/hub-internals.mjs +2 -2
- package/dist/node/index.d.mts +1 -1
- package/dist/node/index.mjs +2 -2
- package/dist/recipes/common-rpc-functions.d.mts +1 -1
- package/dist/recipes/interactive-auth.d.mts +2 -2
- package/dist/recipes/interactive-auth.mjs +2 -2
- package/dist/{remote-assets-nTTxY2MU.mjs → remote-assets-DRfY2RjX.mjs} +6 -5
- package/dist/{revoke-DZbJ7Cl0.mjs → revoke-3Q6aDKWk.mjs} +2 -1
- package/dist/{rolldown-runtime-JspESFgx.mjs → rolldown-runtime-B4iAMlE-.mjs} +1 -1
- package/dist/rpc/transports/sse-client.mjs +1 -1
- package/dist/rpc/transports/sse-server.mjs +2 -2
- package/dist/rpc/transports/ws-bun.d.mts +1 -1
- package/dist/rpc/transports/ws-bun.mjs +1 -1
- package/dist/rpc/transports/ws-client.mjs +1 -1
- package/dist/rpc/transports/ws-server.mjs +1 -1
- package/dist/{rpc-core-Cc4lucI0.d.mts → rpc-core-CR8o34O9.d.mts} +2 -2
- package/dist/{rpc-core-DMTe-hLo.mjs → rpc-core-Dru9uoM0.mjs} +11 -4
- package/dist/{state-Cc1IeSmQ.mjs → state-CK9LjrnT.mjs} +1 -1
- package/dist/{storage-CYye7P7L.mjs → storage-D4a99Ung.mjs} +3 -3
- package/dist/types/index.d.mts +2 -2
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/get-port.d.mts +17 -0
- package/dist/utils/get-port.mjs +2 -0
- package/dist/utils/launch-editor.mjs +12 -11
- package/dist/utils/open.mjs +1 -1
- package/dist/utils/remote-assets.mjs +1 -1
- package/dist/utils/serve-static.mjs +6 -5
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/{wire-codec-GsoRxIsD.mjs → wire-codec-0K-o5MYW.mjs} +0 -2
- package/dist/{ws-server-BiUne4K7.mjs → ws-server-BdSLrhxE.mjs} +1 -1
- package/package.json +2 -1
- package/dist/{host-h3-Crkzm42q.mjs → host-h3-fRbF9yor.mjs} +1 -1
|
@@ -493,734 +493,874 @@ declare function createStreamSink<T>(options?: CreateStreamSinkOptions): StreamS
|
|
|
493
493
|
*/
|
|
494
494
|
declare function createStreamReader<T>(options?: CreateStreamReaderOptions): StreamReader<T>;
|
|
495
495
|
//#endregion
|
|
496
|
-
//#region src/types/
|
|
496
|
+
//#region src/types/diagnostics.d.ts
|
|
497
497
|
/**
|
|
498
|
-
*
|
|
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.
|
|
499
503
|
*/
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Server→client notification that this connection's auth token has been
|
|
503
|
-
* revoked. The client drops to untrusted on receipt. Broadcast by
|
|
504
|
-
* `revokeActiveConnectionsForToken`.
|
|
505
|
-
*
|
|
506
|
-
* @internal
|
|
507
|
-
*/
|
|
508
|
-
'devframe:auth:revoked': () => Promise<void>;
|
|
509
|
-
/**
|
|
510
|
-
* Streaming chunk pushed from server to subscribed clients. Wired by
|
|
511
|
-
* `RpcStreamingHost`; do not register manually.
|
|
512
|
-
*
|
|
513
|
-
* @internal
|
|
514
|
-
*/
|
|
515
|
-
'devframe:streaming:chunk': (channel: string, id: string, seq: number, chunk: any) => Promise<void>;
|
|
516
|
-
/**
|
|
517
|
-
* Streaming terminator pushed from server to subscribed clients. Wired by
|
|
518
|
-
* `RpcStreamingHost`; do not register manually.
|
|
519
|
-
*
|
|
520
|
-
* @internal
|
|
521
|
-
*/
|
|
522
|
-
'devframe:streaming:end': (channel: string, id: string, error?: {
|
|
523
|
-
name: string;
|
|
524
|
-
message: string;
|
|
525
|
-
}) => Promise<void>;
|
|
526
|
-
/**
|
|
527
|
-
* Server→client cancel for an in-flight upload. Wired by
|
|
528
|
-
* `RpcStreamingHost`; do not register manually.
|
|
529
|
-
*
|
|
530
|
-
* @internal
|
|
531
|
-
*/
|
|
532
|
-
'devframe:streaming:upload-cancel': (channel: string, id: string) => Promise<void>;
|
|
533
|
-
/**
|
|
534
|
-
* Full shared-state snapshot pushed from server to subscribed clients.
|
|
535
|
-
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
536
|
-
*
|
|
537
|
-
* @internal
|
|
538
|
-
*/
|
|
539
|
-
'devframe:rpc:client-state:updated': (key: string, fullState: any, syncId: string) => Promise<void>;
|
|
540
|
-
/**
|
|
541
|
-
* Incremental shared-state patch pushed from server to subscribed clients.
|
|
542
|
-
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
543
|
-
*
|
|
544
|
-
* @internal
|
|
545
|
-
*/
|
|
546
|
-
'devframe:rpc:client-state:patch': (key: string, patches: any[], syncId: string) => Promise<void>;
|
|
547
|
-
}
|
|
504
|
+
type DevframeDiagnosticsLogger = Record<string, any>;
|
|
548
505
|
/**
|
|
549
|
-
*
|
|
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
|
+
* ```
|
|
550
527
|
*/
|
|
551
|
-
interface
|
|
552
|
-
/**
|
|
553
|
-
* Authenticate a connection with a previously-issued bearer token; resolves
|
|
554
|
-
* whether the connection is now trusted. The interactive handler is provided
|
|
555
|
-
* by the host adapter (e.g. Vite DevTools); the standalone server registers
|
|
556
|
-
* an auto-trust noop when `auth: false`.
|
|
557
|
-
*
|
|
558
|
-
* Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
|
|
559
|
-
* reachable before the connection is trusted — this is the *only* rule the
|
|
560
|
-
* pre-trust gate applies, no per-method allowlist.
|
|
561
|
-
*
|
|
562
|
-
* @internal
|
|
563
|
-
*/
|
|
564
|
-
'anonymous:devframe:auth': (params: {
|
|
565
|
-
authToken: string;
|
|
566
|
-
ua: string;
|
|
567
|
-
origin: string;
|
|
568
|
-
}) => Promise<{
|
|
569
|
-
isTrusted: boolean;
|
|
570
|
-
}>;
|
|
528
|
+
interface DevframeDiagnosticsHost {
|
|
571
529
|
/**
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
* reachable before the connection is trusted.
|
|
578
|
-
*
|
|
579
|
-
* @internal
|
|
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.
|
|
580
535
|
*/
|
|
581
|
-
|
|
582
|
-
code: string;
|
|
583
|
-
ua: string;
|
|
584
|
-
origin: string;
|
|
585
|
-
}) => Promise<{
|
|
586
|
-
authToken: string | null;
|
|
587
|
-
}>;
|
|
536
|
+
readonly logger: DevframeDiagnosticsLogger;
|
|
588
537
|
/**
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
* `
|
|
593
|
-
*
|
|
594
|
-
* @internal
|
|
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.
|
|
595
543
|
*/
|
|
596
|
-
|
|
544
|
+
register: (definitions: Record<string, unknown>) => void;
|
|
597
545
|
/**
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
546
|
+
* Build a typed diagnostics object with the host's ANSI console reporter
|
|
547
|
+
* pre-wired. The same `devframe/utils/nostics` `defineDiagnostics` every
|
|
548
|
+
* built-in plugin's module-level `diagnostics.ts` uses, so integrations
|
|
549
|
+
* don't need to take a direct dependency on `nostics`.
|
|
602
550
|
*/
|
|
603
|
-
|
|
551
|
+
defineDiagnostics: typeof defineDiagnostics;
|
|
552
|
+
}
|
|
553
|
+
//#endregion
|
|
554
|
+
//#region src/types/host.d.ts
|
|
555
|
+
interface DevframeHost {
|
|
604
556
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
*
|
|
557
|
+
* Serve static assets at the given URL base — a local directory, or a
|
|
558
|
+
* resolved {@link RemoteAssetsStore} back-proxy. Called by
|
|
559
|
+
* `DevframeViewHost.hostStatic` (which normalizes `RemoteAssets`
|
|
560
|
+
* declarations into stores first). Implementations map this to whatever
|
|
561
|
+
* the underlying runtime expects (Vite middleware, h3 handler, no-op
|
|
562
|
+
* for build snapshots) — the shared engine in
|
|
563
|
+
* `devframe/utils/serve-static` accepts either shape.
|
|
609
564
|
*/
|
|
610
|
-
|
|
565
|
+
mountStatic: (base: string, source: string | RemoteAssetsStore) => void | Promise<void>;
|
|
611
566
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
567
|
+
* Serve the host's connection meta (`__connection.json`) at the given URL
|
|
568
|
+
* base, so a devframe SPA mounted there can discover the RPC/WS endpoint
|
|
569
|
+
* via `connectDevframe()`'s relative `./__connection.json` fetch.
|
|
614
570
|
*
|
|
615
|
-
*
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
*
|
|
620
|
-
* `RpcSharedStateHost`; do not register manually.
|
|
571
|
+
* Called by `ctx.install` for each mounted devframe (alongside
|
|
572
|
+
* `mountStatic`). Without it, an embedded SPA can only discover the
|
|
573
|
+
* endpoint by inheriting it from a same-origin parent window — which fails
|
|
574
|
+
* for cross-origin or sandboxed iframes. Implementations serve the same
|
|
575
|
+
* meta they expose at the hub's own base.
|
|
621
576
|
*
|
|
622
|
-
*
|
|
577
|
+
* Optional in the type, but a host that mounts a devframe with a servable
|
|
578
|
+
* `distDir` yet omits this hook triggers a `DF8106` diagnostic, since the
|
|
579
|
+
* SPA's `./__connection.json` fetch would otherwise fall through and break
|
|
580
|
+
* silently. A static-snapshot host that bakes the meta into its served files
|
|
581
|
+
* can implement it as a no-op to acknowledge this intentionally.
|
|
623
582
|
*/
|
|
624
|
-
|
|
583
|
+
mountConnectionMeta?: (base: string) => void | Promise<void>;
|
|
625
584
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
585
|
+
* Return the public origin the host is reachable at, e.g.
|
|
586
|
+
* `http://localhost:5173`. Used by the dock host to enrich remote
|
|
587
|
+
* iframe URLs with a full `origin`. Called only when a dock needs an
|
|
588
|
+
* absolute URL; hosts that never serve remote docks can return any
|
|
589
|
+
* reasonable value.
|
|
630
590
|
*/
|
|
631
|
-
|
|
632
|
-
afterSeq?: number;
|
|
633
|
-
}) => Promise<void>;
|
|
591
|
+
resolveOrigin: () => string;
|
|
634
592
|
/**
|
|
635
|
-
*
|
|
636
|
-
*
|
|
593
|
+
* Resolve a directory the host owns for persisted devframe state.
|
|
594
|
+
* Each host picks its own app-name namespace so storage doesn't
|
|
595
|
+
* collide between, say, the Vite host (`.vite/devframe`) and a
|
|
596
|
+
* standalone CLI host (`.<appName>/devframe`).
|
|
637
597
|
*
|
|
638
|
-
*
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
*
|
|
643
|
-
*
|
|
598
|
+
* - `workspace` — state shared with the whole team through version
|
|
599
|
+
* control (saved queries, shared presets). Conventionally
|
|
600
|
+
* `${workspaceRoot}/.devframe/`; hosts must place it somewhere
|
|
601
|
+
* committable.
|
|
602
|
+
* - `project` — per-checkout private state (caches, personal
|
|
603
|
+
* settings). Typically under
|
|
604
|
+
* `${cwd}/node_modules/.<appName>/devframe/`, which version
|
|
605
|
+
* control ignores.
|
|
606
|
+
* - `global` — per-user state (auth tokens, machine-wide
|
|
607
|
+
* preferences). Typically under
|
|
608
|
+
* `${homedir()}/.<appName>/devframe/`.
|
|
644
609
|
*
|
|
645
|
-
*
|
|
610
|
+
* Implementations should ensure the directory exists or be safe to
|
|
611
|
+
* pass to a downstream `createStorage(...)` call that creates it
|
|
612
|
+
* lazily.
|
|
646
613
|
*/
|
|
647
|
-
|
|
614
|
+
getStorageDir: (scope: DevframeStorageScope) => string;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Storage placement classes for {@link DevframeHost.getStorageDir}:
|
|
618
|
+
* `workspace` is committable and team-shared, `project` is per-checkout
|
|
619
|
+
* private, `global` is per-user.
|
|
620
|
+
*/
|
|
621
|
+
type DevframeStorageScope = 'workspace' | 'project' | 'global';
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/types/views.d.ts
|
|
624
|
+
interface DevframeViewHost {
|
|
648
625
|
/**
|
|
649
|
-
* Client→server upload chunk. Wired by `RpcStreamingHost`; do not
|
|
650
|
-
* register manually.
|
|
651
|
-
*
|
|
652
626
|
* @internal
|
|
653
627
|
*/
|
|
654
|
-
|
|
628
|
+
buildStaticDirs: {
|
|
629
|
+
baseUrl: string;
|
|
630
|
+
source: StaticAssetsSource;
|
|
631
|
+
}[];
|
|
655
632
|
/**
|
|
656
|
-
*
|
|
657
|
-
* register
|
|
633
|
+
* Helper to host static files
|
|
634
|
+
* - In `dev` mode, it will register middleware to `viteServer.middlewares` to host the static files
|
|
635
|
+
* - In `build` mode, it will copy the static files to the dist directory
|
|
658
636
|
*
|
|
659
|
-
* @
|
|
637
|
+
* Accepts a local dist directory, or a {@link StaticAssetsSource} remote
|
|
638
|
+
* declaration served through devframe's caching CDN back-proxy.
|
|
660
639
|
*/
|
|
661
|
-
|
|
662
|
-
name: string;
|
|
663
|
-
message: string;
|
|
664
|
-
}) => Promise<void>;
|
|
640
|
+
hostStatic: (baseUrl: string, source: StaticAssetsSource) => void;
|
|
665
641
|
}
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/types/scope.d.ts
|
|
644
|
+
type AnyRpcFn = (...args: any[]) => any;
|
|
666
645
|
/**
|
|
667
|
-
*
|
|
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.
|
|
668
651
|
*/
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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>>;
|
|
691
|
+
/**
|
|
692
|
+
* Subscribe to settings changes. Resolves to an unsubscribe function.
|
|
693
|
+
* Fires on every local or remote mutation with the full snapshot.
|
|
694
|
+
*/
|
|
695
|
+
onChange: (fn: (value: Readonly<T>) => void) => Promise<() => void>;
|
|
675
696
|
}
|
|
676
|
-
|
|
697
|
+
/**
|
|
698
|
+
* The two settings scopes available on a scoped context.
|
|
699
|
+
*
|
|
700
|
+
* - `project` — per-workspace state, persisted under the host's
|
|
701
|
+
* `workspace` storage dir. Project-local settings.
|
|
702
|
+
* - `global` — per-user state, persisted under the host's `global`
|
|
703
|
+
* storage dir. Machine-wide preferences.
|
|
704
|
+
*/
|
|
705
|
+
interface DevframeSettings<T extends Record<string, any> = Record<string, any>> {
|
|
706
|
+
global: DevframeSettingsStore<T>;
|
|
707
|
+
project: DevframeSettingsStore<T>;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Map the server-side RPC registry down to the bare names owned by a
|
|
711
|
+
* namespace, so a scoped `call('get-cwd')` is typed without the
|
|
712
|
+
* `my-plugin:` prefix.
|
|
713
|
+
*/
|
|
714
|
+
type ScopedServerFunctions<NS extends string> = { [K in keyof DevframeRpcServerFunctions as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcServerFunctions[K]; };
|
|
715
|
+
/**
|
|
716
|
+
* Map the client-side RPC registry down to the bare names owned by a
|
|
717
|
+
* namespace, used by scoped `broadcast`.
|
|
718
|
+
*/
|
|
719
|
+
type ScopedClientFunctions<NS extends string> = { [K in keyof DevframeRpcClientFunctions as K extends `${NS}:${infer R}` ? R : never]: DevframeRpcClientFunctions[K]; };
|
|
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[]> {
|
|
677
730
|
method: METHOD;
|
|
678
731
|
args: Args;
|
|
679
732
|
optional?: boolean;
|
|
680
733
|
event?: boolean;
|
|
681
|
-
filter?: (client: BirpcReturn<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>) => boolean | void;
|
|
682
734
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
*/
|
|
689
|
-
invokeLocal: <T extends keyof DevframeRpcServerFunctions, Args extends Parameters<DevframeRpcServerFunctions[T]>>(method: T, ...args: Args) => Promise<Awaited<ReturnType<DevframeRpcServerFunctions[T]>>>;
|
|
735
|
+
/**
|
|
736
|
+
* Node-side streaming host scoped to a namespace. Channel names are
|
|
737
|
+
* auto-prefixed with `<namespace>:`.
|
|
738
|
+
*/
|
|
739
|
+
interface DevframeScopedStreamingHost {
|
|
690
740
|
/**
|
|
691
|
-
*
|
|
741
|
+
* Register a streaming channel. The bare `name` is auto-prefixed with
|
|
742
|
+
* the scope namespace (`<namespace>:<name>`); pass an already-qualified
|
|
743
|
+
* name (containing `:`) to opt out.
|
|
692
744
|
*/
|
|
693
|
-
|
|
745
|
+
create: <T = unknown>(name: string, opts?: RpcStreamingChannelOptions) => RpcStreamingChannel<T>;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* The scoped node RPC surface exposed on `ctx.scope('my-plugin').rpc`.
|
|
749
|
+
* IDs and keys you pass are auto-namespaced with `my-plugin:`.
|
|
750
|
+
*/
|
|
751
|
+
interface DevframeScopedNodeRpc<NS extends string = string> {
|
|
752
|
+
/** The namespace this surface is scoped to. */
|
|
753
|
+
readonly namespace: NS;
|
|
694
754
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
755
|
+
* Register a server RPC function. The definition's `name` must be bare
|
|
756
|
+
* (no `:`); it is stored as `<namespace>:<name>`. Throws `DF0034` if an
|
|
757
|
+
* already-namespaced name is passed.
|
|
698
758
|
*/
|
|
699
|
-
|
|
759
|
+
register: (fn: RpcFunctionDefinition<string, any, any, any, any, any, DevframeNodeContext>, force?: boolean) => void;
|
|
700
760
|
/**
|
|
701
|
-
*
|
|
761
|
+
* Update a previously registered server RPC function. Same naming rules
|
|
762
|
+
* as {@link DevframeScopedNodeRpc.register}.
|
|
702
763
|
*/
|
|
703
|
-
|
|
764
|
+
update: (fn: RpcFunctionDefinition<string, any, any, any, any, any, DevframeNodeContext>, force?: boolean) => void;
|
|
704
765
|
/**
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
* @see RpcStreamingHost
|
|
766
|
+
* Invoke a locally registered server RPC function. Bare names are
|
|
767
|
+
* resolved within this namespace; pass a fully-qualified name
|
|
768
|
+
* (containing `:`) to call another scope's function.
|
|
709
769
|
*/
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
interface RpcSharedStateHost {
|
|
717
|
-
get: <T extends object = any>(key: string, options?: RpcSharedStateGetOptions<T>) => Promise<SharedState<T>>;
|
|
718
|
-
keys: () => string[];
|
|
770
|
+
call: {
|
|
771
|
+
<T extends keyof ScopedServerFunctions<NS> & string>(method: T, ...args: Parameters<ScopedRpcFn<DevframeRpcServerFunctions, NS, T>>): Promise<Awaited<ReturnType<ScopedRpcFn<DevframeRpcServerFunctions, NS, T>>>>;
|
|
772
|
+
<T extends keyof DevframeRpcServerFunctions & string>(method: T, ...args: Parameters<Extract<DevframeRpcServerFunctions[T], AnyRpcFn>>): Promise<Awaited<ReturnType<Extract<DevframeRpcServerFunctions[T], AnyRpcFn>>>>;
|
|
773
|
+
(method: string, ...args: any[]): Promise<any>;
|
|
774
|
+
};
|
|
719
775
|
/**
|
|
720
|
-
*
|
|
721
|
-
*
|
|
722
|
-
* Useful for protocol adapters (e.g. MCP) that surface shared state
|
|
723
|
-
* as dynamic resources.
|
|
776
|
+
* Broadcast a client RPC event to every connected client. Bare method
|
|
777
|
+
* names are resolved within this namespace.
|
|
724
778
|
*/
|
|
725
|
-
|
|
779
|
+
broadcast: {
|
|
780
|
+
<T extends keyof ScopedClientFunctions<NS> & string>(options: ScopedBroadcastOptions<T, Parameters<ScopedRpcFn<DevframeRpcClientFunctions, NS, T>>>): Promise<void>;
|
|
781
|
+
(options: ScopedBroadcastOptions<string, any[]>): Promise<void>;
|
|
782
|
+
};
|
|
726
783
|
/**
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
* Used by short-lived states (e.g. a disposed JSON-render view) to avoid
|
|
730
|
-
* leaking listeners and lingering entries for the context lifetime.
|
|
784
|
+
* Resolve a namespaced shared state. Bare keys are resolved within this
|
|
785
|
+
* namespace; pass a fully-qualified key (containing `:`) to opt out.
|
|
731
786
|
*/
|
|
732
|
-
|
|
787
|
+
sharedState: {
|
|
788
|
+
<T extends keyof ScopedSharedStates<NS> & string>(key: T, options?: RpcSharedStateGetOptions<ScopedSharedStates<NS>[T]>): Promise<SharedState<ScopedSharedStates<NS>[T]>>;
|
|
789
|
+
<T extends Record<string, any> = Record<string, any>>(key: string, options?: RpcSharedStateGetOptions<T>): Promise<SharedState<T>>;
|
|
790
|
+
};
|
|
791
|
+
/** Streaming host scoped to this namespace. */
|
|
792
|
+
streaming: DevframeScopedStreamingHost;
|
|
793
|
+
/** Pass-through to the current RPC session, when called inside a handler. */
|
|
794
|
+
getCurrentRpcSession: () => DevframeNodeRpcSession | undefined;
|
|
733
795
|
}
|
|
734
796
|
/**
|
|
735
|
-
*
|
|
797
|
+
* A namespace-scoped view of {@link DevframeNodeContext}. Returned by
|
|
798
|
+
* `ctx.scope('my-plugin')`. Re-exposes the unscoped surfaces (`views`,
|
|
799
|
+
* `diagnostics`, `agent`, …), replaces `rpc` with the auto-namespaced
|
|
800
|
+
* {@link DevframeScopedNodeRpc}, and adds a top-level `settings` store.
|
|
736
801
|
*/
|
|
737
|
-
interface
|
|
738
|
-
/**
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
802
|
+
interface DevframeScopedNodeContext<NS extends string = string, Settings extends Record<string, any> = Record<string, any>> {
|
|
803
|
+
/** The namespace this context is scoped to. */
|
|
804
|
+
readonly namespace: NS;
|
|
805
|
+
/** The underlying unscoped context. */
|
|
806
|
+
readonly base: DevframeNodeContext;
|
|
807
|
+
readonly workspaceRoot: string;
|
|
808
|
+
readonly cwd: string;
|
|
809
|
+
readonly mode: 'dev' | 'build';
|
|
810
|
+
host: DevframeHost;
|
|
811
|
+
rpc: DevframeScopedNodeRpc<NS>;
|
|
812
|
+
/** Persisted settings for this namespace (`global` + `project`). */
|
|
813
|
+
settings: DevframeSettings<Settings>;
|
|
814
|
+
views: DevframeViewHost;
|
|
815
|
+
diagnostics: DevframeDiagnosticsHost;
|
|
816
|
+
agent: DevframeAgentHost;
|
|
747
817
|
/**
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
* this window, late subscribers can still join and replay the buffer
|
|
751
|
-
* + receive the `end` frame.
|
|
752
|
-
*
|
|
753
|
-
* Defaults to `30_000` (30 s) when `replayWindow > 0`, else `0`
|
|
754
|
-
* (immediate free). Set to `0` to opt out, or higher for longer
|
|
755
|
-
* post-mortem replay.
|
|
818
|
+
* Return a new scoped context, replacing the current scope. Pass `null`
|
|
819
|
+
* or `''` to un-scope and get the base context.
|
|
756
820
|
*/
|
|
757
|
-
|
|
821
|
+
scope: DevframeNodeContext['scope'];
|
|
758
822
|
}
|
|
823
|
+
//#endregion
|
|
824
|
+
//#region src/types/services.d.ts
|
|
759
825
|
/**
|
|
760
|
-
*
|
|
761
|
-
*
|
|
762
|
-
|
|
826
|
+
* Augmentation point mapping service ids to their implementation types.
|
|
827
|
+
* Providers extend this via `declare module 'devframe'`.
|
|
828
|
+
*/
|
|
829
|
+
interface DevframeServicesRegistry {}
|
|
830
|
+
/**
|
|
831
|
+
* A known (augmented) service id, or any namespaced string for services
|
|
832
|
+
* without a published type augmentation.
|
|
833
|
+
*/
|
|
834
|
+
type DevframeServiceId = keyof DevframeServicesRegistry | (string & {});
|
|
835
|
+
/** Resolved service type for an id: augmented type, or `unknown`. */
|
|
836
|
+
type DevframeServiceOf<ID> = ID extends keyof DevframeServicesRegistry ? DevframeServicesRegistry[ID] : unknown;
|
|
837
|
+
/**
|
|
838
|
+
* Augmentation point mapping a service's npm package name to the RPC scope
|
|
839
|
+
* namespace it registers its functions under, so a client's
|
|
840
|
+
* `services.get('@devframes/service-x')` returns a scoped RPC surface typed
|
|
841
|
+
* against that namespace. Service packages contribute their entry via
|
|
842
|
+
* declaration merging:
|
|
763
843
|
*
|
|
764
|
-
*
|
|
844
|
+
* ```ts
|
|
845
|
+
* declare module 'devframe' {
|
|
846
|
+
* interface DevframeServicesScopeRegistry {
|
|
847
|
+
* '@devframes/service-open': 'devframes:service:open'
|
|
848
|
+
* }
|
|
849
|
+
* }
|
|
850
|
+
* ```
|
|
765
851
|
*/
|
|
766
|
-
interface
|
|
767
|
-
|
|
768
|
-
|
|
852
|
+
interface DevframeServicesScopeRegistry {}
|
|
853
|
+
/** Resolved RPC scope namespace for a service package name, or `string`. */
|
|
854
|
+
type DevframeServiceScopeOf<PKG> = PKG extends keyof DevframeServicesScopeRegistry ? DevframeServicesScopeRegistry[PKG] & string : string;
|
|
855
|
+
/**
|
|
856
|
+
* A **wire service** — a shared server-side capability (e.g. open-in-editor,
|
|
857
|
+
* syntax highlighting) packaged so any devframe host can install it once and
|
|
858
|
+
* every plugin/client can consume it without re-implementing or re-bundling
|
|
859
|
+
* it. Contrast with plain {@link DevframeServicesHost.provide}, which shares
|
|
860
|
+
* an in-process object between plugins on the node side only: a
|
|
861
|
+
* `DevframeServiceDefinition` additionally registers RPC functions under its
|
|
862
|
+
* {@link DevframeServiceDefinition.scope} and is **advertised to clients**
|
|
863
|
+
* through the reactive `devframe:services` shared state, so browser UIs can
|
|
864
|
+
* feature-detect it (`ctx.services.has(pkg)`) and degrade gracefully.
|
|
865
|
+
*
|
|
866
|
+
* Ship one per npm package (`@devframes/service-<slug>` for first-party),
|
|
867
|
+
* with the package's default export being the `create<X>Service` factory —
|
|
868
|
+
* never a pre-built instance.
|
|
869
|
+
*/
|
|
870
|
+
interface DevframeServiceDefinition<API = unknown, Options = any> {
|
|
769
871
|
/**
|
|
770
|
-
*
|
|
771
|
-
* (`
|
|
772
|
-
* `pipeTo` consumption. The sink's `signal` aborts when every subscriber
|
|
773
|
-
* disconnects or cancels.
|
|
872
|
+
* The npm package name this service ships in — also its registry key
|
|
873
|
+
* (`ctx.services.has('@devframes/service-x')` on both node and client).
|
|
774
874
|
*/
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
875
|
+
package: string;
|
|
876
|
+
/** Semver of the service — advertised to clients, checked against descriptor ranges. */
|
|
877
|
+
version: string;
|
|
778
878
|
/**
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
* Node-stream interop: convert a `Readable` with `Readable.toWeb(node)`
|
|
783
|
-
* before passing it here.
|
|
879
|
+
* RPC namespace the service's functions register under, following the
|
|
880
|
+
* plugin id grammar (e.g. `devframes:service:open`). `setup` receives a
|
|
881
|
+
* context pre-scoped to it, so functions register with bare names.
|
|
784
882
|
*/
|
|
785
|
-
|
|
786
|
-
id?: string;
|
|
787
|
-
}) => Promise<StreamSink<T>>;
|
|
788
|
-
/** Look up an active stream by id. Returns `undefined` if none. */
|
|
789
|
-
get: (id: string) => StreamSink<T> | undefined;
|
|
790
|
-
/** All active outbound stream ids on this channel. */
|
|
791
|
-
ids: () => string[];
|
|
883
|
+
scope: string;
|
|
792
884
|
/**
|
|
793
|
-
*
|
|
794
|
-
*
|
|
795
|
-
* the client writes chunks. Typical pattern is to call this from an
|
|
796
|
-
* action handler, kick off background processing, and return the id
|
|
797
|
-
* so the caller can start uploading:
|
|
798
|
-
*
|
|
799
|
-
* ```ts
|
|
800
|
-
* handler: async () => {
|
|
801
|
-
* const reader = channel.openInbound()
|
|
802
|
-
* ;(async () => {
|
|
803
|
-
* for await (const chunk of reader) processChunk(chunk)
|
|
804
|
-
* })()
|
|
805
|
-
* return { uploadId: reader.id }
|
|
806
|
-
* }
|
|
807
|
-
* ```
|
|
808
|
-
*
|
|
809
|
-
* Calling `reader.cancel()` on the server sends an `upload-cancel` to
|
|
810
|
-
* the uploading client, which aborts its sink.
|
|
885
|
+
* Extra advertised metadata (feature flags, defaults, …). Must be
|
|
886
|
+
* JSON-serializable — it is mirrored to every client.
|
|
811
887
|
*/
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
888
|
+
meta?: Record<string, unknown>;
|
|
889
|
+
/**
|
|
890
|
+
* This instance's own option set (usually baked in by the factory that
|
|
891
|
+
* created the definition). Joins the merge alongside every declarative
|
|
892
|
+
* descriptor's `options`.
|
|
893
|
+
*/
|
|
894
|
+
options?: Options;
|
|
895
|
+
/**
|
|
896
|
+
* Merge the option sets contributed by multiple installers (in declaration
|
|
897
|
+
* order) into the one bag passed to `setup`. Defaults to a shallow merge
|
|
898
|
+
* where later sets win.
|
|
899
|
+
*/
|
|
900
|
+
mergeOptions?: (sets: Options[]) => Options;
|
|
901
|
+
/**
|
|
902
|
+
* Construct the service: register its RPC functions on the pre-scoped
|
|
903
|
+
* context and return its **node API** — the in-process surface other
|
|
904
|
+
* plugins get from `ctx.services.get(package)` (no RPC hop server-side).
|
|
905
|
+
* `info.options` carries every installer's option sets merged at the
|
|
906
|
+
* `ready()` barrier (via {@link mergeOptions} when present, otherwise a
|
|
907
|
+
* shallow merge in declaration order — later sets win).
|
|
908
|
+
*/
|
|
909
|
+
setup: (ctx: DevframeScopedNodeContext, info: {
|
|
910
|
+
options?: Options;
|
|
911
|
+
}) => API | Promise<API>;
|
|
815
912
|
}
|
|
816
913
|
/**
|
|
817
|
-
*
|
|
818
|
-
*
|
|
819
|
-
*
|
|
914
|
+
* Declarative reference to a service package — the form a
|
|
915
|
+
* {@link DevframeServiceInput} takes when the installer doesn't hold the
|
|
916
|
+
* factory itself (e.g. `DevframeDefinition.services`). The host imports the
|
|
917
|
+
* package's default-export factory and installs the resulting definition at
|
|
918
|
+
* the `ready()` barrier.
|
|
820
919
|
*/
|
|
821
|
-
interface
|
|
920
|
+
interface DevframeServiceDescriptor<Options = any> {
|
|
921
|
+
/** npm package name of the service (its default export is the factory). */
|
|
922
|
+
package: string;
|
|
822
923
|
/**
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
924
|
+
* Accepted semver range for the installed service. An unsatisfied range
|
|
925
|
+
* warns (`DF0069`) — or throws (`DF0068`) when {@link required} — while the
|
|
926
|
+
* service still installs; the advertised meta carries the real version.
|
|
826
927
|
*/
|
|
827
|
-
|
|
928
|
+
version?: string;
|
|
828
929
|
/**
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
930
|
+
* Fail hard when the service can't be imported or its version range isn't
|
|
931
|
+
* satisfied. By default a missing service is skipped silently — clients see
|
|
932
|
+
* `has() === false` and degrade.
|
|
832
933
|
*
|
|
833
|
-
* @
|
|
934
|
+
* @default false
|
|
834
935
|
*/
|
|
835
|
-
|
|
936
|
+
required?: boolean;
|
|
937
|
+
/** Option set this installer contributes to the merge. */
|
|
938
|
+
options?: Options;
|
|
836
939
|
}
|
|
837
|
-
//#endregion
|
|
838
|
-
//#region src/types/diagnostics.d.ts
|
|
839
940
|
/**
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
* integrations.
|
|
941
|
+
* What can be passed to `ctx.services.install()` (and listed in
|
|
942
|
+
* `DevframeDefinition.services`): a declarative {@link DevframeServiceDescriptor}
|
|
943
|
+
* (the host imports the factory) or a ready {@link DevframeServiceDefinition}
|
|
944
|
+
* (the installer already called the factory — its `options` join the merge).
|
|
845
945
|
*/
|
|
846
|
-
type
|
|
946
|
+
type DevframeServiceInput<API = unknown, Options = any> = DevframeServiceDescriptor<Options> | DevframeServiceDefinition<API, Options>;
|
|
847
947
|
/**
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
* registry without taking a direct dependency on `nostics`.
|
|
851
|
-
*
|
|
852
|
-
* Typical usage from a plugin's `setup(ctx)`:
|
|
853
|
-
*
|
|
854
|
-
* ```ts
|
|
855
|
-
* const myDiagnostics = ctx.diagnostics.defineDiagnostics({
|
|
856
|
-
* docsBase: 'https://example.com/errors',
|
|
857
|
-
* codes: {
|
|
858
|
-
* MYP0001: { why: 'Something went wrong' },
|
|
859
|
-
* },
|
|
860
|
-
* })
|
|
861
|
-
* ctx.diagnostics.register(myDiagnostics)
|
|
862
|
-
*
|
|
863
|
-
* // Through the shared lookup (loose typing):
|
|
864
|
-
* throw ctx.diagnostics.logger.MYP0001()
|
|
865
|
-
*
|
|
866
|
-
* // Or directly on the typed handle returned from `defineDiagnostics`:
|
|
867
|
-
* throw myDiagnostics.MYP0001()
|
|
868
|
-
* ```
|
|
948
|
+
* One service's advertisement entry, mirrored to clients through the
|
|
949
|
+
* `devframe:services` shared state.
|
|
869
950
|
*/
|
|
870
|
-
interface
|
|
951
|
+
interface DevframeServiceMeta {
|
|
952
|
+
/** npm package name — the registry key. */
|
|
953
|
+
package: string;
|
|
954
|
+
/** Installed version of the service. */
|
|
955
|
+
version: string;
|
|
956
|
+
/** RPC namespace its functions live under. */
|
|
957
|
+
scope: string;
|
|
958
|
+
/** Extra service-declared metadata. */
|
|
959
|
+
meta?: Record<string, unknown>;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Shape of the `devframe:services` shared state: package name → advertisement.
|
|
963
|
+
*/
|
|
964
|
+
type DevframeServicesState = Record<string, DevframeServiceMeta>;
|
|
965
|
+
interface DevframeServicesHost {
|
|
871
966
|
/**
|
|
872
|
-
*
|
|
873
|
-
*
|
|
874
|
-
* diagnostic and routes it through registered reporters; prefix with
|
|
875
|
-
* `throw` to raise. Loosely typed — for autocompletion, keep a reference
|
|
876
|
-
* to the typed result of `defineDiagnostics()` instead.
|
|
967
|
+
* Publish a service under a namespaced id. Throws `DF0037` when the id is
|
|
968
|
+
* already provided (revoke first to replace). Returns a revoke function.
|
|
877
969
|
*/
|
|
878
|
-
|
|
970
|
+
provide: <ID extends DevframeServiceId>(id: ID, service: DevframeServiceOf<ID>) => () => void;
|
|
971
|
+
/** The service currently provided under `id`, or `undefined`. */
|
|
972
|
+
get: <ID extends DevframeServiceId>(id: ID) => DevframeServiceOf<ID> | undefined;
|
|
973
|
+
has: (id: DevframeServiceId) => boolean;
|
|
879
974
|
/**
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
* factory pre-wires the host's ANSI console reporter.
|
|
975
|
+
* Run `callback` with the service as soon as it is available — immediately
|
|
976
|
+
* when already provided, otherwise on `provide`. Survives provider/consumer
|
|
977
|
+
* setup-order differences. The callback also re-fires if the service is
|
|
978
|
+
* revoked and provided again. Returns an unsubscribe function.
|
|
885
979
|
*/
|
|
886
|
-
|
|
980
|
+
whenAvailable: <ID extends DevframeServiceId>(id: ID, callback: (service: DevframeServiceOf<ID>) => void) => () => void;
|
|
981
|
+
/** Ids of every currently-provided service. */
|
|
982
|
+
keys: () => string[];
|
|
887
983
|
/**
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
891
|
-
*
|
|
892
|
-
|
|
893
|
-
|
|
984
|
+
* Install a **wire service** (see {@link DevframeServiceDefinition}).
|
|
985
|
+
* Before {@link DevframeServicesHost.ready} fires, installs are queued —
|
|
986
|
+
* option sets from every installer accumulate and each service is
|
|
987
|
+
* constructed **once** at the barrier with the merged options; the
|
|
988
|
+
* returned promise resolves with the service's node API then (or
|
|
989
|
+
* `undefined` when an optional descriptor's package can't be imported).
|
|
990
|
+
* After the barrier, installs construct immediately; installing an
|
|
991
|
+
* already-installed package returns the existing API (a warning is
|
|
992
|
+
* emitted when the late install carried options, since they're ignored).
|
|
993
|
+
*
|
|
994
|
+
* `resolveFrom` is where a descriptor's package resolves **from**: a path
|
|
995
|
+
* or file URL (e.g. `import.meta.url`), or an npm package name — typically
|
|
996
|
+
* the declaring plugin's `packageName`, so its declared services resolve
|
|
997
|
+
* against the plugin's own dependencies. Falls back to the context's
|
|
998
|
+
* `workspaceRoot`.
|
|
999
|
+
*/
|
|
1000
|
+
install: <API = unknown, Options = any>(input: DevframeServiceInput<API, Options>, options?: {
|
|
1001
|
+
resolveFrom?: string | null;
|
|
1002
|
+
}) => Promise<API | undefined>;
|
|
1003
|
+
/**
|
|
1004
|
+
* Fire the collect-then-setup barrier: resolve every queued descriptor
|
|
1005
|
+
* (importing its package), merge option sets per service, construct each
|
|
1006
|
+
* service once, `provide()` its node API under the package name, and
|
|
1007
|
+
* advertise it to clients via the `devframe:services` shared state.
|
|
1008
|
+
* Idempotent — adapters call it once after every devframe's `setup` has
|
|
1009
|
+
* run; a repeat returns the same promise. Rejects when a `required`
|
|
1010
|
+
* service fails to import or misses its version range.
|
|
1011
|
+
*/
|
|
1012
|
+
ready: () => Promise<void>;
|
|
894
1013
|
}
|
|
895
1014
|
//#endregion
|
|
896
|
-
//#region src/types/
|
|
897
|
-
|
|
1015
|
+
//#region src/types/rpc-augments.d.ts
|
|
1016
|
+
/**
|
|
1017
|
+
* To be extended
|
|
1018
|
+
*/
|
|
1019
|
+
interface DevframeRpcClientFunctions {
|
|
898
1020
|
/**
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
* `
|
|
902
|
-
*
|
|
903
|
-
*
|
|
904
|
-
* for build snapshots) — the shared engine in
|
|
905
|
-
* `devframe/utils/serve-static` accepts either shape.
|
|
1021
|
+
* Server→client notification that this connection's auth token has been
|
|
1022
|
+
* revoked. The client drops to untrusted on receipt. Broadcast by
|
|
1023
|
+
* `revokeActiveConnectionsForToken`.
|
|
1024
|
+
*
|
|
1025
|
+
* @internal
|
|
906
1026
|
*/
|
|
907
|
-
|
|
1027
|
+
'devframe:auth:revoked': () => Promise<void>;
|
|
908
1028
|
/**
|
|
909
|
-
*
|
|
910
|
-
*
|
|
911
|
-
* via `connectDevframe()`'s relative `./__connection.json` fetch.
|
|
1029
|
+
* Streaming chunk pushed from server to subscribed clients. Wired by
|
|
1030
|
+
* `RpcStreamingHost`; do not register manually.
|
|
912
1031
|
*
|
|
913
|
-
*
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
*
|
|
1032
|
+
* @internal
|
|
1033
|
+
*/
|
|
1034
|
+
'devframe:streaming:chunk': (channel: string, id: string, seq: number, chunk: any) => Promise<void>;
|
|
1035
|
+
/**
|
|
1036
|
+
* Streaming terminator pushed from server to subscribed clients. Wired by
|
|
1037
|
+
* `RpcStreamingHost`; do not register manually.
|
|
918
1038
|
*
|
|
919
|
-
*
|
|
920
|
-
* `distDir` yet omits this hook triggers a `DF8106` diagnostic, since the
|
|
921
|
-
* SPA's `./__connection.json` fetch would otherwise fall through and break
|
|
922
|
-
* silently. A static-snapshot host that bakes the meta into its served files
|
|
923
|
-
* can implement it as a no-op to acknowledge this intentionally.
|
|
1039
|
+
* @internal
|
|
924
1040
|
*/
|
|
925
|
-
|
|
1041
|
+
'devframe:streaming:end': (channel: string, id: string, error?: {
|
|
1042
|
+
name: string;
|
|
1043
|
+
message: string;
|
|
1044
|
+
}) => Promise<void>;
|
|
926
1045
|
/**
|
|
927
|
-
*
|
|
928
|
-
* `
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
* reasonable value.
|
|
1046
|
+
* Server→client cancel for an in-flight upload. Wired by
|
|
1047
|
+
* `RpcStreamingHost`; do not register manually.
|
|
1048
|
+
*
|
|
1049
|
+
* @internal
|
|
932
1050
|
*/
|
|
933
|
-
|
|
1051
|
+
'devframe:streaming:upload-cancel': (channel: string, id: string) => Promise<void>;
|
|
934
1052
|
/**
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
* collide between, say, the Vite host (`.vite/devframe`) and a
|
|
938
|
-
* standalone CLI host (`.<appName>/devframe`).
|
|
1053
|
+
* Full shared-state snapshot pushed from server to subscribed clients.
|
|
1054
|
+
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
939
1055
|
*
|
|
940
|
-
*
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* `${cwd}/node_modules/.<appName>/devframe/`, which version
|
|
947
|
-
* control ignores.
|
|
948
|
-
* - `global` — per-user state (auth tokens, machine-wide
|
|
949
|
-
* preferences). Typically under
|
|
950
|
-
* `${homedir()}/.<appName>/devframe/`.
|
|
1056
|
+
* @internal
|
|
1057
|
+
*/
|
|
1058
|
+
'devframe:rpc:client-state:updated': (key: string, fullState: any, syncId: string) => Promise<void>;
|
|
1059
|
+
/**
|
|
1060
|
+
* Incremental shared-state patch pushed from server to subscribed clients.
|
|
1061
|
+
* Wired by `RpcSharedStateHost`; do not register manually.
|
|
951
1062
|
*
|
|
952
|
-
*
|
|
953
|
-
* pass to a downstream `createStorage(...)` call that creates it
|
|
954
|
-
* lazily.
|
|
1063
|
+
* @internal
|
|
955
1064
|
*/
|
|
956
|
-
|
|
1065
|
+
'devframe:rpc:client-state:patch': (key: string, patches: any[], syncId: string) => Promise<void>;
|
|
957
1066
|
}
|
|
958
1067
|
/**
|
|
959
|
-
*
|
|
960
|
-
* `workspace` is committable and team-shared, `project` is per-checkout
|
|
961
|
-
* private, `global` is per-user.
|
|
1068
|
+
* To be extended
|
|
962
1069
|
*/
|
|
963
|
-
|
|
964
|
-
//#endregion
|
|
965
|
-
//#region src/types/views.d.ts
|
|
966
|
-
interface DevframeViewHost {
|
|
1070
|
+
interface DevframeRpcServerFunctions {
|
|
967
1071
|
/**
|
|
1072
|
+
* Authenticate a connection with a previously-issued bearer token; resolves
|
|
1073
|
+
* whether the connection is now trusted. The interactive handler is provided
|
|
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
|
+
*
|
|
968
1081
|
* @internal
|
|
969
1082
|
*/
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1083
|
+
'anonymous:devframe:auth': (params: {
|
|
1084
|
+
authToken: string;
|
|
1085
|
+
ua: string;
|
|
1086
|
+
origin: string;
|
|
1087
|
+
}) => Promise<{
|
|
1088
|
+
isTrusted: boolean;
|
|
1089
|
+
}>;
|
|
974
1090
|
/**
|
|
975
|
-
*
|
|
976
|
-
* -
|
|
977
|
-
*
|
|
1091
|
+
* Exchange a one-time authentication code (shown by the dev server) for a fresh,
|
|
1092
|
+
* node-issued bearer token, returning the token on success or `null`. The
|
|
1093
|
+
* handler is provided by the host adapter on top of `exchangeTempAuthCode`.
|
|
978
1094
|
*
|
|
979
|
-
*
|
|
980
|
-
*
|
|
1095
|
+
* Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
|
|
1096
|
+
* reachable before the connection is trusted.
|
|
1097
|
+
*
|
|
1098
|
+
* @internal
|
|
981
1099
|
*/
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
* `Registry` (`<NS>:<T>`). Indexing the plain registry behind a `keyof`
|
|
990
|
-
* guard keeps TypeScript from rejecting a generic index into the
|
|
991
|
-
* key-remapped `Scoped*Functions<NS>` maps (TS2536) once a plugin augments
|
|
992
|
-
* the registry, while preserving each name's argument/return typing.
|
|
993
|
-
*/
|
|
994
|
-
type ScopedRpcFn<Registry, NS extends string, T extends string> = `${NS}:${T}` extends keyof Registry ? Extract<Registry[`${NS}:${T}`], AnyRpcFn> : AnyRpcFn;
|
|
995
|
-
/**
|
|
996
|
-
* Augmentable registry mapping a scope namespace to the shape of its
|
|
997
|
-
* persisted settings. Tools augment it so `ctx.scope('my-plugin')`
|
|
998
|
-
* gets a fully-typed `settings.global` / `settings.project`:
|
|
999
|
-
*
|
|
1000
|
-
* ```ts
|
|
1001
|
-
* declare module 'devframe' {
|
|
1002
|
-
* interface DevframeSettingsRegistry {
|
|
1003
|
-
* 'my-plugin': { theme: 'light' | 'dark', recentFiles: string[] }
|
|
1004
|
-
* }
|
|
1005
|
-
* }
|
|
1006
|
-
* ```
|
|
1007
|
-
*/
|
|
1008
|
-
interface DevframeSettingsRegistry {}
|
|
1009
|
-
/**
|
|
1010
|
-
* Resolve the settings shape for a namespace from
|
|
1011
|
-
* {@link DevframeSettingsRegistry}, falling back to an open record when
|
|
1012
|
-
* the namespace hasn't been augmented.
|
|
1013
|
-
*/
|
|
1014
|
-
type SettingsForNamespace<NS extends string> = NS extends keyof DevframeSettingsRegistry ? DevframeSettingsRegistry[NS] extends Record<string, any> ? DevframeSettingsRegistry[NS] : Record<string, any> : Record<string, any>;
|
|
1015
|
-
/**
|
|
1016
|
-
* A persisted key-value settings store for one scope (`global` or
|
|
1017
|
-
* `project`). Backed by a file on the node side and by the shared-state
|
|
1018
|
-
* sync protocol on the client, so a `set` on either side propagates to
|
|
1019
|
-
* every connected peer and survives restarts.
|
|
1020
|
-
*
|
|
1021
|
-
* Every method is async because the underlying shared state is resolved
|
|
1022
|
-
* lazily on first access.
|
|
1023
|
-
*/
|
|
1024
|
-
interface DevframeSettingsStore<T extends Record<string, any> = Record<string, any>> {
|
|
1025
|
-
/** Read a single setting. Resolves to `undefined` when unset. */
|
|
1026
|
-
get: <K extends keyof T>(key: K) => Promise<T[K] | undefined>;
|
|
1027
|
-
/** Write a single setting. */
|
|
1028
|
-
set: <K extends keyof T>(key: K, value: T[K]) => Promise<void>;
|
|
1029
|
-
/** Remove a single setting. */
|
|
1030
|
-
delete: <K extends keyof T>(key: K) => Promise<void>;
|
|
1031
|
-
/** Read the whole settings object (immutable snapshot). */
|
|
1032
|
-
all: () => Promise<Readonly<T>>;
|
|
1100
|
+
'anonymous:devframe:auth:exchange': (params: {
|
|
1101
|
+
code: string;
|
|
1102
|
+
ua: string;
|
|
1103
|
+
origin: string;
|
|
1104
|
+
}) => Promise<{
|
|
1105
|
+
authToken: string | null;
|
|
1106
|
+
}>;
|
|
1033
1107
|
/**
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1108
|
+
* Self-revoke: the caller asks the server to revoke its own bearer token
|
|
1109
|
+
* (if any) and drop to untrusted. Requires an already-trusted caller, so
|
|
1110
|
+
* unlike the two handshake methods above it does **not** carry the
|
|
1111
|
+
* `anonymous:` prefix. Registered by `recipes/interactive-auth`.
|
|
1112
|
+
*
|
|
1113
|
+
* @internal
|
|
1036
1114
|
*/
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1115
|
+
'devframe:auth:revoke': () => Promise<void>;
|
|
1116
|
+
/**
|
|
1117
|
+
* Subscribe a client to a shared-state key. Wired by
|
|
1118
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
1119
|
+
*
|
|
1120
|
+
* @internal
|
|
1121
|
+
*/
|
|
1122
|
+
'devframe:rpc:server-state:subscribe': (key: string) => Promise<void>;
|
|
1123
|
+
/**
|
|
1124
|
+
* Read the current value for a shared-state key. Wired by
|
|
1125
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
1126
|
+
*
|
|
1127
|
+
* @internal
|
|
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.
|
|
1133
|
+
*
|
|
1134
|
+
* @internal
|
|
1135
|
+
*/
|
|
1136
|
+
'devframe:rpc:server-state:set': (key: string, value: any, syncId: string) => Promise<void>;
|
|
1137
|
+
/**
|
|
1138
|
+
* Apply a patch to a shared-state value (from the client). Wired by
|
|
1139
|
+
* `RpcSharedStateHost`; do not register manually.
|
|
1140
|
+
*
|
|
1141
|
+
* @internal
|
|
1142
|
+
*/
|
|
1143
|
+
'devframe:rpc:server-state:patch': (key: string, patches: any[], syncId: string) => Promise<void>;
|
|
1144
|
+
/**
|
|
1145
|
+
* Client→server streaming subscription with optional replay cursor.
|
|
1146
|
+
* Wired by `RpcStreamingHost`; do not register manually.
|
|
1147
|
+
*
|
|
1148
|
+
* @internal
|
|
1149
|
+
*/
|
|
1150
|
+
'devframe:streaming:subscribe': (channel: string, id: string, opts?: {
|
|
1151
|
+
afterSeq?: number;
|
|
1152
|
+
}) => Promise<void>;
|
|
1153
|
+
/**
|
|
1154
|
+
* Client→server streaming unsubscribe. Wired by `RpcStreamingHost`;
|
|
1155
|
+
* do not register manually.
|
|
1156
|
+
*
|
|
1157
|
+
* @internal
|
|
1158
|
+
*/
|
|
1159
|
+
'devframe:streaming:unsubscribe': (channel: string, id: string) => Promise<void>;
|
|
1160
|
+
/**
|
|
1161
|
+
* Client→server streaming cancellation request. Wired by
|
|
1162
|
+
* `RpcStreamingHost`; do not register manually.
|
|
1163
|
+
*
|
|
1164
|
+
* @internal
|
|
1165
|
+
*/
|
|
1166
|
+
'devframe:streaming:cancel': (channel: string, id: string) => Promise<void>;
|
|
1167
|
+
/**
|
|
1168
|
+
* Client→server upload chunk. Wired by `RpcStreamingHost`; do not
|
|
1169
|
+
* register manually.
|
|
1170
|
+
*
|
|
1171
|
+
* @internal
|
|
1172
|
+
*/
|
|
1173
|
+
'devframe:streaming:upload-chunk': (channel: string, id: string, seq: number, chunk: any) => Promise<void>;
|
|
1174
|
+
/**
|
|
1175
|
+
* Client→server upload terminator. Wired by `RpcStreamingHost`; do not
|
|
1176
|
+
* register manually.
|
|
1177
|
+
*
|
|
1178
|
+
* @internal
|
|
1179
|
+
*/
|
|
1180
|
+
'devframe:streaming:upload-end': (channel: string, id: string, error?: {
|
|
1181
|
+
name: string;
|
|
1182
|
+
message: string;
|
|
1183
|
+
}) => Promise<void>;
|
|
1184
|
+
}
|
|
1068
1185
|
/**
|
|
1069
|
-
*
|
|
1186
|
+
* To be extended
|
|
1070
1187
|
*/
|
|
1071
|
-
interface
|
|
1188
|
+
interface DevframeRpcSharedStates {
|
|
1189
|
+
/**
|
|
1190
|
+
* Wire-service advertisements — package name → `{ package, version,
|
|
1191
|
+
* scope, meta }` for every installed {@link import('./services').DevframeServiceDefinition}.
|
|
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.
|
|
1195
|
+
*/
|
|
1196
|
+
'devframe:services': DevframeServicesState;
|
|
1197
|
+
}
|
|
1198
|
+
//#endregion
|
|
1199
|
+
//#region src/types/rpc.d.ts
|
|
1200
|
+
interface DevframeNodeRpcSession {
|
|
1201
|
+
meta: DevframeNodeRpcSessionMeta;
|
|
1202
|
+
rpc: BirpcReturn<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>;
|
|
1203
|
+
}
|
|
1204
|
+
interface RpcBroadcastOptions<METHOD, Args extends any[]> {
|
|
1072
1205
|
method: METHOD;
|
|
1073
1206
|
args: Args;
|
|
1074
1207
|
optional?: boolean;
|
|
1075
1208
|
event?: boolean;
|
|
1209
|
+
filter?: (client: BirpcReturn<DevframeRpcClientFunctions, DevframeRpcServerFunctions, false>) => boolean | void;
|
|
1076
1210
|
}
|
|
1077
|
-
|
|
1078
|
-
* Node-side streaming host scoped to a namespace. Channel names are
|
|
1079
|
-
* auto-prefixed with `<namespace>:`.
|
|
1080
|
-
*/
|
|
1081
|
-
interface DevframeScopedStreamingHost {
|
|
1211
|
+
type RpcFunctionsHost = RpcFunctionsCollectorBase<DevframeRpcServerFunctions, DevframeNodeContext> & {
|
|
1082
1212
|
/**
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1213
|
+
* Invoke a locally registered server RPC function directly.
|
|
1214
|
+
*
|
|
1215
|
+
* This bypasses transport and is useful for server-side cross-function calls.
|
|
1086
1216
|
*/
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1089
|
-
/**
|
|
1090
|
-
* The scoped node RPC surface exposed on `ctx.scope('my-plugin').rpc`.
|
|
1091
|
-
* IDs and keys you pass are auto-namespaced with `my-plugin:`.
|
|
1092
|
-
*/
|
|
1093
|
-
interface DevframeScopedNodeRpc<NS extends string = string> {
|
|
1094
|
-
/** The namespace this surface is scoped to. */
|
|
1095
|
-
readonly namespace: NS;
|
|
1217
|
+
invokeLocal: <T extends keyof DevframeRpcServerFunctions, Args extends Parameters<DevframeRpcServerFunctions[T]>>(method: T, ...args: Args) => Promise<Awaited<ReturnType<DevframeRpcServerFunctions[T]>>>;
|
|
1096
1218
|
/**
|
|
1097
|
-
*
|
|
1098
|
-
* (no `:`); it is stored as `<namespace>:<name>`. Throws `DF0034` if an
|
|
1099
|
-
* already-namespaced name is passed.
|
|
1219
|
+
* Broadcast a message to all connected clients
|
|
1100
1220
|
*/
|
|
1101
|
-
|
|
1221
|
+
broadcast: <T extends keyof DevframeRpcClientFunctions, Args extends Parameters<DevframeRpcClientFunctions[T]>>(options: RpcBroadcastOptions<T, Args>) => Promise<void>;
|
|
1102
1222
|
/**
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1223
|
+
* Get the current RPC client
|
|
1224
|
+
*
|
|
1225
|
+
* Available in RPC functions to get the current RPC client
|
|
1105
1226
|
*/
|
|
1106
|
-
|
|
1227
|
+
getCurrentRpcSession: () => DevframeNodeRpcSession | undefined;
|
|
1107
1228
|
/**
|
|
1108
|
-
*
|
|
1109
|
-
* resolved within this namespace; pass a fully-qualified name
|
|
1110
|
-
* (containing `:`) to call another scope's function.
|
|
1229
|
+
* The shared state host
|
|
1111
1230
|
*/
|
|
1112
|
-
|
|
1113
|
-
<T extends keyof ScopedServerFunctions<NS> & string>(method: T, ...args: Parameters<ScopedRpcFn<DevframeRpcServerFunctions, NS, T>>): Promise<Awaited<ReturnType<ScopedRpcFn<DevframeRpcServerFunctions, NS, T>>>>;
|
|
1114
|
-
<T extends keyof DevframeRpcServerFunctions & string>(method: T, ...args: Parameters<Extract<DevframeRpcServerFunctions[T], AnyRpcFn>>): Promise<Awaited<ReturnType<Extract<DevframeRpcServerFunctions[T], AnyRpcFn>>>>;
|
|
1115
|
-
(method: string, ...args: any[]): Promise<any>;
|
|
1116
|
-
};
|
|
1231
|
+
sharedState: RpcSharedStateHost;
|
|
1117
1232
|
/**
|
|
1118
|
-
*
|
|
1119
|
-
*
|
|
1233
|
+
* The streaming channel host. Provides per-channel `start()` /
|
|
1234
|
+
* `pipeFrom()` producers; clients consume via `rpc.streaming.subscribe()`.
|
|
1235
|
+
*
|
|
1236
|
+
* @see RpcStreamingHost
|
|
1120
1237
|
*/
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1238
|
+
streaming: RpcStreamingHost;
|
|
1239
|
+
};
|
|
1240
|
+
interface RpcSharedStateGetOptions<T> {
|
|
1241
|
+
sharedState?: SharedState<T>;
|
|
1242
|
+
initialValue?: T;
|
|
1243
|
+
}
|
|
1244
|
+
interface RpcSharedStateHost {
|
|
1245
|
+
get: <T extends object = any>(key: string, options?: RpcSharedStateGetOptions<T>) => Promise<SharedState<T>>;
|
|
1246
|
+
keys: () => string[];
|
|
1125
1247
|
/**
|
|
1126
|
-
*
|
|
1127
|
-
*
|
|
1248
|
+
* Subscribe to new shared-state keys becoming available. Fires when
|
|
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.
|
|
1128
1252
|
*/
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1253
|
+
onKeyAdded: (fn: (key: string) => void) => () => void;
|
|
1254
|
+
/**
|
|
1255
|
+
* Unregister a shared state and drop its broadcast listeners. Returns
|
|
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.
|
|
1259
|
+
*/
|
|
1260
|
+
delete: (key: string) => boolean;
|
|
1137
1261
|
}
|
|
1138
1262
|
/**
|
|
1139
|
-
*
|
|
1140
|
-
* `ctx.scope('my-plugin')`. Re-exposes the unscoped surfaces (`views`,
|
|
1141
|
-
* `diagnostics`, `agent`, …), replaces `rpc` with the auto-namespaced
|
|
1142
|
-
* {@link DevframeScopedNodeRpc}, and adds a top-level `settings` store.
|
|
1263
|
+
* Options for `RpcStreamingHost.create()`.
|
|
1143
1264
|
*/
|
|
1144
|
-
interface
|
|
1145
|
-
/** The namespace this context is scoped to. */
|
|
1146
|
-
readonly namespace: NS;
|
|
1147
|
-
/** The underlying unscoped context. */
|
|
1148
|
-
readonly base: DevframeNodeContext;
|
|
1149
|
-
readonly workspaceRoot: string;
|
|
1150
|
-
readonly cwd: string;
|
|
1151
|
-
readonly mode: 'dev' | 'build';
|
|
1152
|
-
host: DevframeHost;
|
|
1153
|
-
rpc: DevframeScopedNodeRpc<NS>;
|
|
1154
|
-
/** Persisted settings for this namespace (`global` + `project`). */
|
|
1155
|
-
settings: DevframeSettings<Settings>;
|
|
1156
|
-
views: DevframeViewHost;
|
|
1157
|
-
diagnostics: DevframeDiagnosticsHost;
|
|
1158
|
-
agent: DevframeAgentHost;
|
|
1265
|
+
interface RpcStreamingChannelOptions {
|
|
1159
1266
|
/**
|
|
1160
|
-
*
|
|
1161
|
-
*
|
|
1267
|
+
* Size of the per-stream ring buffer kept on the server for
|
|
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.
|
|
1162
1273
|
*/
|
|
1163
|
-
|
|
1274
|
+
replayWindow?: number;
|
|
1275
|
+
/**
|
|
1276
|
+
* Milliseconds a closed stream is retained on the server after its
|
|
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.
|
|
1284
|
+
*/
|
|
1285
|
+
closedStreamRetention?: number;
|
|
1164
1286
|
}
|
|
1165
|
-
//#endregion
|
|
1166
|
-
//#region src/types/services.d.ts
|
|
1167
1287
|
/**
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
* A provider ships a *types-only* augmentation of {@link DevframeServicesRegistry}
|
|
1173
|
-
* (so consumers get full typing from `import type`), then provides the
|
|
1174
|
-
* implementation at setup time:
|
|
1175
|
-
*
|
|
1176
|
-
* ```ts
|
|
1177
|
-
* // provider (e.g. the data-inspector plugin)
|
|
1178
|
-
* declare module 'devframe' {
|
|
1179
|
-
* interface DevframeServicesRegistry {
|
|
1180
|
-
* 'devframes:plugin:data-inspector:sources': DataSourcesService
|
|
1181
|
-
* }
|
|
1182
|
-
* }
|
|
1183
|
-
* ctx.services.provide('devframes:plugin:data-inspector:sources', host)
|
|
1184
|
-
*
|
|
1185
|
-
* // consumer (another plugin) — no runtime dependency on the provider
|
|
1186
|
-
* ctx.services.whenAvailable('devframes:plugin:data-inspector:sources', (sources) => {
|
|
1187
|
-
* sources.register({ id: 'my-plugin:state', title: 'My state', data: () => state })
|
|
1188
|
-
* })
|
|
1189
|
-
* ```
|
|
1288
|
+
* Channel handle returned by `ctx.rpc.streaming.create(name, opts)`. A
|
|
1289
|
+
* channel owns a wire namespace; calling `start()` produces individual
|
|
1290
|
+
* streams keyed by id.
|
|
1190
1291
|
*
|
|
1191
|
-
*
|
|
1192
|
-
* the providing plugin's id.
|
|
1193
|
-
*/
|
|
1194
|
-
/**
|
|
1195
|
-
* Augmentation point mapping service ids to their implementation types.
|
|
1196
|
-
* Providers extend this via `declare module 'devframe'`.
|
|
1292
|
+
* @see {@link https://devfra.me/guide/streaming Streaming guide}
|
|
1197
1293
|
*/
|
|
1198
|
-
interface
|
|
1294
|
+
interface RpcStreamingChannel<T = unknown> {
|
|
1295
|
+
/** Channel name as registered with `ctx.rpc.streaming.create()`. */
|
|
1296
|
+
readonly name: string;
|
|
1297
|
+
/**
|
|
1298
|
+
* Start a new stream. Returns a server-side sink with both an imperative
|
|
1299
|
+
* (`write` / `close` / `error`) surface and a `WritableStream<T>` for
|
|
1300
|
+
* `pipeTo` consumption. The sink's `signal` aborts when every subscriber
|
|
1301
|
+
* disconnects or cancels.
|
|
1302
|
+
*/
|
|
1303
|
+
start: (opts?: {
|
|
1304
|
+
id?: string;
|
|
1305
|
+
}) => StreamSink<T>;
|
|
1306
|
+
/**
|
|
1307
|
+
* Convenience: start a stream and pipe a `ReadableStream<T>` into it.
|
|
1308
|
+
* The pipe uses `sink.signal` so cancellation propagates upstream.
|
|
1309
|
+
*
|
|
1310
|
+
* Node-stream interop: convert a `Readable` with `Readable.toWeb(node)`
|
|
1311
|
+
* before passing it here.
|
|
1312
|
+
*/
|
|
1313
|
+
pipeFrom: (readable: ReadableStream<T>, opts?: {
|
|
1314
|
+
id?: string;
|
|
1315
|
+
}) => Promise<StreamSink<T>>;
|
|
1316
|
+
/** Look up an active stream by id. Returns `undefined` if none. */
|
|
1317
|
+
get: (id: string) => StreamSink<T> | undefined;
|
|
1318
|
+
/** All active outbound stream ids on this channel. */
|
|
1319
|
+
ids: () => string[];
|
|
1320
|
+
/**
|
|
1321
|
+
* Open an inbound stream — the server side of a client-to-server
|
|
1322
|
+
* upload. Allocates an id, returns a `StreamReader<T>` that fills as
|
|
1323
|
+
* the client writes chunks. Typical pattern is to call this from an
|
|
1324
|
+
* action handler, kick off background processing, and return the id
|
|
1325
|
+
* so the caller can start uploading:
|
|
1326
|
+
*
|
|
1327
|
+
* ```ts
|
|
1328
|
+
* handler: async () => {
|
|
1329
|
+
* const reader = channel.openInbound()
|
|
1330
|
+
* ;(async () => {
|
|
1331
|
+
* for await (const chunk of reader) processChunk(chunk)
|
|
1332
|
+
* })()
|
|
1333
|
+
* return { uploadId: reader.id }
|
|
1334
|
+
* }
|
|
1335
|
+
* ```
|
|
1336
|
+
*
|
|
1337
|
+
* Calling `reader.cancel()` on the server sends an `upload-cancel` to
|
|
1338
|
+
* the uploading client, which aborts its sink.
|
|
1339
|
+
*/
|
|
1340
|
+
openInbound: (opts?: {
|
|
1341
|
+
id?: string;
|
|
1342
|
+
}) => StreamReader<T>;
|
|
1343
|
+
}
|
|
1199
1344
|
/**
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1345
|
+
* Server-side streaming host. Lives on `ctx.rpc.streaming` alongside
|
|
1346
|
+
* `ctx.rpc.sharedState`. Each named channel owns its own stream registry
|
|
1347
|
+
* and wire namespace.
|
|
1202
1348
|
*/
|
|
1203
|
-
|
|
1204
|
-
/** Resolved service type for an id: augmented type, or `unknown`. */
|
|
1205
|
-
type DevframeServiceOf<ID> = ID extends keyof DevframeServicesRegistry ? DevframeServicesRegistry[ID] : unknown;
|
|
1206
|
-
interface DevframeServicesHost {
|
|
1349
|
+
interface RpcStreamingHost {
|
|
1207
1350
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
1351
|
+
* Register a streaming channel. Names follow the `<plugin-id>:<channel>`
|
|
1352
|
+
* convention (e.g. `'my-devtool:chat-stream'`). Throws `DF0032` if the
|
|
1353
|
+
* name is already taken.
|
|
1210
1354
|
*/
|
|
1211
|
-
|
|
1212
|
-
/** The service currently provided under `id`, or `undefined`. */
|
|
1213
|
-
get: <ID extends DevframeServiceId>(id: ID) => DevframeServiceOf<ID> | undefined;
|
|
1214
|
-
has: (id: DevframeServiceId) => boolean;
|
|
1355
|
+
create: <T = unknown>(name: string, opts?: RpcStreamingChannelOptions) => RpcStreamingChannel<T>;
|
|
1215
1356
|
/**
|
|
1216
|
-
*
|
|
1217
|
-
*
|
|
1218
|
-
*
|
|
1219
|
-
*
|
|
1357
|
+
* Adapters call this when a session disconnects so the host can drop
|
|
1358
|
+
* subscribers and abort orphaned streams. Most users do not need this;
|
|
1359
|
+
* it's wired by the RPC server binding automatically.
|
|
1360
|
+
*
|
|
1361
|
+
* @internal
|
|
1220
1362
|
*/
|
|
1221
|
-
|
|
1222
|
-
/** Ids of every currently-provided service. */
|
|
1223
|
-
keys: () => string[];
|
|
1363
|
+
_onSessionDisconnected: (meta: DevframeNodeRpcSessionMeta) => void;
|
|
1224
1364
|
}
|
|
1225
1365
|
//#endregion
|
|
1226
1366
|
//#region src/types/context.d.ts
|
|
@@ -1841,9 +1981,22 @@ interface DevframeDefinition {
|
|
|
1841
1981
|
dev?: boolean;
|
|
1842
1982
|
build?: boolean;
|
|
1843
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[];
|
|
1844
1997
|
/** Server-side setup — the primary entrypoint. Runs in every runtime. */
|
|
1845
1998
|
setup: (ctx: DevframeNodeContext, info?: DevframeSetupInfo) => void | Promise<void>;
|
|
1846
1999
|
cli?: DevframeCliOptions;
|
|
1847
2000
|
}
|
|
1848
2001
|
//#endregion
|
|
1849
|
-
export {
|
|
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 };
|