devframe 0.9.5 → 0.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/dist/{_shared-CsBsnhuO.d.mts → _shared-BJ6AmwTu.d.mts} +1 -1
  3. package/dist/adapters/build.d.mts +1 -1
  4. package/dist/adapters/build.mjs +1 -1
  5. package/dist/adapters/cac.d.mts +1 -1
  6. package/dist/adapters/cac.mjs +3 -2
  7. package/dist/adapters/dev.d.mts +4 -4
  8. package/dist/adapters/dev.mjs +1 -1
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +3 -3
  11. package/dist/adapters/initiate.mjs +1 -1
  12. package/dist/adapters/mcp.d.mts +30 -26
  13. package/dist/adapters/mcp.mjs +559 -1
  14. package/dist/cli/main.mjs +1 -1
  15. package/dist/client/index.d.mts +5 -5
  16. package/dist/client/index.mjs +1 -64
  17. package/dist/constants.d.mts +13 -1
  18. package/dist/constants.mjs +1 -1
  19. package/dist/{context-c0OlSc12.d.mts → context-BhBzOXVa.d.mts} +3 -3
  20. package/dist/{context-DO0Qu1O8.d.mts → context-HOnG-WvG.d.mts} +1 -1
  21. package/dist/{context-Cx9V9Grq.mjs → context-KUqAfawQ.mjs} +1 -1
  22. package/dist/{context-Bc5S40Qt.mjs → context-omlma_1n.mjs} +3 -150
  23. package/dist/{dev-BRQA42Lh.mjs → dev-Czit5IP4.mjs} +3 -3
  24. package/dist/{events-DzolaBEs.mjs → events-4daw0uK9.mjs} +16 -2
  25. package/dist/in-page-channel/index.d.mts +306 -0
  26. package/dist/in-page-channel/index.mjs +816 -0
  27. package/dist/{index-DOg4S5wk.d.mts → index-BzyopqxV.d.mts} +1 -1
  28. package/dist/{index-B_DPNIT4.d.mts → index-DcKxIMoI.d.mts} +2 -2
  29. package/dist/index.d.mts +4 -4
  30. package/dist/{instance-shell-CPuYY8pw.d.mts → instance-shell-Bh4l9Vz_.d.mts} +3 -3
  31. package/dist/{instance-shell-fB5PyVtR.mjs → instance-shell-CpVW4zG0.mjs} +15 -2
  32. package/dist/internal/index.d.mts +16 -6
  33. package/dist/internal/index.mjs +3 -3
  34. package/dist/node/auth.d.mts +2 -2
  35. package/dist/node/auth.mjs +1 -1
  36. package/dist/node/hub-internals.d.mts +2 -2
  37. package/dist/node/hub-internals.mjs +1 -1
  38. package/dist/node/index.d.mts +3 -3
  39. package/dist/node/index.mjs +1 -1
  40. package/dist/recipes/common-rpc-functions.d.mts +3 -3
  41. package/dist/recipes/interactive-auth.d.mts +13 -2
  42. package/dist/recipes/interactive-auth.mjs +5 -1
  43. package/dist/{revoke-PihtvVil.mjs → revoke-COLW0cOi.mjs} +1 -1
  44. package/dist/rpc/dump.d.mts +1 -1
  45. package/dist/rpc/index.d.mts +2 -2
  46. package/dist/rpc/transports/sse-client.d.mts +1 -1
  47. package/dist/rpc/transports/sse-server.d.mts +2 -2
  48. package/dist/rpc/transports/ws-bun.d.mts +2 -2
  49. package/dist/rpc/transports/ws-client.d.mts +1 -1
  50. package/dist/rpc/transports/ws-deno.d.mts +2 -2
  51. package/dist/rpc/transports/ws-server.d.mts +1 -1
  52. package/dist/{rpc-core-Oyz-cJBC.d.mts → rpc-core-BHUsDl7s.d.mts} +3 -3
  53. package/dist/{host-agent-DbPWo0Bl.mjs → services-install-CqqQVls5.mjs} +150 -2
  54. package/dist/{sse-client-CdGD_B8_.d.mts → sse-client-DwX021ST.d.mts} +1 -1
  55. package/dist/types/index.d.mts +3 -3
  56. package/dist/{types-BBLeGfpt.d.mts → types-CMdPYhOx.d.mts} +1 -1
  57. package/dist/utils/events.d.mts +1 -1
  58. package/dist/utils/serve-static.mjs +1 -1
  59. package/dist/utils/shared-state.d.mts +1 -1
  60. package/dist/utils/streaming-channel.d.mts +1 -1
  61. package/dist/{ws-client-Bo2t6hES.d.mts → ws-client-7_lZi-B9.d.mts} +1 -1
  62. package/dist/{ws-server-DLtEoLdR.d.mts → ws-server-Di2oqr4q.d.mts} +1 -1
  63. package/package.json +2 -1
  64. package/skills/devframe/SKILL.md +86 -82
  65. package/dist/http-D7wWuy7-.mjs +0 -569
  66. package/dist/transports-vhizgqXM.mjs +0 -15
@@ -1,4 +1,4 @@
1
- import { t as DEVFRAME_EVENTS } from "./events-DzolaBEs.mjs";
1
+ import { t as DEVFRAME_EVENTS } from "./events-4daw0uK9.mjs";
2
2
  //#region src/constants.ts
3
3
  const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
4
4
  /**
@@ -1,6 +1,6 @@
1
- import { d as RpcFunctionsCollectorBase } from "./index-DOg4S5wk.mjs";
2
- import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-BBLeGfpt.mjs";
3
- import { d as DevframeRpcConnection, u as DevframeNodeRpcSessionMeta } from "./ws-server-DLtEoLdR.mjs";
1
+ import { d as RpcFunctionsCollectorBase } from "./index-BzyopqxV.mjs";
2
+ import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-CMdPYhOx.mjs";
3
+ import { d as DevframeRpcConnection, u as DevframeNodeRpcSessionMeta } from "./ws-server-Di2oqr4q.mjs";
4
4
  import { a as RemoteAssetsStore, o as StaticAssetsSource } from "./remote-assets-B13_vRkJ.mjs";
5
5
  import { m as defineDiagnostics } from "./nostics-SPGDgHEP.mjs";
6
6
  import { BirpcReturn } from "birpc";
@@ -1,4 +1,4 @@
1
- import { k as SharedState, o as DevframeNodeContext } from "./context-c0OlSc12.mjs";
1
+ import { k as SharedState, o as DevframeNodeContext } from "./context-BhBzOXVa.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -1,6 +1,6 @@
1
1
  import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
2
2
  import { t as createStorage } from "./storage-BXNVhKyR.mjs";
3
- import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-PihtvVil.mjs";
3
+ import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-COLW0cOi.mjs";
4
4
  import { join } from "pathe";
5
5
  //#region src/node/hub-internals/context.ts
6
6
  const internalContextMap = /* @__PURE__ */ new WeakMap();
@@ -2,17 +2,15 @@ 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 DEVFRAME_EVENTS } from "./events-DzolaBEs.mjs";
5
+ import { t as DEVFRAME_EVENTS } from "./events-4daw0uK9.mjs";
6
6
  import { DEVFRAME_SERVICES_STATE_KEY } from "./constants.mjs";
7
7
  import { t as diagnostics$1 } from "./diagnostics-BXWW3VK-.mjs";
8
- import { r as createEventEmitter, t as DevframeAgentHost } from "./host-agent-DbPWo0Bl.mjs";
8
+ import { a as DevframeAgentHost, i as satisfiesVersionRange, n as expandResolveFrom, r as importServicePackage, s as createEventEmitter, t as deepMergeOptionSets } from "./services-install-CqqQVls5.mjs";
9
9
  import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-DezQmPQU.mjs";
10
10
  import { t as createStorage } from "./storage-BXNVhKyR.mjs";
11
- import { createRequire } from "node:module";
12
11
  import { createSharedState } from "devframe/utils/shared-state";
13
12
  import process from "node:process";
14
- import { pathToFileURL } from "node:url";
15
- import { isAbsolute, join } from "pathe";
13
+ import { join } from "pathe";
16
14
  import { existsSync } from "node:fs";
17
15
  //#region src/node/host-diagnostics.ts
18
16
  var DevframeDiagnosticsHost = class {
@@ -763,151 +761,6 @@ var RpcFunctionsHostImpl = class extends RpcFunctionsCollectorBase {
763
761
  }
764
762
  };
765
763
  //#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 (the common case: the declaring plugin's
780
- * `importMetaUrl`, so a service it declares resolves against the plugin's own
781
- * dependencies); a bare npm package name resolves to that package's location
782
- * from `cwd`. 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(
813
- /* webpackIgnore: true */
814
- /* @vite-ignore */
815
- /* turbopackIgnore: true */
816
- pathToFileURL(resolved).href
817
- );
818
- }
819
- throw lastError;
820
- }
821
- function parseVersion(input) {
822
- const [core, ...prerelease] = input.trim().replace(/^v/, "").split("-");
823
- if (!core) return void 0;
824
- const parts = core.split(".").map((part) => Number.parseInt(part, 10));
825
- if (parts.length === 0 || parts.some((part) => Number.isNaN(part) || part < 0)) return void 0;
826
- while (parts.length < 3) parts.push(0);
827
- return {
828
- parts,
829
- ...prerelease.length ? { prerelease: prerelease.join("-") } : {}
830
- };
831
- }
832
- function compareVersions(a, b) {
833
- for (let i = 0; i < 3; i++) {
834
- const diff = (a.parts[i] ?? 0) - (b.parts[i] ?? 0);
835
- if (diff !== 0) return diff;
836
- }
837
- if (a.prerelease && !b.prerelease) return -1;
838
- if (!a.prerelease && b.prerelease) return 1;
839
- if (a.prerelease && b.prerelease) return a.prerelease < b.prerelease ? -1 : a.prerelease > b.prerelease ? 1 : 0;
840
- return 0;
841
- }
842
- function satisfiesComparator(version, comparator) {
843
- const raw = comparator.trim();
844
- if (!raw || raw === "*" || raw === "x") return true;
845
- const operatorMatch = raw.match(/^([\^~]|>=|<=|[><=])?(.+)$/);
846
- if (!operatorMatch) return false;
847
- const operator = operatorMatch[1];
848
- const rest = operatorMatch[2].trim();
849
- const segments = rest.replace(/\.[x*]/gi, "").split(".").filter(Boolean);
850
- const base = parseVersion(rest.replace(/[x*]/gi, "0"));
851
- if (!base) return false;
852
- switch (operator) {
853
- case ">": return compareVersions(version, base) > 0;
854
- case ">=": return compareVersions(version, base) >= 0;
855
- case "<": return compareVersions(version, base) < 0;
856
- case "<=": return compareVersions(version, base) <= 0;
857
- case "^": {
858
- if (compareVersions(version, base) < 0) return false;
859
- const fixedIndex = base.parts.findIndex((part) => part !== 0);
860
- const lockUpTo = fixedIndex === -1 ? base.parts.length - 1 : fixedIndex;
861
- for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
862
- return true;
863
- }
864
- case "~": {
865
- if (compareVersions(version, base) < 0) return false;
866
- const lockUpTo = segments.length >= 2 ? 1 : 0;
867
- for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
868
- return true;
869
- }
870
- default:
871
- 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;
872
- return segments.length >= 3 ? compareVersions(version, base) === 0 : true;
873
- }
874
- }
875
- /**
876
- * Pragmatic semver range check for service version declarations — supports
877
- * the common forms (`1.2.3`, `^1.2.3`, `~1.2`, `>=1 <3`, `1.x`, `*`, and
878
- * `||`-joined alternatives) without pulling in a semver dependency. An
879
- * unparseable version or range reads as **not satisfied**.
880
- */
881
- function satisfiesVersionRange(version, range) {
882
- const parsed = parseVersion(version);
883
- if (!parsed) return false;
884
- const alternatives = range.split("||").map((alt) => alt.trim()).filter(Boolean);
885
- if (alternatives.length === 0) return true;
886
- return alternatives.some((alternative) => alternative.split(/\s+/).every((comparator) => satisfiesComparator(parsed, comparator)));
887
- }
888
- function isPlainObject(value) {
889
- return typeof value === "object" && value !== null && !Array.isArray(value);
890
- }
891
- /** Deep-merge two values with the service option-set rules (see below). */
892
- function deepMergeTwo(a, b) {
893
- if (Array.isArray(a) && Array.isArray(b)) return [.../* @__PURE__ */ new Set([...a, ...b])];
894
- if (isPlainObject(a) && isPlainObject(b)) {
895
- const out = { ...a };
896
- for (const key of Object.keys(b)) out[key] = key in a ? deepMergeTwo(a[key], b[key]) : b[key];
897
- return out;
898
- }
899
- return b;
900
- }
901
- /**
902
- * Default option-set merge when a service declares no `mergeOptions`:
903
- * deep-merge in declaration order — objects recurse, arrays union-dedupe,
904
- * scalars take the later value. Covers the built-in services (`roots` /
905
- * `langs` union, `themes` per-key last-wins) without a custom hook.
906
- */
907
- function deepMergeOptionSets(sets) {
908
- return sets.reduce((merged, set) => deepMergeTwo(merged, set));
909
- }
910
- //#endregion
911
764
  //#region src/node/host-services.ts
912
765
  const debug = createDebug("devframe:services");
913
766
  function isServiceDefinition(input) {
@@ -1,11 +1,11 @@
1
1
  import { resolveClientAssets } from "./index.mjs";
2
2
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
3
3
  import { t as diagnostics } from "./diagnostics-BXWW3VK-.mjs";
4
- import { t as createHostContext } from "./context-Bc5S40Qt.mjs";
4
+ import { t as createHostContext } from "./context-omlma_1n.mjs";
5
5
  import { t as resolveStaticAssetsSource } from "./remote-assets-DezQmPQU.mjs";
6
6
  import { i as resolveMcpConnectionMeta, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-BM3PdYli.mjs";
7
7
  import { t as createH3DevframeHost } from "./host-h3-fRbF9yor.mjs";
8
- import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-fB5PyVtR.mjs";
8
+ import { a as importRuntimeModule, i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-CpVW4zG0.mjs";
9
9
  import { open } from "./utils/open.mjs";
10
10
  import { mountStaticHandler } from "./utils/serve-static.mjs";
11
11
  import { createServer } from "node:http";
@@ -92,7 +92,7 @@ function initDevframe(def, options) {
92
92
  const mcpPath = joinURL(base, mcpMeta.path);
93
93
  let mountMcpHttp;
94
94
  try {
95
- ({mountMcpHttp} = await import("./http-D7wWuy7-.mjs").then((n) => n.t));
95
+ ({mountMcpHttp} = await importRuntimeModule("devframe/adapters/mcp"));
96
96
  } catch (error) {
97
97
  const reason = error instanceof Error ? error.message : String(error);
98
98
  throw diagnostics.DF0017({
@@ -5,7 +5,7 @@
5
5
  * broadcast notifications — so these names live in one place instead of
6
6
  * scattered string literals.
7
7
  *
8
- * **Keep this in sync with [`docs/content/1.guide/20.events.md`](../../../docs/content/1.guide/20.events.md)**
8
+ * **Keep this in sync with [`docs/content/8.references/3.events.md`](../../../docs/content/8.references/3.events.md)**
9
9
  * (the "Core devframe events" section): every name here appears in that page's
10
10
  * tables, and every name there resolves to an entry here. Add, rename, or
11
11
  * remove a name in both places in the same change, and reference
@@ -58,8 +58,22 @@ const DEVFRAME_EVENTS = {
58
58
  streamingEnd: "devframe:streaming:end",
59
59
  streamingUploadCancel: "devframe:streaming:upload-cancel"
60
60
  },
61
+ /**
62
+ * In-page channel notifications the page script pushes to its panels
63
+ * (page script → panel), `devframe:` prefix. The paired request methods
64
+ * (`devframe:in-page:page-state:subscribe`/`set`/`patch`) are call
65
+ * endpoints, not events, and are defined at their handlers
66
+ * (`in-page-channel/state.ts`).
67
+ */
68
+ inPageChannel: {
69
+ panelStateUpdated: "devframe:in-page:panel-state:updated",
70
+ panelStatePatch: "devframe:in-page:panel-state:patch"
71
+ },
61
72
  /** `postMessage` channels the runtime posts across window boundaries. */
62
- postMessage: { remoteAssetsError: "devframe:remote-assets-error" }
73
+ postMessage: {
74
+ remoteAssetsError: "devframe:remote-assets-error",
75
+ inPageChannel: "devframe:in-page-channel"
76
+ }
63
77
  };
64
78
  //#endregion
65
79
  export { DEVFRAME_EVENTS as t };
@@ -0,0 +1,306 @@
1
+ import { D as InferArgsType, E as Thenable, O as InferReturnType, T as RpcReturnSchema, i as RpcArgsSchema } from "../types-CMdPYhOx.mjs";
2
+ import { k as SharedState, zt as EventEmitter } from "../context-BhBzOXVa.mjs";
3
+ import { BirpcReturn } from "birpc";
4
+ //#region src/in-page-channel/types.d.ts
5
+ /**
6
+ * The shared contract of one in-page channel, declared once (usually in a
7
+ * `shared/protocol.ts` both sides import) and passed to both endpoints as a
8
+ * type parameter. Purely a type — the only runtime companion is the
9
+ * channel-name constant declared next to it.
10
+ */
11
+ interface InPageChannelProtocol {
12
+ /** Functions implemented by the page script, callable by panels. */
13
+ pageScript?: Record<string, (...args: any[]) => any>;
14
+ /** Functions implemented by panels, callable by the page script. */
15
+ panel?: Record<string, (...args: any[]) => any>;
16
+ /**
17
+ * Shared-state slots. The page script is the authority: it owns the
18
+ * canonical value; panels are seeded on connect and converge through
19
+ * syncId-deduplicated patches.
20
+ */
21
+ sharedStates?: Record<string, object>;
22
+ }
23
+ type SideFunctions<S> = S extends Record<string, (...args: any[]) => any> ? S : Record<string, never>;
24
+ type PageScriptFunctions<P extends InPageChannelProtocol> = SideFunctions<NonNullable<P['pageScript']>>;
25
+ type PanelFunctions<P extends InPageChannelProtocol> = SideFunctions<NonNullable<P['panel']>>;
26
+ type SharedStates<P extends InPageChannelProtocol> = P['sharedStates'] extends Record<string, object> ? P['sharedStates'] : Record<string, never>;
27
+ type FnArgs<F> = F extends ((...args: infer A) => any) ? A : never;
28
+ type FnReturn<F> = F extends ((...args: any[]) => infer R) ? Awaited<R> : never;
29
+ /**
30
+ * Types of an in-page channel function — `RpcFunctionType` minus the
31
+ * server-only `static`: `event` is fire-and-forget (the only type valid for
32
+ * fan-out), `action` performs, `query` requests data (the default).
33
+ */
34
+ type InPageFunctionType = 'action' | 'event' | 'query';
35
+ /**
36
+ * An in-page channel function definition — the `defineRpcFunction` authoring
37
+ * shape (`name`, `type`, Standard-Schema `args`/`returns`,
38
+ * `jsonSerializable`, `handler`) narrowed to the browser: there is no
39
+ * `dump`/`snapshot`/`cacheable`/`agent`. When `jsonSerializable` is `true`,
40
+ * payloads are strictly validated at the receiving endpoint and misshapen
41
+ * values reject the call with a descriptive `InPageChannelError` instead of
42
+ * a cryptic `DataCloneError` in the port.
43
+ */
44
+ type InPageFunctionDefinition<NAME extends string, TYPE extends InPageFunctionType = 'query', ARGS extends any[] = [], RETURN = void, AS extends RpcArgsSchema | undefined = undefined, RS extends RpcReturnSchema | undefined = undefined> = [AS, RS] extends [undefined, undefined] ? {
45
+ name: NAME;
46
+ type?: TYPE;
47
+ args?: AS;
48
+ returns?: RS;
49
+ jsonSerializable?: boolean;
50
+ handler: (...args: ARGS) => RETURN;
51
+ } : {
52
+ name: NAME;
53
+ type?: TYPE;
54
+ /** Standard Schema array validating (and typing) the arguments. */
55
+ args: AS;
56
+ /** Standard Schema typing the resolved return value. */
57
+ returns: RS;
58
+ jsonSerializable?: boolean;
59
+ handler: (...args: InferArgsType<AS>) => Thenable<InferReturnType<RS>>;
60
+ };
61
+ /** Loosely-typed definition — the registration unit both endpoints accept. */
62
+ type InPageFunctionDefinitionAny = InPageFunctionDefinition<string, any, any, any, any, any>;
63
+ /**
64
+ * Connection lifecycle of a panel endpoint: `connecting` (handshake retry
65
+ * loop running, outgoing traffic buffered) → `connected` → back to
66
+ * `connecting` on port loss, or `closed` after `close()` (permanent).
67
+ */
68
+ type InPageChannelStatus = 'connecting' | 'connected' | 'closed';
69
+ interface InPageChannelCommonOptions {
70
+ /**
71
+ * Channel name, namespaced with the devframe id by convention
72
+ * (e.g. `devframes:plugin:a11y`). Both endpoints must use the same name.
73
+ */
74
+ name: string;
75
+ /** Implementations of this endpoint's side of the protocol. */
76
+ functions?: readonly InPageFunctionDefinitionAny[];
77
+ /**
78
+ * Origins accepted during the handshake (and used as `targetOrigin` when
79
+ * posting handshake messages). The in-page channel is same-origin by
80
+ * definition, so this defaults to `[location.origin]`.
81
+ */
82
+ allowedOrigins?: string[];
83
+ /**
84
+ * Timeout for request/response calls, in milliseconds; `-1` disables.
85
+ * Rejections are `InPageChannelError`s (code `timeout`) carrying the
86
+ * endpoint status, so a hanging call explains itself.
87
+ * @default 15000
88
+ */
89
+ callTimeoutMs?: number;
90
+ /**
91
+ * Liveness heartbeat guarding against silently dead ports. Defaults to a
92
+ * 5s ping / 12s silence window; pass `false` to disable — on both
93
+ * endpoints together.
94
+ */
95
+ heartbeat?: {
96
+ intervalMs?: number;
97
+ timeoutMs?: number;
98
+ } | false;
99
+ /**
100
+ * Applied to each outgoing argument and handler result before posting —
101
+ * the place to unwrap framework reactivity (Vue `toRaw`, Solid `unwrap`)
102
+ * into plain structured-cloneable values.
103
+ */
104
+ serialize?: (value: unknown) => unknown;
105
+ /** Applied to each incoming argument and call result. */
106
+ deserialize?: (value: unknown) => unknown;
107
+ }
108
+ /** Options for {@link createPageScriptChannel}. */
109
+ interface CreatePageScriptChannelOptions extends InPageChannelCommonOptions {
110
+ /**
111
+ * Window whose `message` events carry panel hellos. Defaults to the
112
+ * global `window`; pass `false` to skip the handshake listener entirely
113
+ * (bring-your-own ports via `addPanelPort` only).
114
+ */
115
+ window?: Window | false;
116
+ }
117
+ /** Options for {@link connectPanelChannel}. */
118
+ interface ConnectPanelChannelOptions extends InPageChannelCommonOptions {
119
+ /**
120
+ * The panel's own window (listens for the handshake grant). Defaults to
121
+ * the global `window`; pass `false` with `transport` to skip the handshake.
122
+ */
123
+ window?: Window | false;
124
+ /**
125
+ * Windows the hello is posted to. Defaults to the panel's ancestor chain
126
+ * plus its `opener` — the places a same-tab page script can live. When
127
+ * empty and no `transport` is given, the endpoint stays `connecting` and
128
+ * warns once.
129
+ */
130
+ targets?: Window[];
131
+ /** Pre-established port to the page script, bypassing the handshake. */
132
+ transport?: MessagePort;
133
+ /**
134
+ * Pin this panel to one page-script instance id. By default the panel
135
+ * auto-pairs with the most recent page script that answers — almost
136
+ * always "my own tab's page script".
137
+ */
138
+ instanceId?: string;
139
+ /**
140
+ * Base interval between handshake hello retries, in milliseconds; each
141
+ * retry backs off ×1.5 up to a 3s cap, forever (the page script may load
142
+ * later than the panel).
143
+ * @default 300
144
+ */
145
+ helloIntervalMs?: number;
146
+ /**
147
+ * Maximum `callEvent` payloads buffered while `connecting`, flushed on
148
+ * connect; when full, the oldest is dropped with a console warning.
149
+ * @default 64
150
+ */
151
+ eventBufferLimit?: number;
152
+ }
153
+ /**
154
+ * The channel shared-state accessor — mirrors `rpc.sharedState`, with the
155
+ * page script playing the server's role as rendezvous and authority. The
156
+ * page script's first `get` of a key must provide `initialValue`; a panel's
157
+ * `get` without one resolves once the authority's first replay arrives.
158
+ */
159
+ interface InPageSharedStateHost<P extends InPageChannelProtocol> {
160
+ get: <K extends keyof SharedStates<P> & string>(key: K, options?: {
161
+ initialValue?: SharedStates<P>[K];
162
+ }) => Promise<SharedState<SharedStates<P>[K]>>;
163
+ }
164
+ /** Emitter events of a page-script endpoint. */
165
+ interface PageScriptChannelEvents<P extends InPageChannelProtocol> {
166
+ 'panel:connected': (panel: PanelPeer<P>) => void;
167
+ 'panel:disconnected': (panel: PanelPeer<P>) => void;
168
+ }
169
+ /** One connected panel, as seen from the page script. */
170
+ interface PanelPeer<P extends InPageChannelProtocol> {
171
+ /** Unique id of the panel endpoint (stable across its lifetime, not reloads). */
172
+ readonly id: string;
173
+ /** Call one panel's function and await the result. */
174
+ call: <K extends keyof PanelFunctions<P> & string>(name: K, ...args: FnArgs<PanelFunctions<P>[K]>) => Promise<FnReturn<PanelFunctions<P>[K]>>;
175
+ /** Disconnect this panel. */
176
+ close: () => void;
177
+ }
178
+ /**
179
+ * The page-script endpoint of an in-page channel: answers panel handshakes,
180
+ * holds one dedicated port per connected panel, fans events out to all of
181
+ * them, and is the authority for the channel's shared states.
182
+ */
183
+ interface PageScriptChannel<P extends InPageChannelProtocol> {
184
+ readonly name: string;
185
+ /**
186
+ * This page context's instance id (persisted per tab in sessionStorage),
187
+ * carried in every handshake so panels can pin to one instance when the
188
+ * same app is open in several tabs.
189
+ */
190
+ readonly instanceId: string;
191
+ /** Currently connected panels. */
192
+ readonly panels: readonly PanelPeer<P>[];
193
+ readonly events: Pick<EventEmitter<PageScriptChannelEvents<P>>, 'on' | 'once'>;
194
+ /**
195
+ * Fan a fire-and-forget event out to every connected panel; panels that
196
+ * don't implement the function ignore it.
197
+ */
198
+ callEvent: <K extends keyof PanelFunctions<P> & string>(name: K, ...args: FnArgs<PanelFunctions<P>[K]>) => void;
199
+ /** Page-script-authoritative shared states, replayed to joining panels. */
200
+ readonly sharedState: InPageSharedStateHost<P>;
201
+ /** Adopt a pre-established port as a panel peer (bring-your-own transport). */
202
+ addPanelPort: (port: MessagePort) => PanelPeer<P>;
203
+ /** Tear the endpoint down: disconnect every panel, stop answering hellos. */
204
+ close: () => void;
205
+ }
206
+ /** Emitter events of a panel endpoint. */
207
+ interface PanelChannelEvents {
208
+ 'status:updated': (status: InPageChannelStatus) => void;
209
+ }
210
+ /**
211
+ * The panel endpoint of an in-page channel: finds the page script with a
212
+ * retrying same-origin handshake, survives reloads on either side by
213
+ * re-handshaking, and buffers outgoing traffic while `connecting`.
214
+ */
215
+ interface PanelChannel<P extends InPageChannelProtocol> {
216
+ readonly name: string;
217
+ readonly status: InPageChannelStatus;
218
+ /** The paired page script's instance id, once connected. */
219
+ readonly pageScript: {
220
+ instanceId: string;
221
+ } | undefined;
222
+ readonly events: Pick<EventEmitter<PanelChannelEvents>, 'on' | 'once'>;
223
+ /**
224
+ * Resolves once connected. With `timeoutMs`, rejects with an
225
+ * `InPageChannelError` (code `timeout`) when no page script answered in
226
+ * time — the hook for a "no page script found" fallback UI.
227
+ */
228
+ whenConnected: (timeoutMs?: number) => Promise<void>;
229
+ /**
230
+ * Call a page-script function and await the result. While `connecting`
231
+ * the call is buffered and sent on connect; it rejects with code
232
+ * `timeout` when `callTimeoutMs` elapses first.
233
+ */
234
+ call: <K extends keyof PageScriptFunctions<P> & string>(name: K, ...args: FnArgs<PageScriptFunctions<P>[K]>) => Promise<FnReturn<PageScriptFunctions<P>[K]>>;
235
+ /**
236
+ * Fire-and-forget to the page script. While `connecting` the event is
237
+ * buffered (up to `eventBufferLimit`) and flushed on connect.
238
+ */
239
+ callEvent: <K extends keyof PageScriptFunctions<P> & string>(name: K, ...args: FnArgs<PageScriptFunctions<P>[K]>) => void;
240
+ /** Shared states mirrored from the page-script authority. */
241
+ readonly sharedState: InPageSharedStateHost<P>;
242
+ /** Tear the endpoint down permanently. */
243
+ close: () => void;
244
+ }
245
+ //#endregion
246
+ //#region src/in-page-channel/internal.d.ts
247
+ /** Stable failure codes of the in-page channel. */
248
+ type InPageChannelErrorCode = 'timeout' |
249
+ /** The endpoint was closed (or closed while calls were pending). */
250
+ 'closed' |
251
+ /** A `jsonSerializable` payload contained a non-JSON value. */
252
+ 'not-serializable' |
253
+ /** The port refused to clone a payload (`DataCloneError`). */
254
+ 'not-cloneable' |
255
+ /** Standard-Schema validation of incoming arguments failed. */
256
+ 'invalid-args' |
257
+ /** A shared-state key was first accessed without its initial value. */
258
+ 'state-uninitialized';
259
+ /** A coded in-page channel error; every failure mode carries a stable `code`. */
260
+ declare class InPageChannelError extends Error {
261
+ readonly code: InPageChannelErrorCode;
262
+ name: string;
263
+ constructor(code: InPageChannelErrorCode, message: string, options?: {
264
+ cause?: unknown;
265
+ });
266
+ }
267
+ //#endregion
268
+ //#region src/in-page-channel/page-script.d.ts
269
+ /**
270
+ * Create the page-script endpoint of an in-page channel.
271
+ *
272
+ * It listens for panel hellos on the host page's window and answers each
273
+ * with a dedicated `MessageChannel` port (same-origin enforced both ways),
274
+ * keeping one live peer per panel — dock iframe, popup, and Document-PiP
275
+ * panels all handshake the same way, and a panel reload is simply a new
276
+ * handshake. No server is involved at any point.
277
+ */
278
+ declare function createPageScriptChannel<P extends InPageChannelProtocol>(options: CreatePageScriptChannelOptions): PageScriptChannel<P>;
279
+ //#endregion
280
+ //#region src/in-page-channel/panel.d.ts
281
+ /**
282
+ * Connect the panel endpoint of an in-page channel.
283
+ *
284
+ * The panel initiates: it posts a versioned hello to every window a
285
+ * same-tab page script can live in (its ancestor chain and its `opener`),
286
+ * retrying with backoff until one answers with a dedicated port — so boot
287
+ * order never matters, and a reload of either side is just a re-handshake
288
+ * (`WindowProxy` references survive navigations). While `connecting`,
289
+ * outgoing calls and events are buffered; when no page script exists at all
290
+ * (e.g. the panel opened standalone), the endpoint stays `connecting` and
291
+ * the UI can key a fallback state off `status` / `whenConnected()`.
292
+ */
293
+ declare function connectPanelChannel<P extends InPageChannelProtocol>(options: ConnectPanelChannelOptions): PanelChannel<P>;
294
+ //#endregion
295
+ //#region src/in-page-channel/index.d.ts
296
+ /**
297
+ * Define one in-page channel function — the `defineRpcFunction` authoring
298
+ * shape narrowed to the browser (see {@link InPageFunctionDefinition}).
299
+ * Pure identity: it only types the definition. Functions live in
300
+ * side-specific files and are passed to their endpoint via `functions`; the
301
+ * shared protocol file carries only the contract type and the channel-name
302
+ * constant.
303
+ */
304
+ declare function defineChannelFunction<NAME extends string, TYPE extends InPageFunctionType, ARGS extends any[], RETURN = void, const AS extends RpcArgsSchema | undefined = undefined, const RS extends RpcReturnSchema | undefined = undefined>(definition: InPageFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS>): InPageFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS>;
305
+ //#endregion
306
+ export { type ConnectPanelChannelOptions, type CreatePageScriptChannelOptions, InPageChannelError, type InPageChannelErrorCode, type InPageChannelProtocol, type InPageChannelStatus, type InPageFunctionDefinition, type PageScriptChannel, type PanelChannel, type PanelPeer, connectPanelChannel, createPageScriptChannel, defineChannelFunction };