devframe 0.9.8 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_shared-CCzf9Hub.d.mts +83 -0
- package/dist/_shared-U8D-xzrp.mjs +115 -0
- package/dist/adapters/build.d.mts +6 -6
- package/dist/adapters/build.mjs +33 -36
- package/dist/adapters/cac.d.mts +12 -1
- package/dist/adapters/cac.mjs +8 -8
- package/dist/adapters/dev.d.mts +17 -17
- package/dist/adapters/dev.mjs +2 -2
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/initiate.d.mts +31 -28
- package/dist/adapters/initiate.mjs +1 -1
- package/dist/adapters/mcp.d.mts +28 -14
- package/dist/adapters/mcp.mjs +76 -40
- package/dist/{agent-tool-name-EgfoFO8C.mjs → agent-tool-name-DGzeuWcO.mjs} +2 -2
- package/dist/cli/main.d.mts +1 -1
- package/dist/cli/main.mjs +41 -17
- package/dist/client/index.d.mts +36 -36
- package/dist/client/index.mjs +90 -52
- package/dist/{client-assets-C7SXgTWY.mjs → client-assets-DNdhagGm.mjs} +2 -2
- package/dist/constants.d.mts +8 -7
- package/dist/constants.mjs +7 -6
- package/dist/{context-omlma_1n.mjs → context-B29Oh0oA.mjs} +166 -93
- package/dist/{context-DftYyXaR.d.mts → context-BJiKJqyW.d.mts} +5 -5
- package/dist/{context-CLF4-8Pp.d.mts → context-CHW4BZOj.d.mts} +203 -134
- package/dist/{context-KUqAfawQ.mjs → context-jXO5vzjH.mjs} +3 -3
- package/dist/{crypto-token-XCqTSMg9.mjs → crypto-token-TVbGGFuj.mjs} +1 -1
- package/dist/{dev-CzkPCJwi.mjs → dev-CRYHx7iG.mjs} +73 -40
- package/dist/{diagnostics-BXWW3VK-.mjs → diagnostics-DZA6KQ_Z.mjs} +32 -19
- package/dist/{events-4daw0uK9.mjs → events-BV4Dj59a.mjs} +2 -2
- package/dist/{services-install-CqqQVls5.mjs → host-agent-CKqSTEX9.mjs} +15 -151
- package/dist/{host-h3-fRbF9yor.mjs → host-h3-BROI6SQ4.mjs} +2 -2
- package/dist/in-page-channel/index.d.mts +14 -14
- package/dist/in-page-channel/index.mjs +63 -54
- package/dist/{index-D3k0BIhe.d.mts → index-CNC390b_.d.mts} +4 -4
- package/dist/{index-BzyopqxV.d.mts → index-pIVt92jP.d.mts} +9 -9
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +2 -2
- package/dist/{instance-registry-BjlMQbFB.mjs → instance-registry-DCq762YJ.mjs} +9 -9
- package/dist/{instance-shell-Tcp5hGoq.d.mts → instance-shell-DZipaP9k.d.mts} +18 -18
- package/dist/{instance-shell-CpVW4zG0.mjs → instance-shell-DvYCwrDy.mjs} +223 -67
- package/dist/internal/index.d.mts +45 -26
- package/dist/internal/index.mjs +10 -10
- 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 +5 -5
- package/dist/node/index.mjs +2 -2
- package/dist/{nostics-SPGDgHEP.d.mts → nostics-0sKe5HIj.d.mts} +2 -2
- package/dist/{nostics-CzECRXpE.mjs → nostics-D0PvLZsn.mjs} +1 -1
- package/dist/origin-DGDH8BI9.d.mts +45 -0
- package/dist/origin-DZit9g29.mjs +80 -0
- package/dist/recipes/common-rpc-functions.d.mts +5 -5
- package/dist/recipes/common-rpc-functions.mjs +3 -3
- package/dist/recipes/interactive-auth.d.mts +6 -6
- package/dist/recipes/interactive-auth.mjs +6 -6
- package/dist/{remote-assets-DezQmPQU.mjs → remote-assets-DMFzLSoH.mjs} +27 -7
- package/dist/{remote-assets-B13_vRkJ.d.mts → remote-assets-eBvSKZMS.d.mts} +7 -7
- package/dist/{revoke-COLW0cOi.mjs → revoke-cqztiCX4.mjs} +1 -1
- package/dist/rpc/dump.d.mts +19 -12
- package/dist/rpc/dump.mjs +2 -2
- package/dist/rpc/index.d.mts +2 -2
- package/dist/rpc/index.mjs +2 -3
- package/dist/rpc/transports/sse-client.d.mts +1 -1
- package/dist/rpc/transports/sse-client.mjs +30 -14
- package/dist/rpc/transports/sse-server.d.mts +4 -4
- package/dist/rpc/transports/sse-server.mjs +7 -7
- package/dist/rpc/transports/ws-bun.d.mts +5 -5
- package/dist/rpc/transports/ws-bun.mjs +8 -2
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +2 -2
- package/dist/rpc/transports/ws-deno.d.mts +4 -4
- package/dist/rpc/transports/ws-deno.mjs +8 -2
- package/dist/rpc/transports/ws-server.d.mts +2 -1
- package/dist/rpc/transports/ws-server.mjs +232 -1
- package/dist/{rpc-core-DlHd276T.mjs → rpc-core-CvkU23bQ.mjs} +14 -2
- package/dist/{rpc-core-B3W3z9ff.d.mts → rpc-core-DsyDViQy.d.mts} +4 -4
- package/dist/serialization-DFg5sUt6.mjs +116 -0
- package/dist/services-install-Ben4yh52.mjs +154 -0
- package/dist/session-DPfj6zQG.mjs +16 -0
- package/dist/{shared-state--sduc6Xi.mjs → shared-state-CaxB1q0h.mjs} +2 -2
- package/dist/{simple-schema-DQPZrAaZ.mjs → simple-schema-Bi40l-P6.mjs} +2 -2
- package/dist/{sse-client-DwX021ST.d.mts → sse-client-BQ4jS3eO.d.mts} +4 -4
- package/dist/{state-CK9LjrnT.mjs → state-Mwt6--el.mjs} +17 -9
- package/dist/{storage-BXNVhKyR.mjs → storage-B70AXK64.mjs} +2 -2
- package/dist/types/index.d.mts +6 -6
- package/dist/{types-CMdPYhOx.d.mts → types-Dq4i156u.d.mts} +17 -17
- package/dist/utils/agent-tool-name.d.mts +2 -2
- package/dist/utils/agent-tool-name.mjs +2 -2
- package/dist/utils/colors.d.mts +1 -1
- package/dist/utils/crypto-token.d.mts +1 -1
- package/dist/utils/crypto-token.mjs +1 -1
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/nostics.d.mts +1 -1
- package/dist/utils/nostics.mjs +1 -1
- package/dist/utils/open.mjs +1 -1
- package/dist/utils/origin.d.mts +2 -0
- package/dist/utils/origin.mjs +80 -0
- package/dist/utils/remote-assets.d.mts +2 -2
- package/dist/utils/remote-assets.mjs +1 -1
- package/dist/utils/serve-static.d.mts +4 -4
- package/dist/utils/serve-static.mjs +56 -19
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -1
- package/dist/utils/simple-schema.d.mts +5 -5
- package/dist/utils/simple-schema.mjs +2 -2
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/utils/streaming-channel.mjs +2 -2
- package/dist/utils/when.d.mts +5 -5
- package/dist/{validation-YrZH6atx.mjs → validation-CfV1s0SF.mjs} +2 -2
- package/dist/{wire-codec-0K-o5MYW.mjs → wire-codec-BHGt0sSs.mjs} +3 -3
- package/dist/{static-DV_qUSRS.mjs → write-BuLCrn9h.mjs} +153 -137
- package/dist/{ws-client-7_lZi-B9.d.mts → ws-client-Xpxt0oPA.d.mts} +3 -3
- package/dist/{ws-server-VyHlanzy.d.mts → ws-server-D2nv68Hg.d.mts} +13 -37
- package/package.json +3 -6
- package/skills/devframe/SKILL.md +5 -3
- package/dist/_shared-BM3PdYli.mjs +0 -68
- package/dist/_shared-mtXpcJIv.d.mts +0 -41
- package/dist/diagnostics-CD8nlgll.mjs +0 -39
- package/dist/serialization-BGzEwAdr.mjs +0 -77
- package/dist/ws-server-Y_AumUQH.mjs +0 -294
package/dist/constants.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { t as DEVFRAME_EVENTS } from "./events-
|
|
1
|
+
import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
|
|
2
2
|
//#region src/constants.ts
|
|
3
|
+
/** Devframe runtime routes and static output conventions. */
|
|
3
4
|
const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
|
|
4
5
|
/**
|
|
5
6
|
* Global key holding the serializable connection prepared by
|
|
@@ -10,7 +11,7 @@ const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
|
|
|
10
11
|
/**
|
|
11
12
|
* Route the WebSocket RPC endpoint is bound to, relative to a devframe's
|
|
12
13
|
* base path. Sits next to `__connection.json` so the deployed SPA can reach
|
|
13
|
-
* it on the same origin it loaded from
|
|
14
|
+
* it on the same origin it loaded from: the dev server shares one port for
|
|
14
15
|
* both HTTP and WS, and a host server (Vite, etc.) can mount the WS upgrade
|
|
15
16
|
* handler here without colliding with its own routes (HMR, asset serving).
|
|
16
17
|
*/
|
|
@@ -20,7 +21,7 @@ const DEVFRAME_WS_ROUTE = "__ws";
|
|
|
20
21
|
* path. A single method-dispatched route: `GET` opens the event stream
|
|
21
22
|
* (server→client), `POST` carries RPC frames (client→server). Sits next to
|
|
22
23
|
* `__connection.json` so the deployed SPA reaches it on the same origin it
|
|
23
|
-
* loaded from
|
|
24
|
+
* loaded from; the transport for hosts and proxies where the WebSocket
|
|
24
25
|
* upgrade isn't available.
|
|
25
26
|
*/
|
|
26
27
|
const DEVFRAME_SSE_ROUTE = "__sse";
|
|
@@ -34,7 +35,7 @@ const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
|
|
|
34
35
|
/**
|
|
35
36
|
* Route the Streamable-HTTP MCP endpoint is bound to, relative to a
|
|
36
37
|
* devframe's base path. Sits next to `__connection.json` and the WS route
|
|
37
|
-
* so an MCP client reaches it on the same origin the SPA loaded from
|
|
38
|
+
* so an MCP client reaches it on the same origin the SPA loaded from; the
|
|
38
39
|
* dev server shares one port for HTTP, WS, and MCP. Opt-in via `cli.mcp`.
|
|
39
40
|
*/
|
|
40
41
|
const DEVFRAME_MCP_ROUTE = "__mcp";
|
|
@@ -79,7 +80,7 @@ const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
|
79
80
|
/**
|
|
80
81
|
* `postMessage` type the remote-assets fallback page posts to `window.parent`
|
|
81
82
|
* on load. That page is what a devframe serves (with a 502) when its client
|
|
82
|
-
* assets can be reached neither locally nor through their CDN provider
|
|
83
|
+
* assets can be reached neither locally nor through their CDN provider; the
|
|
83
84
|
* message lets an embedding viewer replace the bare page with its own UI
|
|
84
85
|
* (`@devframes/hub-ui` does, in its iframe view). Payload shape:
|
|
85
86
|
* `RemoteAssetsErrorMessage` (`devframe/types`).
|
|
@@ -87,7 +88,7 @@ const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
|
87
88
|
const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE = DEVFRAME_EVENTS.postMessage.remoteAssetsError;
|
|
88
89
|
/**
|
|
89
90
|
* Prefix that marks an RPC method as callable before a connection is
|
|
90
|
-
* trusted. This is the *only* rule the pre-trust gate applies
|
|
91
|
+
* trusted. This is the *only* rule the pre-trust gate applies; there is no
|
|
91
92
|
* per-method allowlist. Any handshake method a host adapter needs to reach
|
|
92
93
|
* before authentication must be named `anonymous:<rest>` (e.g.
|
|
93
94
|
* `anonymous:devframe:auth`).
|
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import { i as defineDiagnostics } from "./nostics-
|
|
2
|
-
import {
|
|
1
|
+
import { i as defineDiagnostics } from "./nostics-D0PvLZsn.mjs";
|
|
2
|
+
import { r as diagnostics } from "./serialization-DFg5sUt6.mjs";
|
|
3
3
|
import { RpcFunctionsCollectorBase } from "./rpc/index.mjs";
|
|
4
4
|
import { defineRpcFunction } from "./index.mjs";
|
|
5
|
-
import { t as DEVFRAME_EVENTS } from "./events-
|
|
5
|
+
import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
|
|
6
6
|
import { DEVFRAME_SERVICES_STATE_KEY } from "./constants.mjs";
|
|
7
|
-
import { t as diagnostics$1 } from "./diagnostics-
|
|
8
|
-
import {
|
|
9
|
-
import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-
|
|
10
|
-
import { t as
|
|
7
|
+
import { t as diagnostics$1 } from "./diagnostics-DZA6KQ_Z.mjs";
|
|
8
|
+
import { r as createEventEmitter, t as DevframeAgentHost } from "./host-agent-CKqSTEX9.mjs";
|
|
9
|
+
import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-DMFzLSoH.mjs";
|
|
10
|
+
import { i as satisfiesVersionRange, n as expandResolveFrom, r as importServicePackage, t as deepMergeOptionSets } from "./services-install-Ben4yh52.mjs";
|
|
11
|
+
import { t as createStorage } from "./storage-B70AXK64.mjs";
|
|
12
|
+
import { join } from "pathe";
|
|
11
13
|
import { createSharedState } from "devframe/utils/shared-state";
|
|
12
14
|
import process from "node:process";
|
|
13
|
-
import { join } from "pathe";
|
|
14
15
|
import { existsSync } from "node:fs";
|
|
15
16
|
//#region src/node/host-diagnostics.ts
|
|
16
17
|
var DevframeDiagnosticsHost = class {
|
|
17
18
|
context;
|
|
18
19
|
_registry = {};
|
|
19
20
|
logger = new Proxy({}, { get: (_, code) => this._registry[code] });
|
|
21
|
+
/**
|
|
22
|
+
* Already pre-wires devframe's ANSI console reporter, so no extra merging
|
|
23
|
+
* needed here, the host's `defineDiagnostics` just is the shared one.
|
|
24
|
+
*/
|
|
20
25
|
defineDiagnostics = defineDiagnostics;
|
|
21
26
|
constructor(context, initialDefinitions = []) {
|
|
22
27
|
this.context = context;
|
|
@@ -123,7 +128,16 @@ function createRpcSharedStateServerHost(rpc) {
|
|
|
123
128
|
if (!sharedState.has(key)) return void 0;
|
|
124
129
|
return (await host.get(key)).value();
|
|
125
130
|
},
|
|
126
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Pre-compute snapshots for the build-mode static dump so the SPA
|
|
133
|
+
* can read them without a live server. The `undefined` fallback makes
|
|
134
|
+
* a key the build never created resolve like the live handler does
|
|
135
|
+
* (returning `undefined`) instead of failing the dump lookup.
|
|
136
|
+
*/
|
|
137
|
+
dump: () => ({
|
|
138
|
+
inputs: host.keys().map((key) => [key]),
|
|
139
|
+
fallback: void 0
|
|
140
|
+
})
|
|
127
141
|
});
|
|
128
142
|
rpc.register({
|
|
129
143
|
name: "devframe:rpc:server-state:set",
|
|
@@ -158,7 +172,7 @@ var StreamClosedError = class extends Error {
|
|
|
158
172
|
name = "StreamClosedError";
|
|
159
173
|
};
|
|
160
174
|
/**
|
|
161
|
-
* Build a server-side stream sink. RPC-agnostic
|
|
175
|
+
* Build a server-side stream sink. RPC-agnostic; the RPC host wires
|
|
162
176
|
* `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
|
|
163
177
|
* for late or reconnecting subscribers.
|
|
164
178
|
*/
|
|
@@ -229,7 +243,7 @@ function createStreamSink(options = {}) {
|
|
|
229
243
|
};
|
|
230
244
|
}
|
|
231
245
|
/**
|
|
232
|
-
* Build a client-side stream reader. RPC-agnostic
|
|
246
|
+
* Build a client-side stream reader. RPC-agnostic; the RPC host calls
|
|
233
247
|
* `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
|
|
234
248
|
* terminal frame. Consumers iterate with `for await` or pipe `readable`.
|
|
235
249
|
*/
|
|
@@ -412,7 +426,7 @@ function streamKey(channel, id) {
|
|
|
412
426
|
}
|
|
413
427
|
/**
|
|
414
428
|
* Build the server-side streaming host. Mirrors the layout of
|
|
415
|
-
* `createRpcSharedStateServerHost
|
|
429
|
+
* `createRpcSharedStateServerHost`: registers a fixed set of internal
|
|
416
430
|
* RPC methods (`subscribe` / `unsubscribe` / `cancel`) once, then per-channel
|
|
417
431
|
* state lives in a `Map<channelName, ChannelState>`.
|
|
418
432
|
*/
|
|
@@ -573,6 +587,10 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
573
587
|
name,
|
|
574
588
|
options: {
|
|
575
589
|
replayWindow,
|
|
590
|
+
/**
|
|
591
|
+
* Default to a 30-second hold when replay is enabled so late
|
|
592
|
+
* subscribers can join after the producer finishes.
|
|
593
|
+
*/
|
|
576
594
|
closedStreamRetention: opts.closedStreamRetention ?? (replayWindow > 0 ? 3e4 : 0)
|
|
577
595
|
},
|
|
578
596
|
streams: /* @__PURE__ */ new Map(),
|
|
@@ -669,37 +687,39 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
669
687
|
id: key.slice(sepIdx + 1)
|
|
670
688
|
};
|
|
671
689
|
}
|
|
690
|
+
function dropOutboundSubscriptions(meta) {
|
|
691
|
+
if (!meta.subscribedStreams) return;
|
|
692
|
+
for (const key of meta.subscribedStreams) {
|
|
693
|
+
const parsed = parseKey(key);
|
|
694
|
+
const state = parsed && channels.get(parsed.channelName);
|
|
695
|
+
const record = state && parsed && state.streams.get(parsed.id);
|
|
696
|
+
if (!parsed || !state || !record) continue;
|
|
697
|
+
record.subscribers.delete(meta);
|
|
698
|
+
if (record.subscribers.size === 0 && !record.sink.closed) record.sink.abort("all subscribers disconnected");
|
|
699
|
+
maybeFreeStream(state, parsed.id);
|
|
700
|
+
}
|
|
701
|
+
meta.subscribedStreams.clear();
|
|
702
|
+
}
|
|
703
|
+
function endInboundUploads(meta) {
|
|
704
|
+
if (!meta.uploadingStreams) return;
|
|
705
|
+
for (const key of meta.uploadingStreams) {
|
|
706
|
+
const parsed = parseKey(key);
|
|
707
|
+
const state = parsed && channels.get(parsed.channelName);
|
|
708
|
+
const record = state && parsed && state.inbound.get(parsed.id);
|
|
709
|
+
if (!parsed || !state || !record) continue;
|
|
710
|
+
record.reader._end({
|
|
711
|
+
name: "UploadDisconnected",
|
|
712
|
+
message: "Uploader disconnected before completing the stream"
|
|
713
|
+
});
|
|
714
|
+
state.inbound.delete(parsed.id);
|
|
715
|
+
}
|
|
716
|
+
meta.uploadingStreams.clear();
|
|
717
|
+
}
|
|
672
718
|
return {
|
|
673
719
|
create: createChannel,
|
|
674
720
|
_onSessionDisconnected(meta) {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const parsed = parseKey(key);
|
|
678
|
-
if (!parsed) continue;
|
|
679
|
-
const state = channels.get(parsed.channelName);
|
|
680
|
-
const record = state?.streams.get(parsed.id);
|
|
681
|
-
if (!state || !record) continue;
|
|
682
|
-
record.subscribers.delete(meta);
|
|
683
|
-
if (record.subscribers.size === 0 && !record.sink.closed) record.sink.abort("all subscribers disconnected");
|
|
684
|
-
maybeFreeStream(state, parsed.id);
|
|
685
|
-
}
|
|
686
|
-
meta.subscribedStreams.clear();
|
|
687
|
-
}
|
|
688
|
-
if (meta.uploadingStreams) {
|
|
689
|
-
for (const key of meta.uploadingStreams) {
|
|
690
|
-
const parsed = parseKey(key);
|
|
691
|
-
if (!parsed) continue;
|
|
692
|
-
const state = channels.get(parsed.channelName);
|
|
693
|
-
const record = state?.inbound.get(parsed.id);
|
|
694
|
-
if (!state || !record) continue;
|
|
695
|
-
record.reader._end({
|
|
696
|
-
name: "UploadDisconnected",
|
|
697
|
-
message: "Uploader disconnected before completing the stream"
|
|
698
|
-
});
|
|
699
|
-
state.inbound.delete(parsed.id);
|
|
700
|
-
}
|
|
701
|
-
meta.uploadingStreams.clear();
|
|
702
|
-
}
|
|
721
|
+
dropOutboundSubscriptions(meta);
|
|
722
|
+
endInboundUploads(meta);
|
|
703
723
|
}
|
|
704
724
|
};
|
|
705
725
|
}
|
|
@@ -763,6 +783,17 @@ var RpcFunctionsHostImpl = class extends RpcFunctionsCollectorBase {
|
|
|
763
783
|
//#endregion
|
|
764
784
|
//#region src/node/host-services.ts
|
|
765
785
|
const debug = createDebug("devframe:services");
|
|
786
|
+
/**
|
|
787
|
+
* Per-RPC-host registry of the services already installed against it, keyed
|
|
788
|
+
* by the RPC host object. A single wire service can be declared from more
|
|
789
|
+
* than one devframe context that shares one RPC host (e.g. a kit that mounts
|
|
790
|
+
* a definition alongside another context, so both iterate `def.services`).
|
|
791
|
+
* The per-instance `installed` guard can't see across those sibling hosts, so
|
|
792
|
+
* the second context would re-run the service factory and re-register its RPC,
|
|
793
|
+
* hitting DF0021. Sharing the registry by RPC host makes the first install win
|
|
794
|
+
* across every context on that host.
|
|
795
|
+
*/
|
|
796
|
+
const installedByRpcHost = /* @__PURE__ */ new WeakMap();
|
|
766
797
|
function isServiceDefinition(input) {
|
|
767
798
|
return typeof input.setup === "function";
|
|
768
799
|
}
|
|
@@ -784,13 +815,24 @@ function validateServiceDefinition(def) {
|
|
|
784
815
|
});
|
|
785
816
|
}
|
|
786
817
|
/**
|
|
818
|
+
* Merge every installer's option set in declaration order (the default
|
|
819
|
+
* deep-merge unions arrays and lets later scalars win; a service may override
|
|
820
|
+
* with its own `mergeOptions`).
|
|
821
|
+
*/
|
|
822
|
+
function mergeInstallerOptions(def, entries) {
|
|
823
|
+
const sets = entries.map((entry) => entry.input.options).filter((options) => options !== void 0);
|
|
824
|
+
if (def.mergeOptions) return def.mergeOptions(sets);
|
|
825
|
+
return sets.length > 0 ? deepMergeOptionSets(sets) : void 0;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
787
828
|
* Cross-plugin service registry (see `types/services.ts` for the contract).
|
|
788
829
|
* Values are held per context instance; `whenAvailable` subscriptions make
|
|
789
|
-
* the mechanism
|
|
830
|
+
* the mechanism work regardless of setup ordering between provider and
|
|
831
|
+
* consumer.
|
|
790
832
|
*
|
|
791
833
|
* On top of the in-process `provide`/`get` tier, this host implements the
|
|
792
834
|
* **wire-service** lifecycle: `install()` queues definitions/descriptors,
|
|
793
|
-
* `ready()` fires the collect-then-setup barrier
|
|
835
|
+
* `ready()` fires the collect-then-setup barrier: importing descriptor
|
|
794
836
|
* packages, merging option sets per service, constructing each service once,
|
|
795
837
|
* providing its node API under the package name, and advertising it to
|
|
796
838
|
* clients through the `devframe:services` shared state.
|
|
@@ -800,11 +842,27 @@ var DevframeServicesHostImpl = class {
|
|
|
800
842
|
services = /* @__PURE__ */ new Map();
|
|
801
843
|
listeners = /* @__PURE__ */ new Map();
|
|
802
844
|
pending = /* @__PURE__ */ new Map();
|
|
803
|
-
|
|
845
|
+
localInstalled = /* @__PURE__ */ new Map();
|
|
804
846
|
readyPromise;
|
|
805
847
|
constructor(context) {
|
|
806
848
|
this.context = context;
|
|
807
849
|
}
|
|
850
|
+
/**
|
|
851
|
+
* The install-dedup registry, shared across every services host that shares
|
|
852
|
+
* this host's RPC host so the first install of a package wins across sibling
|
|
853
|
+
* contexts. Falls back to a per-instance map when there is no RPC host (a
|
|
854
|
+
* context-less host only exercises the in-process `provide`/`get` tier).
|
|
855
|
+
*/
|
|
856
|
+
get installed() {
|
|
857
|
+
const rpc = this.context?.rpc;
|
|
858
|
+
if (!rpc) return this.localInstalled;
|
|
859
|
+
let registry = installedByRpcHost.get(rpc);
|
|
860
|
+
if (!registry) {
|
|
861
|
+
registry = /* @__PURE__ */ new Map();
|
|
862
|
+
installedByRpcHost.set(rpc, registry);
|
|
863
|
+
}
|
|
864
|
+
return registry;
|
|
865
|
+
}
|
|
808
866
|
provide(id, service) {
|
|
809
867
|
const key = id;
|
|
810
868
|
if (this.services.has(key)) throw diagnostics$1.DF0037({ id: key });
|
|
@@ -882,47 +940,75 @@ var DevframeServicesHostImpl = class {
|
|
|
882
940
|
}
|
|
883
941
|
async installPackage(pkg, entries) {
|
|
884
942
|
if (this.installed.has(pkg)) {
|
|
885
|
-
|
|
886
|
-
|
|
943
|
+
const api = this.installed.get(pkg);
|
|
944
|
+
if (this.services.has(pkg)) diagnostics$1.DF0066({ package: pkg });
|
|
945
|
+
else this.provide(pkg, api);
|
|
946
|
+
return api;
|
|
887
947
|
}
|
|
888
948
|
let def = entries.filter((entry) => isServiceDefinition(entry.input))[0]?.input;
|
|
889
949
|
if (!def) {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
950
|
+
def = await this.importServiceDefinition(pkg, entries);
|
|
951
|
+
if (!def) return void 0;
|
|
952
|
+
}
|
|
953
|
+
this.checkVersionRanges(pkg, def, entries);
|
|
954
|
+
const options = mergeInstallerOptions(def, entries);
|
|
955
|
+
if (!this.context) throw diagnostics$1.DF0070({
|
|
956
|
+
package: pkg,
|
|
957
|
+
reason: "this services host has no node context to install into"
|
|
958
|
+
});
|
|
959
|
+
debug("installing service %s@%s (scope %s)", def.package, def.version, def.scope);
|
|
960
|
+
const scoped = this.context.scope(def.scope);
|
|
961
|
+
const api = await def.setup(scoped, options === void 0 ? {} : { options });
|
|
962
|
+
this.installed.set(pkg, api);
|
|
963
|
+
this.provide(def.package, api);
|
|
964
|
+
await this.advertise(def);
|
|
965
|
+
return api;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Import a descriptor-only package, run its factory, and validate the
|
|
969
|
+
* resulting definition. Returns `undefined` when an optional package can't
|
|
970
|
+
* be imported (the install is skipped); throws for a required one.
|
|
971
|
+
*/
|
|
972
|
+
async importServiceDefinition(pkg, entries) {
|
|
973
|
+
const required = entries.map((entry) => entry.input).some((descriptor) => descriptor.required === true);
|
|
974
|
+
const cwd = this.context?.cwd ?? process.cwd();
|
|
975
|
+
const resolveFroms = [
|
|
976
|
+
...entries.map((entry) => entry.resolveFrom && expandResolveFrom(entry.resolveFrom, cwd)),
|
|
977
|
+
this.context?.workspaceRoot,
|
|
978
|
+
cwd
|
|
979
|
+
];
|
|
980
|
+
let mod;
|
|
981
|
+
try {
|
|
982
|
+
mod = await importServicePackage(pkg, resolveFroms);
|
|
983
|
+
} catch (error) {
|
|
984
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
985
|
+
if (required) throw diagnostics$1.DF0067({
|
|
917
986
|
package: pkg,
|
|
918
|
-
reason
|
|
987
|
+
reason,
|
|
988
|
+
cause: error
|
|
919
989
|
});
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
package: pkg
|
|
923
|
-
};
|
|
924
|
-
validateServiceDefinition(def);
|
|
990
|
+
debug("optional service %s not importable, skipping: %s", pkg, reason);
|
|
991
|
+
return;
|
|
925
992
|
}
|
|
993
|
+
const factory = mod.default;
|
|
994
|
+
if (typeof factory !== "function") throw diagnostics$1.DF0070({
|
|
995
|
+
package: pkg,
|
|
996
|
+
reason: "its default export is not a factory function"
|
|
997
|
+
});
|
|
998
|
+
let def = await factory();
|
|
999
|
+
if (!def || typeof def.setup !== "function") throw diagnostics$1.DF0070({
|
|
1000
|
+
package: pkg,
|
|
1001
|
+
reason: "its factory did not return a definition with a `setup` function"
|
|
1002
|
+
});
|
|
1003
|
+
if (typeof def.package !== "string" || def.package.length === 0) def = {
|
|
1004
|
+
...def,
|
|
1005
|
+
package: pkg
|
|
1006
|
+
};
|
|
1007
|
+
validateServiceDefinition(def);
|
|
1008
|
+
return def;
|
|
1009
|
+
}
|
|
1010
|
+
/** Version-range checks against the resolved definition's real version. */
|
|
1011
|
+
checkVersionRanges(pkg, def, entries) {
|
|
926
1012
|
for (const entry of entries) {
|
|
927
1013
|
const descriptor = entry.input;
|
|
928
1014
|
if (isServiceDefinition(entry.input) || typeof descriptor.version !== "string") continue;
|
|
@@ -938,19 +1024,6 @@ var DevframeServicesHostImpl = class {
|
|
|
938
1024
|
installed: def.version
|
|
939
1025
|
});
|
|
940
1026
|
}
|
|
941
|
-
const sets = entries.map((entry) => entry.input.options).filter((options) => options !== void 0);
|
|
942
|
-
const options = def.mergeOptions ? def.mergeOptions(sets) : sets.length > 0 ? deepMergeOptionSets(sets) : void 0;
|
|
943
|
-
if (!this.context) throw diagnostics$1.DF0070({
|
|
944
|
-
package: pkg,
|
|
945
|
-
reason: "this services host has no node context to install into"
|
|
946
|
-
});
|
|
947
|
-
debug("installing service %s@%s (scope %s)", def.package, def.version, def.scope);
|
|
948
|
-
const scoped = this.context.scope(def.scope);
|
|
949
|
-
const api = await def.setup(scoped, options === void 0 ? {} : { options });
|
|
950
|
-
this.installed.set(def.package, api);
|
|
951
|
-
this.provide(def.package, api);
|
|
952
|
-
await this.advertise(def);
|
|
953
|
-
return api;
|
|
954
1027
|
}
|
|
955
1028
|
advertisementState() {
|
|
956
1029
|
return this.context.rpc.sharedState.get(DEVFRAME_SERVICES_STATE_KEY, { initialValue: {} });
|
|
@@ -1019,7 +1092,7 @@ const agentListResources = defineRpcFunction({
|
|
|
1019
1092
|
/**
|
|
1020
1093
|
* Built-in agent introspection RPC functions. Registered automatically
|
|
1021
1094
|
* by `createHostContext`. Not themselves agent-exposed (no `agent`
|
|
1022
|
-
* field)
|
|
1095
|
+
* field); they power the MCP adapter and any future agent CLI.
|
|
1023
1096
|
*/
|
|
1024
1097
|
const BUILTIN_AGENT_RPC = [
|
|
1025
1098
|
defineRpcFunction({
|
|
@@ -1169,7 +1242,7 @@ function createScopedNodeContext(context, namespace) {
|
|
|
1169
1242
|
* Framework- and build-tool-agnostic core of the Devframe node context.
|
|
1170
1243
|
* Wires the RPC host, view (HTTP file-serving) host, diagnostics, and
|
|
1171
1244
|
* agent subsystems. Host adapters can wrap this to augment `ctx` with
|
|
1172
|
-
* extra surfaces
|
|
1245
|
+
* extra surfaces; for example, `@vitejs/devtools-kit`'s
|
|
1173
1246
|
* `createKitContext` attaches `docks`, `terminals`, `messages`, and
|
|
1174
1247
|
* `commands` when mounted into Vite DevTools.
|
|
1175
1248
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as SharedState, o as DevframeNodeContext } from "./context-
|
|
1
|
+
import { k as SharedState, o as DevframeNodeContext } from "./context-CHW4BZOj.mjs";
|
|
2
2
|
//#region src/node/hub-internals/context.d.ts
|
|
3
3
|
interface InternalAnonymousAuthStorage {
|
|
4
4
|
trusted: Record<string, {
|
|
@@ -10,7 +10,7 @@ interface InternalAnonymousAuthStorage {
|
|
|
10
10
|
}
|
|
11
11
|
interface RemoteTokenRecord {
|
|
12
12
|
dockId: string;
|
|
13
|
-
/** Dock URL origin
|
|
13
|
+
/** Dock URL origin, matched against WS handshake `Origin` header when `originLock` is on. */
|
|
14
14
|
origin: string;
|
|
15
15
|
originLock: boolean;
|
|
16
16
|
}
|
|
@@ -24,7 +24,7 @@ interface DevframeInternalContext {
|
|
|
24
24
|
*/
|
|
25
25
|
revokeAuthToken: (token: string) => Promise<void>;
|
|
26
26
|
/**
|
|
27
|
-
* Session-only tokens issued to remote-UI iframe docks. Not persisted
|
|
27
|
+
* Session-only tokens issued to remote-UI iframe docks. Not persisted;
|
|
28
28
|
* regenerated on every dev-server restart.
|
|
29
29
|
*/
|
|
30
30
|
remoteTokens: Map<string, RemoteTokenRecord>;
|
|
@@ -45,8 +45,8 @@ interface DevframeInternalContext {
|
|
|
45
45
|
url: string;
|
|
46
46
|
};
|
|
47
47
|
/**
|
|
48
|
-
* Set {@link DevframeInternalContext.wsEndpoint} and notify subscribers
|
|
49
|
-
*
|
|
48
|
+
* Set {@link DevframeInternalContext.wsEndpoint} and notify subscribers.
|
|
49
|
+
* The WS-binding tiers (side-car, shared-server, and the `unbound` tier's
|
|
50
50
|
* `attach()`) call this once the socket is bound (or `undefined` once torn
|
|
51
51
|
* down) instead of assigning the field directly, so anything that already
|
|
52
52
|
* projected the endpoint (a hub's remote-dock URLs, registered before an
|