devframe 0.9.0 → 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-CbZ_yhbz.mjs → _shared-BM3PdYli.mjs} +1 -1
- 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 -21
- 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--rAPzbvO.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/{dist-eNI9qM_I.mjs → dist-CZXfGEkd.mjs} +1 -1
- package/dist/events-DKfSmoTj.mjs +65 -0
- package/dist/{host-agent-xLCSe65D.mjs → host-agent-BDdcOXhG.mjs} +14 -13
- package/dist/{http-DYpK0cUi.mjs → http-BbHCU5Zs.mjs} +8 -5
- 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-CMFyszWJ.mjs → instance-shell-DbDoozNM.mjs} +5 -5
- package/dist/internal/index.d.mts +46 -4
- package/dist/internal/index.mjs +7 -7
- 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/ws-bun.d.mts +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.mjs +1 -1
- 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/package.json +1 -1
- package/dist/{host-h3-Crkzm42q.mjs → host-h3-fRbF9yor.mjs} +1 -1
package/dist/constants.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as DEVFRAME_EVENTS } from "./events-DKfSmoTj.mjs";
|
|
1
2
|
//#region src/constants.ts
|
|
2
3
|
const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
|
|
3
4
|
/**
|
|
@@ -41,6 +42,12 @@ const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
|
|
|
41
42
|
const DEVFRAME_DOCK_IMPORTS_FILENAME = "__client-imports.js";
|
|
42
43
|
const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
|
|
43
44
|
/**
|
|
45
|
+
* Shared-state key carrying the wire-service advertisements (package name →
|
|
46
|
+
* `DevframeServiceMeta`). Written by the node services host; mirrored to
|
|
47
|
+
* clients for feature-detection via `client.services`.
|
|
48
|
+
*/
|
|
49
|
+
const DEVFRAME_SERVICES_STATE_KEY = "devframe:services";
|
|
50
|
+
/**
|
|
44
51
|
* URL fragment / query parameter name carrying the remote dock
|
|
45
52
|
* connection descriptor (defined as `RemoteConnectionInfo` in
|
|
46
53
|
* `@vitejs/devtools-kit`) injected into remote-UI iframe dock URLs.
|
|
@@ -77,7 +84,7 @@ const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
|
|
|
77
84
|
* (`@devframes/hub-ui` does, in its iframe view). Payload shape:
|
|
78
85
|
* `RemoteAssetsErrorMessage` (`devframe/types`).
|
|
79
86
|
*/
|
|
80
|
-
const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE =
|
|
87
|
+
const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE = DEVFRAME_EVENTS.postMessage.remoteAssetsError;
|
|
81
88
|
/**
|
|
82
89
|
* Prefix that marks an RPC method as callable before a connection is
|
|
83
90
|
* trusted. This is the *only* rule the pre-trust gate applies — there is no
|
|
@@ -96,4 +103,4 @@ function isAnonymousRpcMethod(name) {
|
|
|
96
103
|
return name.startsWith(ANONYMOUS_RPC_PREFIX);
|
|
97
104
|
}
|
|
98
105
|
//#endregion
|
|
99
|
-
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_MCP_ROUTE, DEVFRAME_OTP_URL_PARAM, DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_SSE_ROUTE, DEVFRAME_SSE_SESSION_HEADER, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
|
106
|
+
export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_EVENTS, DEVFRAME_MCP_ROUTE, DEVFRAME_OTP_URL_PARAM, DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_SERVICES_STATE_KEY, DEVFRAME_SSE_ROUTE, DEVFRAME_SSE_SESSION_HEADER, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
|
|
@@ -2,13 +2,18 @@ import { i as defineDiagnostics } from "./nostics-CzECRXpE.mjs";
|
|
|
2
2
|
import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
|
|
3
3
|
import { RpcFunctionsCollectorBase } from "./rpc/index.mjs";
|
|
4
4
|
import { defineRpcFunction } from "./index.mjs";
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t as
|
|
5
|
+
import { t as DEVFRAME_EVENTS } from "./events-DKfSmoTj.mjs";
|
|
6
|
+
import { DEVFRAME_SERVICES_STATE_KEY } from "./constants.mjs";
|
|
7
|
+
import { t as diagnostics$1 } from "./diagnostics-Di8ytitn.mjs";
|
|
8
|
+
import { r as createEventEmitter, t as DevframeAgentHost } from "./host-agent-BDdcOXhG.mjs";
|
|
9
|
+
import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-DRfY2RjX.mjs";
|
|
10
|
+
import { t as createStorage } from "./storage-D4a99Ung.mjs";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
9
12
|
import { createSharedState } from "devframe/utils/shared-state";
|
|
13
|
+
import process from "node:process";
|
|
14
|
+
import { pathToFileURL } from "node:url";
|
|
15
|
+
import { isAbsolute, join } from "pathe";
|
|
10
16
|
import { existsSync } from "node:fs";
|
|
11
|
-
import { join } from "pathe";
|
|
12
17
|
//#region src/node/host-diagnostics.ts
|
|
13
18
|
var DevframeDiagnosticsHost = class {
|
|
14
19
|
context;
|
|
@@ -25,7 +30,7 @@ var DevframeDiagnosticsHost = class {
|
|
|
25
30
|
};
|
|
26
31
|
//#endregion
|
|
27
32
|
//#region src/node/rpc-shared-state.ts
|
|
28
|
-
const debug$
|
|
33
|
+
const debug$2 = createDebug("devframe:rpc:state:changed");
|
|
29
34
|
const debugSubscribe = createDebug("devframe:rpc:state:subscribe");
|
|
30
35
|
function createRpcSharedStateServerHost(rpc) {
|
|
31
36
|
const sharedState = /* @__PURE__ */ new Map();
|
|
@@ -35,12 +40,12 @@ function createRpcSharedStateServerHost(rpc) {
|
|
|
35
40
|
const offs = [];
|
|
36
41
|
offs.push(state.on("updated", (fullState, patches, syncId) => {
|
|
37
42
|
if (patches) {
|
|
38
|
-
debug$
|
|
43
|
+
debug$2("patch", {
|
|
39
44
|
key,
|
|
40
45
|
syncId
|
|
41
46
|
});
|
|
42
47
|
rpc.broadcast({
|
|
43
|
-
method:
|
|
48
|
+
method: DEVFRAME_EVENTS.broadcast.clientStatePatch,
|
|
44
49
|
args: [
|
|
45
50
|
key,
|
|
46
51
|
patches,
|
|
@@ -49,12 +54,12 @@ function createRpcSharedStateServerHost(rpc) {
|
|
|
49
54
|
filter: (client) => client.$meta.subscribedStates.has(key)
|
|
50
55
|
});
|
|
51
56
|
} else {
|
|
52
|
-
debug$
|
|
57
|
+
debug$2("updated", {
|
|
53
58
|
key,
|
|
54
59
|
syncId
|
|
55
60
|
});
|
|
56
61
|
rpc.broadcast({
|
|
57
|
-
method:
|
|
62
|
+
method: DEVFRAME_EVENTS.broadcast.clientStateUpdated,
|
|
58
63
|
args: [
|
|
59
64
|
key,
|
|
60
65
|
fullState,
|
|
@@ -72,7 +77,7 @@ function createRpcSharedStateServerHost(rpc) {
|
|
|
72
77
|
get: async (key, options) => {
|
|
73
78
|
if (sharedState.has(key)) return sharedState.get(key);
|
|
74
79
|
if (options?.initialValue === void 0 && options?.sharedState === void 0) throw diagnostics$1.DF0013({ key });
|
|
75
|
-
debug$
|
|
80
|
+
debug$2("new-state", key);
|
|
76
81
|
const state = options.sharedState ?? createSharedState({
|
|
77
82
|
initialValue: options.initialValue,
|
|
78
83
|
enablePatches: false
|
|
@@ -402,7 +407,7 @@ function toErrorPayload(reason) {
|
|
|
402
407
|
}
|
|
403
408
|
//#endregion
|
|
404
409
|
//#region src/node/rpc-streaming.ts
|
|
405
|
-
const debug = createDebug("devframe:rpc:streaming");
|
|
410
|
+
const debug$1 = createDebug("devframe:rpc:streaming");
|
|
406
411
|
const STREAM_KEY_SEPARATOR = "";
|
|
407
412
|
function streamKey(channel, id) {
|
|
408
413
|
return `${channel}${STREAM_KEY_SEPARATOR}${id}`;
|
|
@@ -427,7 +432,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
427
432
|
}
|
|
428
433
|
for (const off of record.unbinders) off();
|
|
429
434
|
state.streams.delete(id);
|
|
430
|
-
debug("freed", state.name, id);
|
|
435
|
+
debug$1("freed", state.name, id);
|
|
431
436
|
}
|
|
432
437
|
function maybeFreeStream(state, id) {
|
|
433
438
|
const record = state.streams.get(id);
|
|
@@ -476,7 +481,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
476
481
|
cancelRetention(record);
|
|
477
482
|
const afterSeq = opts?.afterSeq ?? 0;
|
|
478
483
|
for (const buffered of record.sink.buffer) if (buffered.seq > afterSeq) rpc.broadcast({
|
|
479
|
-
method:
|
|
484
|
+
method: DEVFRAME_EVENTS.broadcast.streamingChunk,
|
|
480
485
|
args: [
|
|
481
486
|
channelName,
|
|
482
487
|
id,
|
|
@@ -488,7 +493,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
488
493
|
filter: (client) => client.$meta === session.meta
|
|
489
494
|
});
|
|
490
495
|
if (record.sink.closed) rpc.broadcast({
|
|
491
|
-
method:
|
|
496
|
+
method: DEVFRAME_EVENTS.broadcast.streamingEnd,
|
|
492
497
|
args: [
|
|
493
498
|
channelName,
|
|
494
499
|
id,
|
|
@@ -589,7 +594,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
589
594
|
state.streams.set(sink.id, record);
|
|
590
595
|
record.unbinders.push(sink.events.on("chunk", (seq, chunk) => {
|
|
591
596
|
rpc.broadcast({
|
|
592
|
-
method:
|
|
597
|
+
method: DEVFRAME_EVENTS.broadcast.streamingChunk,
|
|
593
598
|
args: [
|
|
594
599
|
name,
|
|
595
600
|
sink.id,
|
|
@@ -603,7 +608,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
603
608
|
}));
|
|
604
609
|
record.unbinders.push(sink.events.on("end", (error) => {
|
|
605
610
|
rpc.broadcast({
|
|
606
|
-
method:
|
|
611
|
+
method: DEVFRAME_EVENTS.broadcast.streamingEnd,
|
|
607
612
|
args: [
|
|
608
613
|
name,
|
|
609
614
|
sink.id,
|
|
@@ -636,7 +641,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
636
641
|
const targetMeta = inboundRecord?.uploaderMeta;
|
|
637
642
|
if (!targetMeta) return;
|
|
638
643
|
rpc.broadcast({
|
|
639
|
-
method:
|
|
644
|
+
method: DEVFRAME_EVENTS.broadcast.streamingUploadCancel,
|
|
640
645
|
args: [name, reader.id],
|
|
641
646
|
event: true,
|
|
642
647
|
optional: true,
|
|
@@ -646,7 +651,7 @@ function createRpcStreamingServerHost(rpc) {
|
|
|
646
651
|
});
|
|
647
652
|
inboundRecord = { reader };
|
|
648
653
|
state.inbound.set(reader.id, inboundRecord);
|
|
649
|
-
debug("opened-inbound", name, reader.id);
|
|
654
|
+
debug$1("opened-inbound", name, reader.id);
|
|
650
655
|
return reader;
|
|
651
656
|
}
|
|
652
657
|
return {
|
|
@@ -758,15 +763,177 @@ var RpcFunctionsHostImpl = class extends RpcFunctionsCollectorBase {
|
|
|
758
763
|
}
|
|
759
764
|
};
|
|
760
765
|
//#endregion
|
|
766
|
+
//#region src/node/services-install.ts
|
|
767
|
+
/**
|
|
768
|
+
* Turn a `resolveFrom` value (a file path, a file URL like `import.meta.url`,
|
|
769
|
+
* or a directory) into something `createRequire` accepts — a directory gets a
|
|
770
|
+
* synthetic filename appended so resolution starts inside it.
|
|
771
|
+
*/
|
|
772
|
+
function toRequireBase(resolveFrom) {
|
|
773
|
+
if (resolveFrom.startsWith("file://")) return resolveFrom;
|
|
774
|
+
if ((resolveFrom.split(/[/\\]/).pop() ?? "").includes(".")) return resolveFrom;
|
|
775
|
+
return join(resolveFrom, "_devframe_resolve.js");
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Normalize an `install()` `resolveFrom` into a resolution base. Paths and
|
|
779
|
+
* file URLs pass through; a bare npm package name (the common case: the
|
|
780
|
+
* declaring plugin's `packageName`) resolves to that package's location from
|
|
781
|
+
* `cwd`, so a service it declares resolves against the plugin's own
|
|
782
|
+
* dependencies. An unresolvable package name reads as no base (the caller's
|
|
783
|
+
* workspace fallbacks apply).
|
|
784
|
+
*/
|
|
785
|
+
function expandResolveFrom(resolveFrom, cwd) {
|
|
786
|
+
if (resolveFrom.startsWith("file://") || resolveFrom.startsWith(".") || isAbsolute(resolveFrom)) return resolveFrom;
|
|
787
|
+
const require = createRequire(join(cwd, "_devframe_resolve.js"));
|
|
788
|
+
try {
|
|
789
|
+
return require.resolve(`${resolveFrom}/package.json`);
|
|
790
|
+
} catch {}
|
|
791
|
+
try {
|
|
792
|
+
return require.resolve(resolveFrom);
|
|
793
|
+
} catch {}
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Import a service package's module, trying each `resolveFrom` candidate in
|
|
797
|
+
* order (so a plugin-declared service resolves against the plugin's own
|
|
798
|
+
* dependency tree first, then the workspace fallback). Throws the last
|
|
799
|
+
* resolution error when no candidate succeeds.
|
|
800
|
+
*/
|
|
801
|
+
async function importServicePackage(pkg, resolveFroms) {
|
|
802
|
+
const candidates = [...new Set(resolveFroms.filter((x) => typeof x === "string" && x.length > 0))];
|
|
803
|
+
let lastError = /* @__PURE__ */ new Error(`no resolution base available for "${pkg}"`);
|
|
804
|
+
for (const from of candidates) {
|
|
805
|
+
let resolved;
|
|
806
|
+
try {
|
|
807
|
+
resolved = createRequire(toRequireBase(from)).resolve(pkg);
|
|
808
|
+
} catch (error) {
|
|
809
|
+
lastError = error;
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
return await import(pathToFileURL(resolved).href);
|
|
813
|
+
}
|
|
814
|
+
throw lastError;
|
|
815
|
+
}
|
|
816
|
+
function parseVersion(input) {
|
|
817
|
+
const [core, ...prerelease] = input.trim().replace(/^v/, "").split("-");
|
|
818
|
+
if (!core) return void 0;
|
|
819
|
+
const parts = core.split(".").map((part) => Number.parseInt(part, 10));
|
|
820
|
+
if (parts.length === 0 || parts.some((part) => Number.isNaN(part) || part < 0)) return void 0;
|
|
821
|
+
while (parts.length < 3) parts.push(0);
|
|
822
|
+
return {
|
|
823
|
+
parts,
|
|
824
|
+
...prerelease.length ? { prerelease: prerelease.join("-") } : {}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function compareVersions(a, b) {
|
|
828
|
+
for (let i = 0; i < 3; i++) {
|
|
829
|
+
const diff = (a.parts[i] ?? 0) - (b.parts[i] ?? 0);
|
|
830
|
+
if (diff !== 0) return diff;
|
|
831
|
+
}
|
|
832
|
+
if (a.prerelease && !b.prerelease) return -1;
|
|
833
|
+
if (!a.prerelease && b.prerelease) return 1;
|
|
834
|
+
if (a.prerelease && b.prerelease) return a.prerelease < b.prerelease ? -1 : a.prerelease > b.prerelease ? 1 : 0;
|
|
835
|
+
return 0;
|
|
836
|
+
}
|
|
837
|
+
function satisfiesComparator(version, comparator) {
|
|
838
|
+
const raw = comparator.trim();
|
|
839
|
+
if (!raw || raw === "*" || raw === "x") return true;
|
|
840
|
+
const operatorMatch = raw.match(/^([\^~]|>=|<=|[><=])?(.+)$/);
|
|
841
|
+
if (!operatorMatch) return false;
|
|
842
|
+
const operator = operatorMatch[1];
|
|
843
|
+
const rest = operatorMatch[2].trim();
|
|
844
|
+
const segments = rest.replace(/\.[x*]/gi, "").split(".").filter(Boolean);
|
|
845
|
+
const base = parseVersion(rest.replace(/[x*]/gi, "0"));
|
|
846
|
+
if (!base) return false;
|
|
847
|
+
switch (operator) {
|
|
848
|
+
case ">": return compareVersions(version, base) > 0;
|
|
849
|
+
case ">=": return compareVersions(version, base) >= 0;
|
|
850
|
+
case "<": return compareVersions(version, base) < 0;
|
|
851
|
+
case "<=": return compareVersions(version, base) <= 0;
|
|
852
|
+
case "^": {
|
|
853
|
+
if (compareVersions(version, base) < 0) return false;
|
|
854
|
+
const fixedIndex = base.parts.findIndex((part) => part !== 0);
|
|
855
|
+
const lockUpTo = fixedIndex === -1 ? base.parts.length - 1 : fixedIndex;
|
|
856
|
+
for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
case "~": {
|
|
860
|
+
if (compareVersions(version, base) < 0) return false;
|
|
861
|
+
const lockUpTo = segments.length >= 2 ? 1 : 0;
|
|
862
|
+
for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
default:
|
|
866
|
+
for (let i = 0; i < Math.max(segments.length, 3); i++) if (i < segments.length && (version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
|
|
867
|
+
return segments.length >= 3 ? compareVersions(version, base) === 0 : true;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Pragmatic semver range check for service version declarations — supports
|
|
872
|
+
* the common forms (`1.2.3`, `^1.2.3`, `~1.2`, `>=1 <3`, `1.x`, `*`, and
|
|
873
|
+
* `||`-joined alternatives) without pulling in a semver dependency. An
|
|
874
|
+
* unparseable version or range reads as **not satisfied**.
|
|
875
|
+
*/
|
|
876
|
+
function satisfiesVersionRange(version, range) {
|
|
877
|
+
const parsed = parseVersion(version);
|
|
878
|
+
if (!parsed) return false;
|
|
879
|
+
const alternatives = range.split("||").map((alt) => alt.trim()).filter(Boolean);
|
|
880
|
+
if (alternatives.length === 0) return true;
|
|
881
|
+
return alternatives.some((alternative) => alternative.split(/\s+/).every((comparator) => satisfiesComparator(parsed, comparator)));
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Default option-set merge when a service declares no `mergeOptions`:
|
|
885
|
+
* shallow-merge plain objects in declaration order (later sets win); any
|
|
886
|
+
* non-object set collapses the merge to "last one wins".
|
|
887
|
+
*/
|
|
888
|
+
function shallowMergeOptionSets(sets) {
|
|
889
|
+
if (sets.some((set) => typeof set !== "object" || set === null || Array.isArray(set))) return sets[sets.length - 1];
|
|
890
|
+
return Object.assign({}, ...sets);
|
|
891
|
+
}
|
|
892
|
+
//#endregion
|
|
761
893
|
//#region src/node/host-services.ts
|
|
894
|
+
const debug = createDebug("devframe:services");
|
|
895
|
+
function isServiceDefinition(input) {
|
|
896
|
+
return typeof input.setup === "function";
|
|
897
|
+
}
|
|
898
|
+
function validateServiceInput(input) {
|
|
899
|
+
if (!input || typeof input.package !== "string" || input.package.length === 0) throw diagnostics$1.DF0070({
|
|
900
|
+
package: String(input?.package ?? input),
|
|
901
|
+
reason: "the input has no `package` name"
|
|
902
|
+
});
|
|
903
|
+
if (isServiceDefinition(input)) validateServiceDefinition(input);
|
|
904
|
+
}
|
|
905
|
+
function validateServiceDefinition(def) {
|
|
906
|
+
if (typeof def.version !== "string" || def.version.length === 0) throw diagnostics$1.DF0070({
|
|
907
|
+
package: def.package,
|
|
908
|
+
reason: "the definition has no `version`"
|
|
909
|
+
});
|
|
910
|
+
if (typeof def.scope !== "string" || def.scope.length === 0) throw diagnostics$1.DF0070({
|
|
911
|
+
package: def.package,
|
|
912
|
+
reason: "the definition has no RPC `scope` namespace"
|
|
913
|
+
});
|
|
914
|
+
}
|
|
762
915
|
/**
|
|
763
916
|
* Cross-plugin service registry (see `types/services.ts` for the contract).
|
|
764
917
|
* Values are held per context instance; `whenAvailable` subscriptions make
|
|
765
918
|
* the mechanism robust against setup ordering between provider and consumer.
|
|
919
|
+
*
|
|
920
|
+
* On top of the in-process `provide`/`get` tier, this host implements the
|
|
921
|
+
* **wire-service** lifecycle: `install()` queues definitions/descriptors,
|
|
922
|
+
* `ready()` fires the collect-then-setup barrier — importing descriptor
|
|
923
|
+
* packages, merging option sets per service, constructing each service once,
|
|
924
|
+
* providing its node API under the package name, and advertising it to
|
|
925
|
+
* clients through the `devframe:services` shared state.
|
|
766
926
|
*/
|
|
767
927
|
var DevframeServicesHostImpl = class {
|
|
928
|
+
context;
|
|
768
929
|
services = /* @__PURE__ */ new Map();
|
|
769
930
|
listeners = /* @__PURE__ */ new Map();
|
|
931
|
+
pending = /* @__PURE__ */ new Map();
|
|
932
|
+
installed = /* @__PURE__ */ new Map();
|
|
933
|
+
readyPromise;
|
|
934
|
+
constructor(context) {
|
|
935
|
+
this.context = context;
|
|
936
|
+
}
|
|
770
937
|
provide(id, service) {
|
|
771
938
|
const key = id;
|
|
772
939
|
if (this.services.has(key)) throw diagnostics$1.DF0037({ id: key });
|
|
@@ -799,6 +966,136 @@ var DevframeServicesHostImpl = class {
|
|
|
799
966
|
keys() {
|
|
800
967
|
return Array.from(this.services.keys());
|
|
801
968
|
}
|
|
969
|
+
install(input, options) {
|
|
970
|
+
validateServiceInput(input);
|
|
971
|
+
const promise = new Promise((resolve, reject) => {
|
|
972
|
+
const entry = {
|
|
973
|
+
input,
|
|
974
|
+
resolveFrom: options?.resolveFrom,
|
|
975
|
+
resolve,
|
|
976
|
+
reject
|
|
977
|
+
};
|
|
978
|
+
if (this.readyPromise) this.flushPackage(input.package, [entry]).catch(() => {});
|
|
979
|
+
else {
|
|
980
|
+
let entries = this.pending.get(input.package);
|
|
981
|
+
if (!entries) {
|
|
982
|
+
entries = [];
|
|
983
|
+
this.pending.set(input.package, entries);
|
|
984
|
+
}
|
|
985
|
+
entries.push(entry);
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
promise.catch(() => {});
|
|
989
|
+
return promise;
|
|
990
|
+
}
|
|
991
|
+
ready() {
|
|
992
|
+
if (this.readyPromise) return this.readyPromise;
|
|
993
|
+
this.readyPromise = this.flushAll();
|
|
994
|
+
return this.readyPromise;
|
|
995
|
+
}
|
|
996
|
+
async flushAll() {
|
|
997
|
+
if (this.context) await this.advertisementState();
|
|
998
|
+
const groups = Array.from(this.pending.entries());
|
|
999
|
+
this.pending.clear();
|
|
1000
|
+
for (const [pkg, entries] of groups) await this.flushPackage(pkg, entries);
|
|
1001
|
+
}
|
|
1002
|
+
async flushPackage(pkg, entries) {
|
|
1003
|
+
try {
|
|
1004
|
+
const api = await this.installPackage(pkg, entries);
|
|
1005
|
+
for (const entry of entries) entry.resolve(api);
|
|
1006
|
+
return api;
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
for (const entry of entries) entry.reject(error);
|
|
1009
|
+
throw error;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
async installPackage(pkg, entries) {
|
|
1013
|
+
if (this.installed.has(pkg)) {
|
|
1014
|
+
diagnostics$1.DF0066({ package: pkg });
|
|
1015
|
+
return this.installed.get(pkg);
|
|
1016
|
+
}
|
|
1017
|
+
let def = entries.filter((entry) => isServiceDefinition(entry.input))[0]?.input;
|
|
1018
|
+
if (!def) {
|
|
1019
|
+
const required = entries.map((entry) => entry.input).some((descriptor) => descriptor.required === true);
|
|
1020
|
+
const cwd = this.context?.cwd ?? process.cwd();
|
|
1021
|
+
const resolveFroms = [
|
|
1022
|
+
...entries.map((entry) => entry.resolveFrom && expandResolveFrom(entry.resolveFrom, cwd)),
|
|
1023
|
+
this.context?.workspaceRoot,
|
|
1024
|
+
cwd
|
|
1025
|
+
];
|
|
1026
|
+
let mod;
|
|
1027
|
+
try {
|
|
1028
|
+
mod = await importServicePackage(pkg, resolveFroms);
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1031
|
+
if (required) throw diagnostics$1.DF0067({
|
|
1032
|
+
package: pkg,
|
|
1033
|
+
reason,
|
|
1034
|
+
cause: error
|
|
1035
|
+
});
|
|
1036
|
+
debug("optional service %s not importable, skipping: %s", pkg, reason);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
const factory = mod.default;
|
|
1040
|
+
if (typeof factory !== "function") throw diagnostics$1.DF0070({
|
|
1041
|
+
package: pkg,
|
|
1042
|
+
reason: "its default export is not a factory function"
|
|
1043
|
+
});
|
|
1044
|
+
def = await factory();
|
|
1045
|
+
if (!def || typeof def.setup !== "function") throw diagnostics$1.DF0070({
|
|
1046
|
+
package: pkg,
|
|
1047
|
+
reason: "its factory did not return a definition with a `setup` function"
|
|
1048
|
+
});
|
|
1049
|
+
if (typeof def.package !== "string" || def.package.length === 0) def = {
|
|
1050
|
+
...def,
|
|
1051
|
+
package: pkg
|
|
1052
|
+
};
|
|
1053
|
+
validateServiceDefinition(def);
|
|
1054
|
+
}
|
|
1055
|
+
for (const entry of entries) {
|
|
1056
|
+
const descriptor = entry.input;
|
|
1057
|
+
if (isServiceDefinition(entry.input) || typeof descriptor.version !== "string") continue;
|
|
1058
|
+
if (satisfiesVersionRange(def.version, descriptor.version)) continue;
|
|
1059
|
+
if (descriptor.required === true) throw diagnostics$1.DF0068({
|
|
1060
|
+
package: pkg,
|
|
1061
|
+
required: descriptor.version,
|
|
1062
|
+
installed: def.version
|
|
1063
|
+
});
|
|
1064
|
+
diagnostics$1.DF0069({
|
|
1065
|
+
package: pkg,
|
|
1066
|
+
required: descriptor.version,
|
|
1067
|
+
installed: def.version
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
const sets = entries.map((entry) => entry.input.options).filter((options) => options !== void 0);
|
|
1071
|
+
const options = def.mergeOptions ? def.mergeOptions(sets) : sets.length > 0 ? shallowMergeOptionSets(sets) : void 0;
|
|
1072
|
+
if (!this.context) throw diagnostics$1.DF0070({
|
|
1073
|
+
package: pkg,
|
|
1074
|
+
reason: "this services host has no node context to install into"
|
|
1075
|
+
});
|
|
1076
|
+
debug("installing service %s@%s (scope %s)", def.package, def.version, def.scope);
|
|
1077
|
+
const scoped = this.context.scope(def.scope);
|
|
1078
|
+
const api = await def.setup(scoped, options === void 0 ? {} : { options });
|
|
1079
|
+
this.installed.set(def.package, api);
|
|
1080
|
+
this.provide(def.package, api);
|
|
1081
|
+
await this.advertise(def);
|
|
1082
|
+
return api;
|
|
1083
|
+
}
|
|
1084
|
+
advertisementState() {
|
|
1085
|
+
return this.context.rpc.sharedState.get(DEVFRAME_SERVICES_STATE_KEY, { initialValue: {} });
|
|
1086
|
+
}
|
|
1087
|
+
async advertise(def) {
|
|
1088
|
+
const state = await this.advertisementState();
|
|
1089
|
+
const { package: pkg, version, scope, meta } = def;
|
|
1090
|
+
state.mutate((value) => {
|
|
1091
|
+
value[pkg] = {
|
|
1092
|
+
package: pkg,
|
|
1093
|
+
version,
|
|
1094
|
+
scope,
|
|
1095
|
+
...meta ? { meta } : {}
|
|
1096
|
+
};
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
802
1099
|
};
|
|
803
1100
|
//#endregion
|
|
804
1101
|
//#region src/node/host-views.ts
|
|
@@ -900,7 +1197,8 @@ function createNodeSettingsStore(context, namespace, scope) {
|
|
|
900
1197
|
let statePromise;
|
|
901
1198
|
function store() {
|
|
902
1199
|
if (!statePromise) {
|
|
903
|
-
const
|
|
1200
|
+
const dir = context.host.getStorageDir(STORAGE_SCOPE[scope]);
|
|
1201
|
+
const filepath = join(dir, "settings", `${namespace}.json`);
|
|
904
1202
|
statePromise = context.rpc.sharedState.get(stateKey, { sharedState: createStorage({
|
|
905
1203
|
filepath,
|
|
906
1204
|
initialValue: {}
|
|
@@ -1023,7 +1321,7 @@ async function createHostContext(options) {
|
|
|
1023
1321
|
context.rpc = rpcHost;
|
|
1024
1322
|
context.views = viewsHost;
|
|
1025
1323
|
context.diagnostics = diagnosticsHost;
|
|
1026
|
-
context.services = new DevframeServicesHostImpl();
|
|
1324
|
+
context.services = new DevframeServicesHostImpl(context);
|
|
1027
1325
|
context.agent = new DevframeAgentHost(context);
|
|
1028
1326
|
const scopedCache = /* @__PURE__ */ new Map();
|
|
1029
1327
|
context.scope = ((namespace) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
|
|
2
|
-
import { t as createStorage } from "./storage-
|
|
3
|
-
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-
|
|
2
|
+
import { t as createStorage } from "./storage-D4a99Ung.mjs";
|
|
3
|
+
import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-3Q6aDKWk.mjs";
|
|
4
4
|
import { join } from "pathe";
|
|
5
5
|
//#region src/node/hub-internals/context.ts
|
|
6
6
|
const internalContextMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as DevframeNodeContext,
|
|
1
|
+
import { g as DevframeNodeContext, gt as SharedState } from "./devframe-BlLEZR-x.mjs";
|
|
2
2
|
//#region src/node/hub-internals/context.d.ts
|
|
3
3
|
interface InternalAnonymousAuthStorage {
|
|
4
4
|
trusted: Record<string, {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
|
|
2
|
-
import { t as diagnostics } from "./diagnostics-
|
|
3
|
-
import { t as createHostContext } from "./context-
|
|
4
|
-
import { t as resolveStaticAssetsSource } from "./remote-assets-
|
|
5
|
-
import { i as resolveMcpConnectionMeta, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-
|
|
6
|
-
import { t as createH3DevframeHost } from "./host-h3-
|
|
7
|
-
import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-
|
|
2
|
+
import { t as diagnostics } from "./diagnostics-Di8ytitn.mjs";
|
|
3
|
+
import { t as createHostContext } from "./context-Bdkakk8S.mjs";
|
|
4
|
+
import { t as resolveStaticAssetsSource } from "./remote-assets-DRfY2RjX.mjs";
|
|
5
|
+
import { i as resolveMcpConnectionMeta, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-BM3PdYli.mjs";
|
|
6
|
+
import { t as createH3DevframeHost } from "./host-h3-fRbF9yor.mjs";
|
|
7
|
+
import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-DbDoozNM.mjs";
|
|
8
8
|
import { open } from "./utils/open.mjs";
|
|
9
9
|
import { mountStaticHandler } from "./utils/serve-static.mjs";
|
|
10
10
|
import { createServer } from "node:http";
|
|
11
|
-
import { resolve } from "pathe";
|
|
12
11
|
import process from "node:process";
|
|
12
|
+
import { resolve } from "pathe";
|
|
13
13
|
import { joinURL, withBase } from "ufo";
|
|
14
14
|
import { H3, toNodeHandler } from "h3";
|
|
15
15
|
//#region src/adapters/initiate.ts
|
|
@@ -39,10 +39,11 @@ function initDevframe(def, options) {
|
|
|
39
39
|
const base = normalizeBasePath(options.base);
|
|
40
40
|
const distDir = options.distDir === false ? void 0 : options.distDir ?? def.cli?.distDir;
|
|
41
41
|
const app = options.app ?? new H3();
|
|
42
|
+
const host = options.host ?? def.cli?.host ?? "localhost";
|
|
42
43
|
const shell = createInstanceShell({
|
|
43
44
|
base,
|
|
44
45
|
app,
|
|
45
|
-
host
|
|
46
|
+
host,
|
|
46
47
|
origin: options.origin,
|
|
47
48
|
auth: options.auth !== void 0 ? options.auth : def.cli?.auth,
|
|
48
49
|
server: options.server,
|
|
@@ -78,7 +79,9 @@ function initDevframe(def, options) {
|
|
|
78
79
|
host: hostImpl
|
|
79
80
|
});
|
|
80
81
|
const setupInfo = { flags: options.flags ?? {} };
|
|
82
|
+
for (const input of def.services ?? []) context.services.install(input, { resolveFrom: def.packageName });
|
|
81
83
|
await def.setup(context, setupInfo);
|
|
84
|
+
await context.services.ready();
|
|
82
85
|
const mcpOption = options.mcp ?? def.cli?.mcp;
|
|
83
86
|
const mcpMeta = resolveMcpConnectionMeta(def, mcpOption);
|
|
84
87
|
let mcpDispose;
|
|
@@ -87,7 +90,7 @@ function initDevframe(def, options) {
|
|
|
87
90
|
const mcpPath = joinURL(base, mcpMeta.path);
|
|
88
91
|
let mountMcpHttp;
|
|
89
92
|
try {
|
|
90
|
-
({mountMcpHttp} = await import("./http-
|
|
93
|
+
({mountMcpHttp} = await import("./http-BbHCU5Zs.mjs").then((n) => n.t));
|
|
91
94
|
} catch (error) {
|
|
92
95
|
const reason = error instanceof Error ? error.message : String(error);
|
|
93
96
|
throw diagnostics.DF0017({
|