@immediately-run/sdk 0.44.0 → 0.45.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/ambient.d.ts +47 -0
- package/dist/auth.cjs +3 -2
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js +3 -2
- package/dist/auth.js.map +1 -1
- package/dist/boot.cjs +3 -2
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.js +3 -2
- package/dist/boot.js.map +1 -1
- package/dist/catalog.cjs +3 -2
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js +3 -2
- package/dist/catalog.js.map +1 -1
- package/dist/contribute.cjs +2 -1
- package/dist/contribute.cjs.map +1 -1
- package/dist/contribute.js +2 -1
- package/dist/contribute.js.map +1 -1
- package/dist/debug.cjs +6 -5
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.js +12 -5
- package/dist/debug.js.map +1 -1
- package/dist/diagnostics.cjs +3 -2
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.js +3 -2
- package/dist/diagnostics.js.map +1 -1
- package/dist/dnd.cjs +5 -3
- package/dist/dnd.cjs.map +1 -1
- package/dist/dnd.js +5 -3
- package/dist/dnd.js.map +1 -1
- package/dist/editor.cjs +3 -1
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +3 -1
- package/dist/editor.js.map +1 -1
- package/dist/editorContext.cjs +3 -2
- package/dist/editorContext.cjs.map +1 -1
- package/dist/editorContext.js +3 -2
- package/dist/editorContext.js.map +1 -1
- package/dist/formFactor.cjs +3 -2
- package/dist/formFactor.cjs.map +1 -1
- package/dist/formFactor.js +3 -2
- package/dist/formFactor.js.map +1 -1
- package/dist/generated/protocol.cjs +23 -0
- package/dist/generated/protocol.cjs.map +1 -0
- package/dist/generated/protocol.d.cts +1 -0
- package/dist/generated/protocol.d.ts +1 -0
- package/dist/generated/protocol.js +2 -0
- package/dist/generated/protocol.js.map +1 -0
- package/dist/hooks.cjs +22 -14
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +26 -4
- package/dist/hooks.d.ts +26 -4
- package/dist/hooks.js +23 -15
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc.cjs +5 -3
- package/dist/ipc.cjs.map +1 -1
- package/dist/ipc.js +5 -3
- package/dist/ipc.js.map +1 -1
- package/dist/launch.cjs +5 -3
- package/dist/launch.cjs.map +1 -1
- package/dist/launch.js +5 -3
- package/dist/launch.js.map +1 -1
- package/dist/llm.cjs +3 -2
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.js +3 -2
- package/dist/llm.js.map +1 -1
- package/dist/metadataSource.cjs +53 -0
- package/dist/metadataSource.cjs.map +1 -0
- package/dist/metadataSource.d.cts +51 -0
- package/dist/metadataSource.d.ts +51 -0
- package/dist/metadataSource.js +29 -0
- package/dist/metadataSource.js.map +1 -0
- package/dist/moduleCache.cjs +2 -1
- package/dist/moduleCache.cjs.map +1 -1
- package/dist/moduleCache.js +2 -1
- package/dist/moduleCache.js.map +1 -1
- package/dist/mounts.cjs +13 -10
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.js +23 -10
- package/dist/mounts.js.map +1 -1
- package/dist/netFetch.cjs +4 -2
- package/dist/netFetch.cjs.map +1 -1
- package/dist/netFetch.js +4 -2
- package/dist/netFetch.js.map +1 -1
- package/dist/onFsChange.cjs +2 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.js +2 -1
- package/dist/onFsChange.js.map +1 -1
- package/dist/protocolSchemes.cjs +46 -0
- package/dist/protocolSchemes.cjs.map +1 -0
- package/dist/protocolSchemes.d.cts +18 -0
- package/dist/protocolSchemes.d.ts +18 -0
- package/dist/protocolSchemes.js +37 -0
- package/dist/protocolSchemes.js.map +1 -0
- package/dist/routing.cjs +2 -1
- package/dist/routing.cjs.map +1 -1
- package/dist/routing.js +2 -1
- package/dist/routing.js.map +1 -1
- package/dist/runtime.cjs +4 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +4 -2
- package/dist/runtime.js.map +1 -1
- package/dist/sandboxTypes.cjs.map +1 -1
- package/dist/sandboxTypes.d.cts +40 -6
- package/dist/sandboxTypes.d.ts +40 -6
- package/dist/secrets.cjs +5 -3
- package/dist/secrets.cjs.map +1 -1
- package/dist/secrets.js +5 -3
- package/dist/secrets.js.map +1 -1
- package/dist/tasks.cjs +6 -4
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +6 -4
- package/dist/tasks.js.map +1 -1
- package/dist/theme.cjs +5 -3
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.js +5 -3
- package/dist/theme.js.map +1 -1
- package/dist/urlUtils.cjs +20 -11
- package/dist/urlUtils.cjs.map +1 -1
- package/dist/urlUtils.d.cts +4 -10
- package/dist/urlUtils.d.ts +4 -10
- package/dist/urlUtils.js +18 -9
- package/dist/urlUtils.js.map +1 -1
- package/dist/vcs.cjs +5 -3
- package/dist/vcs.cjs.map +1 -1
- package/dist/vcs.js +5 -3
- package/dist/vcs.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +11 -4
package/dist/launch.cjs
CHANGED
|
@@ -29,9 +29,11 @@ __export(launch_exports, {
|
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(launch_exports);
|
|
31
31
|
var import_sandboxUtils = require("./sandboxUtils");
|
|
32
|
+
var import_protocol = require("./generated/protocol");
|
|
33
|
+
var import_protocolSchemes = require("./protocolSchemes");
|
|
32
34
|
var _status, _dismissListeners, _ended;
|
|
33
35
|
const liveHandles = /* @__PURE__ */ new Map();
|
|
34
|
-
(0, import_sandboxUtils.addListener)(
|
|
36
|
+
(0, import_sandboxUtils.addListener)(import_protocol.LAUNCH_ENDED, (m) => {
|
|
35
37
|
const h = liveHandles.get(m.launchId);
|
|
36
38
|
if (h) h._end(m.status);
|
|
37
39
|
});
|
|
@@ -48,7 +50,7 @@ class LaunchHandleImpl {
|
|
|
48
50
|
}
|
|
49
51
|
dismiss() {
|
|
50
52
|
if (__privateGet(this, _ended)) return;
|
|
51
|
-
(0, import_sandboxUtils.sendMessage)(
|
|
53
|
+
(0, import_sandboxUtils.sendMessage)(import_protocol.LAUNCH_DISMISS, { launchId: this.launchId });
|
|
52
54
|
}
|
|
53
55
|
onDismiss(cb) {
|
|
54
56
|
if (__privateGet(this, _ended)) {
|
|
@@ -81,7 +83,7 @@ _status = new WeakMap();
|
|
|
81
83
|
_dismissListeners = new WeakMap();
|
|
82
84
|
_ended = new WeakMap();
|
|
83
85
|
const launch = async (target, opts) => {
|
|
84
|
-
const res = await (0, import_sandboxUtils.protocolRequest)(
|
|
86
|
+
const res = await (0, import_sandboxUtils.protocolRequest)(import_protocolSchemes.SCHEMES[import_protocol.PROTOCOL_LAUNCH], "create", [{ target, opts }]);
|
|
85
87
|
if (!res || res.ok !== true || !res.data?.launchId) {
|
|
86
88
|
const code = res && res.ok === false ? res.code ?? "unknown" : "unknown";
|
|
87
89
|
return { ok: false, code };
|
package/dist/launch.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? (res.code ?? 'unknown') : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,0BAA0D;AAC1D,sBAA8D;AAC9D,6BAAwB;AAbxB;AA0FA,MAAM,cAAc,oBAAI,IAA8B;AAAA,IAKtD,iCAAY,8BAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,yCAAY,gCAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,+BAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAS,IAAI,QAAQ,YAAa;AACjE,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
package/dist/launch.js
CHANGED
|
@@ -5,8 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-VHAA22YE.js";
|
|
6
6
|
var _status, _dismissListeners, _ended;
|
|
7
7
|
import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
|
|
8
|
+
import { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from "./generated/protocol";
|
|
9
|
+
import { SCHEMES } from "./protocolSchemes";
|
|
8
10
|
const liveHandles = /* @__PURE__ */ new Map();
|
|
9
|
-
addListener(
|
|
11
|
+
addListener(LAUNCH_ENDED, (m) => {
|
|
10
12
|
const h = liveHandles.get(m.launchId);
|
|
11
13
|
if (h) h._end(m.status);
|
|
12
14
|
});
|
|
@@ -23,7 +25,7 @@ class LaunchHandleImpl {
|
|
|
23
25
|
}
|
|
24
26
|
dismiss() {
|
|
25
27
|
if (__privateGet(this, _ended)) return;
|
|
26
|
-
sendMessage(
|
|
28
|
+
sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });
|
|
27
29
|
}
|
|
28
30
|
onDismiss(cb) {
|
|
29
31
|
if (__privateGet(this, _ended)) {
|
|
@@ -56,7 +58,7 @@ _status = new WeakMap();
|
|
|
56
58
|
_dismissListeners = new WeakMap();
|
|
57
59
|
_ended = new WeakMap();
|
|
58
60
|
const launch = async (target, opts) => {
|
|
59
|
-
const res = await protocolRequest(
|
|
61
|
+
const res = await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], "create", [{ target, opts }]);
|
|
60
62
|
if (!res || res.ok !== true || !res.data?.launchId) {
|
|
61
63
|
const code = res && res.ok === false ? res.code ?? "unknown" : "unknown";
|
|
62
64
|
return { ok: false, code };
|
package/dist/launch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? (res.code ?? 'unknown') : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;AAAA;AAWA,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,gBAAgB,cAAc,uBAAuB;AAC9D,SAAS,eAAe;AA6ExB,MAAM,cAAc,oBAAI,IAA8B;AAKtD,YAAY,cAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,gBAAY,gBAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,MAAM,gBAAgB,QAAQ,eAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAS,IAAI,QAAQ,YAAa;AACjE,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
package/dist/llm.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(llm_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(llm_exports);
|
|
27
27
|
var import_catalog = require("./catalog");
|
|
28
28
|
var import_pushChannel = require("./pushChannel");
|
|
29
|
+
var import_protocol = require("./generated/protocol");
|
|
29
30
|
function chat(req) {
|
|
30
31
|
const { signal, ...params } = req;
|
|
31
32
|
return (0, import_catalog.invokeStream)(
|
|
@@ -35,8 +36,8 @@ function chat(req) {
|
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
const channel = (0, import_pushChannel.createPushChannel)({
|
|
38
|
-
pushType:
|
|
39
|
-
requestType:
|
|
39
|
+
pushType: import_protocol.LLM_PROVIDER,
|
|
40
|
+
requestType: import_protocol.REQUEST_LLM_PROVIDER,
|
|
40
41
|
initial: null,
|
|
41
42
|
parse: (msg) => "provider" in msg ? msg.provider : void 0
|
|
42
43
|
});
|
package/dist/llm.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5); `tool-use`/`tool-result` only when it\n * advertises `features.tools` — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string } // data: base64, no data: URL prefix\n // A tool call the model emitted on a prior `assistant` turn — replay it in the\n // conversation so a follow-up request carries the agentic history. Pairs with the\n // streamed `tool-call` {@link ChatDelta} that first surfaced it.\n | { type: 'tool-use'; id: string; name: string; input: Record<string, unknown> }\n // The result of executing a `tool-use`, fed back so the model can continue. Carried\n // on a `user`/`tool`-role message; `toolCallId` matches the `tool-use` `id`.\n | { type: 'tool-result'; toolCallId: string; content: string; isError?: boolean };\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n /** Abort the completion mid-stream. When it fires, the SDK sends the host a cancel\n * frame so the host aborts the upstream provider request and STOPS BILLING the\n * user's key — not merely stops the app-side iterator (LLM_AND_AGENTS_SPEC §3.3\n * \"abort the in-flight LLM request\", R3-224). Not sent over the wire (an\n * `AbortSignal` isn't serializable); handled SDK-side. */\n signal?: AbortSignal;\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n // Peel `signal` out of the request before it becomes wire params — an AbortSignal\n // can't cross the postMessage boundary as data; it drives the SDK-side cancel frame.\n const { signal, ...params } = req;\n return invokeStream<ChatDelta, ChatResult>(\n 'llm:chat',\n params as unknown as Record<string, unknown>,\n signal,\n );\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\nimport { LLM_PROVIDER, REQUEST_LLM_PROVIDER } from './generated/protocol';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5); `tool-use`/`tool-result` only when it\n * advertises `features.tools` — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string } // data: base64, no data: URL prefix\n // A tool call the model emitted on a prior `assistant` turn — replay it in the\n // conversation so a follow-up request carries the agentic history. Pairs with the\n // streamed `tool-call` {@link ChatDelta} that first surfaced it.\n | { type: 'tool-use'; id: string; name: string; input: Record<string, unknown> }\n // The result of executing a `tool-use`, fed back so the model can continue. Carried\n // on a `user`/`tool`-role message; `toolCallId` matches the `tool-use` `id`.\n | { type: 'tool-result'; toolCallId: string; content: string; isError?: boolean };\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n /** Abort the completion mid-stream. When it fires, the SDK sends the host a cancel\n * frame so the host aborts the upstream provider request and STOPS BILLING the\n * user's key — not merely stops the app-side iterator (LLM_AND_AGENTS_SPEC §3.3\n * \"abort the in-flight LLM request\", R3-224). Not sent over the wire (an\n * `AbortSignal` isn't serializable); handled SDK-side. */\n signal?: AbortSignal;\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n // Peel `signal` out of the request before it becomes wire params — an AbortSignal\n // can't cross the postMessage boundary as data; it drives the SDK-side cancel frame.\n const { signal, ...params } = req;\n return invokeStream<ChatDelta, ChatResult>(\n 'llm:chat',\n params as unknown as Record<string, unknown>,\n signal,\n );\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: LLM_PROVIDER,\n requestType: REQUEST_LLM_PROVIDER,\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,qBAA6B;AAC7B,yBAAkC;AAClC,sBAAmD;AAiF5C,SAAS,KAAK,KAA+D;AAGlF,QAAM,EAAE,QAAQ,GAAG,OAAO,IAAI;AAC9B,aAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AA0BA,MAAM,cAAU,sCAA2C;AAAA,EACzD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QACN,cAAc,MAAO,IAAI,WAAuC;AACpE,CAAC;AAIM,MAAM,eAAe,MAA+B,QAAQ,IAAI;AAIhE,MAAM,uBAAuB,CAClC,aACiB,QAAQ,SAAS,QAAQ;AAIrC,MAAM,kBAAkB,MAA+B,QAAQ,IAAI;","names":[]}
|
package/dist/llm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "./chunk-VHAA22YE.js";
|
|
2
2
|
import { invokeStream } from "./catalog";
|
|
3
3
|
import { createPushChannel } from "./pushChannel";
|
|
4
|
+
import { LLM_PROVIDER, REQUEST_LLM_PROVIDER } from "./generated/protocol";
|
|
4
5
|
function chat(req) {
|
|
5
6
|
const { signal, ...params } = req;
|
|
6
7
|
return invokeStream(
|
|
@@ -10,8 +11,8 @@ function chat(req) {
|
|
|
10
11
|
);
|
|
11
12
|
}
|
|
12
13
|
const channel = createPushChannel({
|
|
13
|
-
pushType:
|
|
14
|
-
requestType:
|
|
14
|
+
pushType: LLM_PROVIDER,
|
|
15
|
+
requestType: REQUEST_LLM_PROVIDER,
|
|
15
16
|
initial: null,
|
|
16
17
|
parse: (msg) => "provider" in msg ? msg.provider : void 0
|
|
17
18
|
});
|
package/dist/llm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5); `tool-use`/`tool-result` only when it\n * advertises `features.tools` — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string } // data: base64, no data: URL prefix\n // A tool call the model emitted on a prior `assistant` turn — replay it in the\n // conversation so a follow-up request carries the agentic history. Pairs with the\n // streamed `tool-call` {@link ChatDelta} that first surfaced it.\n | { type: 'tool-use'; id: string; name: string; input: Record<string, unknown> }\n // The result of executing a `tool-use`, fed back so the model can continue. Carried\n // on a `user`/`tool`-role message; `toolCallId` matches the `tool-use` `id`.\n | { type: 'tool-result'; toolCallId: string; content: string; isError?: boolean };\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n /** Abort the completion mid-stream. When it fires, the SDK sends the host a cancel\n * frame so the host aborts the upstream provider request and STOPS BILLING the\n * user's key — not merely stops the app-side iterator (LLM_AND_AGENTS_SPEC §3.3\n * \"abort the in-flight LLM request\", R3-224). Not sent over the wire (an\n * `AbortSignal` isn't serializable); handled SDK-side. */\n signal?: AbortSignal;\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n // Peel `signal` out of the request before it becomes wire params — an AbortSignal\n // can't cross the postMessage boundary as data; it drives the SDK-side cancel frame.\n const { signal, ...params } = req;\n return invokeStream<ChatDelta, ChatResult>(\n 'llm:chat',\n params as unknown as Record<string, unknown>,\n signal,\n );\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\nimport { LLM_PROVIDER, REQUEST_LLM_PROVIDER } from './generated/protocol';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5); `tool-use`/`tool-result` only when it\n * advertises `features.tools` — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string } // data: base64, no data: URL prefix\n // A tool call the model emitted on a prior `assistant` turn — replay it in the\n // conversation so a follow-up request carries the agentic history. Pairs with the\n // streamed `tool-call` {@link ChatDelta} that first surfaced it.\n | { type: 'tool-use'; id: string; name: string; input: Record<string, unknown> }\n // The result of executing a `tool-use`, fed back so the model can continue. Carried\n // on a `user`/`tool`-role message; `toolCallId` matches the `tool-use` `id`.\n | { type: 'tool-result'; toolCallId: string; content: string; isError?: boolean };\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n /** Abort the completion mid-stream. When it fires, the SDK sends the host a cancel\n * frame so the host aborts the upstream provider request and STOPS BILLING the\n * user's key — not merely stops the app-side iterator (LLM_AND_AGENTS_SPEC §3.3\n * \"abort the in-flight LLM request\", R3-224). Not sent over the wire (an\n * `AbortSignal` isn't serializable); handled SDK-side. */\n signal?: AbortSignal;\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n // Peel `signal` out of the request before it becomes wire params — an AbortSignal\n // can't cross the postMessage boundary as data; it drives the SDK-side cancel frame.\n const { signal, ...params } = req;\n return invokeStream<ChatDelta, ChatResult>(\n 'llm:chat',\n params as unknown as Record<string, unknown>,\n signal,\n );\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: LLM_PROVIDER,\n requestType: REQUEST_LLM_PROVIDER,\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":";AAeA,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,cAAc,4BAA4B;AAiF5C,SAAS,KAAK,KAA+D;AAGlF,QAAM,EAAE,QAAQ,GAAG,OAAO,IAAI;AAC9B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AA0BA,MAAM,UAAU,kBAA2C;AAAA,EACzD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QACN,cAAc,MAAO,IAAI,WAAuC;AACpE,CAAC;AAIM,MAAM,eAAe,MAA+B,QAAQ,IAAI;AAIhE,MAAM,uBAAuB,CAClC,aACiB,QAAQ,SAAS,QAAQ;AAIrC,MAAM,kBAAkB,MAA+B,QAAQ,IAAI;","names":[]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var metadataSource_exports = {};
|
|
20
|
+
__export(metadataSource_exports, {
|
|
21
|
+
MetadataSource: () => MetadataSource,
|
|
22
|
+
useMetadataStore: () => useMetadataStore
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(metadataSource_exports);
|
|
25
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
var import_TinkerableContext = require("./TinkerableContext");
|
|
28
|
+
const MetadataSourceContext = (0, import_react.createContext)(void 0);
|
|
29
|
+
const MetadataSource = ({
|
|
30
|
+
value,
|
|
31
|
+
mode = "replace",
|
|
32
|
+
children
|
|
33
|
+
}) => {
|
|
34
|
+
const outer = (0, import_react.use)(MetadataSourceContext);
|
|
35
|
+
const host = (0, import_react.use)(import_TinkerableContext.TinkerableContext);
|
|
36
|
+
const provided = (0, import_react.useMemo)(() => {
|
|
37
|
+
if (mode !== "merge") return { filesMetadata: value };
|
|
38
|
+
const base = outer?.filesMetadata ?? host?.filesMetadata ?? {};
|
|
39
|
+
return { filesMetadata: { ...base, ...value } };
|
|
40
|
+
}, [value, mode, outer, host]);
|
|
41
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetadataSourceContext.Provider, { value: provided, children });
|
|
42
|
+
};
|
|
43
|
+
const useMetadataStore = () => {
|
|
44
|
+
const source = (0, import_react.use)(MetadataSourceContext);
|
|
45
|
+
const host = (0, import_react.use)(import_TinkerableContext.TinkerableContext);
|
|
46
|
+
return source?.filesMetadata ?? host?.filesMetadata ?? {};
|
|
47
|
+
};
|
|
48
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
49
|
+
0 && (module.exports = {
|
|
50
|
+
MetadataSource,
|
|
51
|
+
useMetadataStore
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=metadataSource.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metadataSource.tsx"],"sourcesContent":["// A supported surface for a viewer app to provide its own metadata store\n// (PLATFORM_LAYERING_SPEC §4 / S3, R3-276).\n//\n// WHAT IT REPLACES. A viewer that scans its own corpus — Grove under dispatch, any\n// app whose content is not the app-root MDX the sandbox pre-scans — had exactly one\n// way to get that map in front of the metadata hooks: re-provide the platform's own\n// context wholesale,\n//\n// <TinkerableContext.Provider value={{ ...host, filesMetadata: corpus.metadata }}>\n//\n// which is a fork of a platform contract in app code. It works until the platform\n// adds a field to `TinkerableState`, at which point every such app is silently\n// re-providing a stale copy of whatever it spread from — and nothing tells it.\n//\n// This narrows that to the one thing the app actually wanted to say: here is the\n// metadata store for my subtree. The platform stays free to grow `TinkerableState`.\n\nimport React, { createContext, use, useMemo } from 'react';\n\nimport { TinkerableContext } from './TinkerableContext';\nimport type { FilesMetadata, Metadata } from './sandboxTypes';\n\n/** How a provided store combines with the one already in scope. */\nexport type MetadataSourceMode =\n /** The provided map IS the store for descendants — the host's entries are not\n * visible. The default: a viewer that scanned its own corpus is describing a\n * different file space, not adding to the platform's. */\n | 'replace'\n /** The provided entries are layered OVER what is already in scope: a path the\n * provider does not name still resolves to the outer store's value. For a viewer\n * that augments the app-root scan rather than replacing it. */\n | 'merge';\n\ninterface MetadataSourceValue {\n filesMetadata: FilesMetadata<Metadata>;\n}\n\n/** Undefined outside any provider, which is how the hooks know to read the host's\n * store instead of an empty one — the two are NOT the same state. */\nconst MetadataSourceContext = createContext<MetadataSourceValue | undefined>(undefined);\n\nexport interface MetadataSourceProps {\n /** The metadata store to provide: path → frontmatter. */\n value: FilesMetadata<Metadata>;\n /** Default `'replace'`. See {@link MetadataSourceMode}. */\n mode?: MetadataSourceMode;\n children?: React.ReactNode;\n}\n\n/**\n * Provide a metadata store to `useMetadataQuery`, `useFileMetadata` and\n * `useAllMetadata` for everything rendered inside.\n *\n * ```tsx\n * <MetadataSource value={corpus.metadata}>\n * <Wiki />\n * </MetadataSource>\n * ```\n *\n * Nesting is allowed and the innermost provider wins; `mode=\"merge\"` layers over\n * whatever is in scope (an outer provider, else the host's store). Unmounting the\n * provider restores the outer store — there is no registry to clean up, so an app\n * cannot leak a stale source by forgetting to unregister.\n *\n * The value is used as given: pass a stable reference (a `useMemo`, module state)\n * if you care about the query hooks' identity-preservation, which is what keeps\n * their results usable in dependency arrays.\n */\nexport const MetadataSource = ({\n value,\n mode = 'replace',\n children,\n}: MetadataSourceProps): React.ReactElement => {\n const outer = use(MetadataSourceContext);\n const host = use(TinkerableContext);\n const provided = useMemo<MetadataSourceValue>(() => {\n if (mode !== 'merge') return { filesMetadata: value };\n const base = outer?.filesMetadata ?? host?.filesMetadata ?? {};\n return { filesMetadata: { ...base, ...value } };\n }, [value, mode, outer, host]);\n return (\n <MetadataSourceContext.Provider value={provided}>{children}</MetadataSourceContext.Provider>\n );\n};\n\n/**\n * The metadata store in scope: the nearest {@link MetadataSource}, else the host's.\n *\n * The metadata hooks read through this, so an app never has to know which of the\n * two answered. Exported because a component that wants the raw map without the\n * query machinery should not have to reach for `TinkerableContext` and get the\n * answer wrong when a provider is present.\n */\nexport const useMetadataStore = <T = Metadata,>(): FilesMetadata<T> => {\n const source = use(MetadataSourceContext);\n const host = use(TinkerableContext);\n return (source?.filesMetadata ?? host?.filesMetadata ?? {}) as FilesMetadata<T>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiFI;AAhEJ,mBAAmD;AAEnD,+BAAkC;AAoBlC,MAAM,4BAAwB,4BAA+C,MAAS;AA6B/E,MAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA,OAAO;AAAA,EACP;AACF,MAA+C;AAC7C,QAAM,YAAQ,kBAAI,qBAAqB;AACvC,QAAM,WAAO,kBAAI,0CAAiB;AAClC,QAAM,eAAW,sBAA6B,MAAM;AAClD,QAAI,SAAS,QAAS,QAAO,EAAE,eAAe,MAAM;AACpD,UAAM,OAAO,OAAO,iBAAiB,MAAM,iBAAiB,CAAC;AAC7D,WAAO,EAAE,eAAe,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE;AAAA,EAChD,GAAG,CAAC,OAAO,MAAM,OAAO,IAAI,CAAC;AAC7B,SACE,4CAAC,sBAAsB,UAAtB,EAA+B,OAAO,UAAW,UAAS;AAE/D;AAUO,MAAM,mBAAmB,MAAuC;AACrE,QAAM,aAAS,kBAAI,qBAAqB;AACxC,QAAM,WAAO,kBAAI,0CAAiB;AAClC,SAAQ,QAAQ,iBAAiB,MAAM,iBAAiB,CAAC;AAC3D;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
import { FilesMetadata, Metadata } from './sandboxTypes.cjs';
|
|
3
|
+
|
|
4
|
+
/** How a provided store combines with the one already in scope. */
|
|
5
|
+
type MetadataSourceMode =
|
|
6
|
+
/** The provided map IS the store for descendants — the host's entries are not
|
|
7
|
+
* visible. The default: a viewer that scanned its own corpus is describing a
|
|
8
|
+
* different file space, not adding to the platform's. */
|
|
9
|
+
'replace'
|
|
10
|
+
/** The provided entries are layered OVER what is already in scope: a path the
|
|
11
|
+
* provider does not name still resolves to the outer store's value. For a viewer
|
|
12
|
+
* that augments the app-root scan rather than replacing it. */
|
|
13
|
+
| 'merge';
|
|
14
|
+
interface MetadataSourceProps {
|
|
15
|
+
/** The metadata store to provide: path → frontmatter. */
|
|
16
|
+
value: FilesMetadata<Metadata>;
|
|
17
|
+
/** Default `'replace'`. See {@link MetadataSourceMode}. */
|
|
18
|
+
mode?: MetadataSourceMode;
|
|
19
|
+
children?: react__default.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Provide a metadata store to `useMetadataQuery`, `useFileMetadata` and
|
|
23
|
+
* `useAllMetadata` for everything rendered inside.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <MetadataSource value={corpus.metadata}>
|
|
27
|
+
* <Wiki />
|
|
28
|
+
* </MetadataSource>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Nesting is allowed and the innermost provider wins; `mode="merge"` layers over
|
|
32
|
+
* whatever is in scope (an outer provider, else the host's store). Unmounting the
|
|
33
|
+
* provider restores the outer store — there is no registry to clean up, so an app
|
|
34
|
+
* cannot leak a stale source by forgetting to unregister.
|
|
35
|
+
*
|
|
36
|
+
* The value is used as given: pass a stable reference (a `useMemo`, module state)
|
|
37
|
+
* if you care about the query hooks' identity-preservation, which is what keeps
|
|
38
|
+
* their results usable in dependency arrays.
|
|
39
|
+
*/
|
|
40
|
+
declare const MetadataSource: ({ value, mode, children, }: MetadataSourceProps) => react__default.ReactElement;
|
|
41
|
+
/**
|
|
42
|
+
* The metadata store in scope: the nearest {@link MetadataSource}, else the host's.
|
|
43
|
+
*
|
|
44
|
+
* The metadata hooks read through this, so an app never has to know which of the
|
|
45
|
+
* two answered. Exported because a component that wants the raw map without the
|
|
46
|
+
* query machinery should not have to reach for `TinkerableContext` and get the
|
|
47
|
+
* answer wrong when a provider is present.
|
|
48
|
+
*/
|
|
49
|
+
declare const useMetadataStore: <T = Metadata>() => FilesMetadata<T>;
|
|
50
|
+
|
|
51
|
+
export { MetadataSource, type MetadataSourceMode, type MetadataSourceProps, useMetadataStore };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import react__default from 'react';
|
|
2
|
+
import { FilesMetadata, Metadata } from './sandboxTypes.js';
|
|
3
|
+
|
|
4
|
+
/** How a provided store combines with the one already in scope. */
|
|
5
|
+
type MetadataSourceMode =
|
|
6
|
+
/** The provided map IS the store for descendants — the host's entries are not
|
|
7
|
+
* visible. The default: a viewer that scanned its own corpus is describing a
|
|
8
|
+
* different file space, not adding to the platform's. */
|
|
9
|
+
'replace'
|
|
10
|
+
/** The provided entries are layered OVER what is already in scope: a path the
|
|
11
|
+
* provider does not name still resolves to the outer store's value. For a viewer
|
|
12
|
+
* that augments the app-root scan rather than replacing it. */
|
|
13
|
+
| 'merge';
|
|
14
|
+
interface MetadataSourceProps {
|
|
15
|
+
/** The metadata store to provide: path → frontmatter. */
|
|
16
|
+
value: FilesMetadata<Metadata>;
|
|
17
|
+
/** Default `'replace'`. See {@link MetadataSourceMode}. */
|
|
18
|
+
mode?: MetadataSourceMode;
|
|
19
|
+
children?: react__default.ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Provide a metadata store to `useMetadataQuery`, `useFileMetadata` and
|
|
23
|
+
* `useAllMetadata` for everything rendered inside.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <MetadataSource value={corpus.metadata}>
|
|
27
|
+
* <Wiki />
|
|
28
|
+
* </MetadataSource>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Nesting is allowed and the innermost provider wins; `mode="merge"` layers over
|
|
32
|
+
* whatever is in scope (an outer provider, else the host's store). Unmounting the
|
|
33
|
+
* provider restores the outer store — there is no registry to clean up, so an app
|
|
34
|
+
* cannot leak a stale source by forgetting to unregister.
|
|
35
|
+
*
|
|
36
|
+
* The value is used as given: pass a stable reference (a `useMemo`, module state)
|
|
37
|
+
* if you care about the query hooks' identity-preservation, which is what keeps
|
|
38
|
+
* their results usable in dependency arrays.
|
|
39
|
+
*/
|
|
40
|
+
declare const MetadataSource: ({ value, mode, children, }: MetadataSourceProps) => react__default.ReactElement;
|
|
41
|
+
/**
|
|
42
|
+
* The metadata store in scope: the nearest {@link MetadataSource}, else the host's.
|
|
43
|
+
*
|
|
44
|
+
* The metadata hooks read through this, so an app never has to know which of the
|
|
45
|
+
* two answered. Exported because a component that wants the raw map without the
|
|
46
|
+
* query machinery should not have to reach for `TinkerableContext` and get the
|
|
47
|
+
* answer wrong when a provider is present.
|
|
48
|
+
*/
|
|
49
|
+
declare const useMetadataStore: <T = Metadata>() => FilesMetadata<T>;
|
|
50
|
+
|
|
51
|
+
export { MetadataSource, type MetadataSourceMode, type MetadataSourceProps, useMetadataStore };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import "./chunk-VHAA22YE.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, use, useMemo } from "react";
|
|
4
|
+
import { TinkerableContext } from "./TinkerableContext";
|
|
5
|
+
const MetadataSourceContext = createContext(void 0);
|
|
6
|
+
const MetadataSource = ({
|
|
7
|
+
value,
|
|
8
|
+
mode = "replace",
|
|
9
|
+
children
|
|
10
|
+
}) => {
|
|
11
|
+
const outer = use(MetadataSourceContext);
|
|
12
|
+
const host = use(TinkerableContext);
|
|
13
|
+
const provided = useMemo(() => {
|
|
14
|
+
if (mode !== "merge") return { filesMetadata: value };
|
|
15
|
+
const base = outer?.filesMetadata ?? host?.filesMetadata ?? {};
|
|
16
|
+
return { filesMetadata: { ...base, ...value } };
|
|
17
|
+
}, [value, mode, outer, host]);
|
|
18
|
+
return /* @__PURE__ */ jsx(MetadataSourceContext.Provider, { value: provided, children });
|
|
19
|
+
};
|
|
20
|
+
const useMetadataStore = () => {
|
|
21
|
+
const source = use(MetadataSourceContext);
|
|
22
|
+
const host = use(TinkerableContext);
|
|
23
|
+
return source?.filesMetadata ?? host?.filesMetadata ?? {};
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
MetadataSource,
|
|
27
|
+
useMetadataStore
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=metadataSource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metadataSource.tsx"],"sourcesContent":["// A supported surface for a viewer app to provide its own metadata store\n// (PLATFORM_LAYERING_SPEC §4 / S3, R3-276).\n//\n// WHAT IT REPLACES. A viewer that scans its own corpus — Grove under dispatch, any\n// app whose content is not the app-root MDX the sandbox pre-scans — had exactly one\n// way to get that map in front of the metadata hooks: re-provide the platform's own\n// context wholesale,\n//\n// <TinkerableContext.Provider value={{ ...host, filesMetadata: corpus.metadata }}>\n//\n// which is a fork of a platform contract in app code. It works until the platform\n// adds a field to `TinkerableState`, at which point every such app is silently\n// re-providing a stale copy of whatever it spread from — and nothing tells it.\n//\n// This narrows that to the one thing the app actually wanted to say: here is the\n// metadata store for my subtree. The platform stays free to grow `TinkerableState`.\n\nimport React, { createContext, use, useMemo } from 'react';\n\nimport { TinkerableContext } from './TinkerableContext';\nimport type { FilesMetadata, Metadata } from './sandboxTypes';\n\n/** How a provided store combines with the one already in scope. */\nexport type MetadataSourceMode =\n /** The provided map IS the store for descendants — the host's entries are not\n * visible. The default: a viewer that scanned its own corpus is describing a\n * different file space, not adding to the platform's. */\n | 'replace'\n /** The provided entries are layered OVER what is already in scope: a path the\n * provider does not name still resolves to the outer store's value. For a viewer\n * that augments the app-root scan rather than replacing it. */\n | 'merge';\n\ninterface MetadataSourceValue {\n filesMetadata: FilesMetadata<Metadata>;\n}\n\n/** Undefined outside any provider, which is how the hooks know to read the host's\n * store instead of an empty one — the two are NOT the same state. */\nconst MetadataSourceContext = createContext<MetadataSourceValue | undefined>(undefined);\n\nexport interface MetadataSourceProps {\n /** The metadata store to provide: path → frontmatter. */\n value: FilesMetadata<Metadata>;\n /** Default `'replace'`. See {@link MetadataSourceMode}. */\n mode?: MetadataSourceMode;\n children?: React.ReactNode;\n}\n\n/**\n * Provide a metadata store to `useMetadataQuery`, `useFileMetadata` and\n * `useAllMetadata` for everything rendered inside.\n *\n * ```tsx\n * <MetadataSource value={corpus.metadata}>\n * <Wiki />\n * </MetadataSource>\n * ```\n *\n * Nesting is allowed and the innermost provider wins; `mode=\"merge\"` layers over\n * whatever is in scope (an outer provider, else the host's store). Unmounting the\n * provider restores the outer store — there is no registry to clean up, so an app\n * cannot leak a stale source by forgetting to unregister.\n *\n * The value is used as given: pass a stable reference (a `useMemo`, module state)\n * if you care about the query hooks' identity-preservation, which is what keeps\n * their results usable in dependency arrays.\n */\nexport const MetadataSource = ({\n value,\n mode = 'replace',\n children,\n}: MetadataSourceProps): React.ReactElement => {\n const outer = use(MetadataSourceContext);\n const host = use(TinkerableContext);\n const provided = useMemo<MetadataSourceValue>(() => {\n if (mode !== 'merge') return { filesMetadata: value };\n const base = outer?.filesMetadata ?? host?.filesMetadata ?? {};\n return { filesMetadata: { ...base, ...value } };\n }, [value, mode, outer, host]);\n return (\n <MetadataSourceContext.Provider value={provided}>{children}</MetadataSourceContext.Provider>\n );\n};\n\n/**\n * The metadata store in scope: the nearest {@link MetadataSource}, else the host's.\n *\n * The metadata hooks read through this, so an app never has to know which of the\n * two answered. Exported because a component that wants the raw map without the\n * query machinery should not have to reach for `TinkerableContext` and get the\n * answer wrong when a provider is present.\n */\nexport const useMetadataStore = <T = Metadata,>(): FilesMetadata<T> => {\n const source = use(MetadataSourceContext);\n const host = use(TinkerableContext);\n return (source?.filesMetadata ?? host?.filesMetadata ?? {}) as FilesMetadata<T>;\n};\n"],"mappings":";AAiFI;AAhEJ,SAAgB,eAAe,KAAK,eAAe;AAEnD,SAAS,yBAAyB;AAoBlC,MAAM,wBAAwB,cAA+C,MAAS;AA6B/E,MAAM,iBAAiB,CAAC;AAAA,EAC7B;AAAA,EACA,OAAO;AAAA,EACP;AACF,MAA+C;AAC7C,QAAM,QAAQ,IAAI,qBAAqB;AACvC,QAAM,OAAO,IAAI,iBAAiB;AAClC,QAAM,WAAW,QAA6B,MAAM;AAClD,QAAI,SAAS,QAAS,QAAO,EAAE,eAAe,MAAM;AACpD,UAAM,OAAO,OAAO,iBAAiB,MAAM,iBAAiB,CAAC;AAC7D,WAAO,EAAE,eAAe,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE;AAAA,EAChD,GAAG,CAAC,OAAO,MAAM,OAAO,IAAI,CAAC;AAC7B,SACE,oBAAC,sBAAsB,UAAtB,EAA+B,OAAO,UAAW,UAAS;AAE/D;AAUO,MAAM,mBAAmB,MAAuC;AACrE,QAAM,SAAS,IAAI,qBAAqB;AACxC,QAAM,OAAO,IAAI,iBAAiB;AAClC,SAAQ,QAAQ,iBAAiB,MAAM,iBAAiB,CAAC;AAC3D;","names":[]}
|
package/dist/moduleCache.cjs
CHANGED
|
@@ -26,11 +26,12 @@ module.exports = __toCommonJS(moduleCache_exports);
|
|
|
26
26
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
27
27
|
var import_react = require("react");
|
|
28
28
|
var import_sandboxUtils = require("./sandboxUtils");
|
|
29
|
+
var import_protocol = require("./generated/protocol");
|
|
29
30
|
class ModuleCache {
|
|
30
31
|
constructor() {
|
|
31
32
|
this.nameResolutionPromises = {};
|
|
32
33
|
this.evaluationContextPromises = {};
|
|
33
|
-
(0, import_sandboxUtils.addListener)(
|
|
34
|
+
(0, import_sandboxUtils.addListener)(import_protocol.COMPILE, () => {
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
37
|
getCacheKey(mod, moduleName) {
|
package/dist/moduleCache.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/moduleCache.tsx"],"sourcesContent":["// based on: https://www.bbss.dev/posts/react-learn-suspense/#fetchcache-provider\n\nimport { createContext, ReactNode, useCallback, useState } from \"react\"\nimport { EvaluationContext } from \"./sandboxTypes\";\nimport { addListener } from \"./sandboxUtils\";\n\nexport class ModuleCache {\n nameResolutionPromises: Record<string, Promise<string>> = {}\n evaluationContextPromises: Record<string, Promise<EvaluationContext>> = {}\n\n constructor() {\n // reset cache on compile\n addListener(
|
|
1
|
+
{"version":3,"sources":["../src/moduleCache.tsx"],"sourcesContent":["// based on: https://www.bbss.dev/posts/react-learn-suspense/#fetchcache-provider\n\nimport { createContext, ReactNode, useCallback, useState } from \"react\"\nimport { EvaluationContext } from \"./sandboxTypes\";\nimport { addListener } from \"./sandboxUtils\";\nimport { COMPILE } from './generated/protocol';\n\nexport class ModuleCache {\n nameResolutionPromises: Record<string, Promise<string>> = {}\n evaluationContextPromises: Record<string, Promise<EvaluationContext>> = {}\n\n constructor() {\n // reset cache on compile\n addListener(COMPILE, () => {\n // NOTE: THIS CAUSES AN UNNECESSARY RELOAD\n // the <Include> component's module evaluation context promise is replaced\n // with a new promise for the same value when a compilation occurs which\n // doesn't affect the current module. Commenting out the following lines\n // eliminates the unnecessary component state loss.\n // this.nameResolutionPromises = {};\n // this.evaluationContextPromises = {};\n })\n }\n\n private getCacheKey(mod:EvaluationContext, moduleName:string):string {\n return `${mod.evaluation.module.filepath}|${moduleName}`;\n }\n\n resolveModuleName(moduleName:string, baseModule?:EvaluationContext):Promise<string> {\n // note: uses current module as base module if none specified by caller\n // @ts-ignore\n const mod = baseModule ?? (module as EvaluationContext);\n const cacheKey = this.getCacheKey(mod, moduleName);\n if (!(cacheKey in this.nameResolutionPromises)) {\n this.nameResolutionPromises[cacheKey] = mod.resolve(moduleName);\n }\n return this.nameResolutionPromises[cacheKey];\n }\n\n getEvaluationContext(moduleName:string, baseModule?:EvaluationContext):Promise<EvaluationContext> {\n // note: uses current module as base module if none specified by caller\n // @ts-ignore\n const mod = baseModule ?? (module as EvaluationContext);\n const cacheKey = this.getCacheKey(mod, moduleName);\n if (!(cacheKey in this.evaluationContextPromises)) {\n this.evaluationContextPromises[cacheKey] = mod.getModuleEvaluationContext(moduleName);\n }\n return this.evaluationContextPromises[cacheKey];\n\n }\n}\n\nexport const ModuleCacheContext = createContext<null|ModuleCache>(null)\n\nexport const ModuleCacheContextProvider = ({ children, moduleCache }:{ children:ReactNode, moduleCache: ModuleCache }) => {\n\n return (\n <ModuleCacheContext.Provider value={moduleCache}>\n {children}\n </ModuleCacheContext.Provider>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyDQ;AAvDR,mBAAgE;AAEhE,0BAA4B;AAC5B,sBAAwB;AAEjB,MAAM,YAAY;AAAA,EAIvB,cAAc;AAHd,kCAA0D,CAAC;AAC3D,qCAAwE,CAAC;AAIvE,yCAAY,yBAAS,MAAM;AAAA,IAQ3B,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,KAAuB,YAA0B;AACnE,WAAO,GAAG,IAAI,WAAW,OAAO,QAAQ,IAAI,UAAU;AAAA,EACxD;AAAA,EAEA,kBAAkB,YAAmB,YAA+C;AAGlF,UAAM,MAAM,cAAe;AAC3B,UAAM,WAAW,KAAK,YAAY,KAAK,UAAU;AACjD,QAAI,EAAE,YAAY,KAAK,yBAAyB;AAC9C,WAAK,uBAAuB,QAAQ,IAAI,IAAI,QAAQ,UAAU;AAAA,IAChE;AACA,WAAO,KAAK,uBAAuB,QAAQ;AAAA,EAC7C;AAAA,EAEA,qBAAqB,YAAmB,YAA0D;AAGhG,UAAM,MAAM,cAAe;AAC3B,UAAM,WAAW,KAAK,YAAY,KAAK,UAAU;AACjD,QAAI,EAAE,YAAY,KAAK,4BAA4B;AACjD,WAAK,0BAA0B,QAAQ,IAAI,IAAI,2BAA2B,UAAU;AAAA,IACtF;AACA,WAAO,KAAK,0BAA0B,QAAQ;AAAA,EAEhD;AACF;AAEO,MAAM,yBAAqB,4BAAgC,IAAI;AAE/D,MAAM,6BAA6B,CAAC,EAAE,UAAU,YAAY,MAAuD;AAEtH,SACI,4CAAC,mBAAmB,UAAnB,EAA4B,OAAO,aAC/B,UACL;AAER;","names":[]}
|
package/dist/moduleCache.js
CHANGED
|
@@ -2,11 +2,12 @@ import "./chunk-VHAA22YE.js";
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext } from "react";
|
|
4
4
|
import { addListener } from "./sandboxUtils";
|
|
5
|
+
import { COMPILE } from "./generated/protocol";
|
|
5
6
|
class ModuleCache {
|
|
6
7
|
constructor() {
|
|
7
8
|
this.nameResolutionPromises = {};
|
|
8
9
|
this.evaluationContextPromises = {};
|
|
9
|
-
addListener(
|
|
10
|
+
addListener(COMPILE, () => {
|
|
10
11
|
});
|
|
11
12
|
}
|
|
12
13
|
getCacheKey(mod, moduleName) {
|
package/dist/moduleCache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/moduleCache.tsx"],"sourcesContent":["// based on: https://www.bbss.dev/posts/react-learn-suspense/#fetchcache-provider\n\nimport { createContext, ReactNode, useCallback, useState } from \"react\"\nimport { EvaluationContext } from \"./sandboxTypes\";\nimport { addListener } from \"./sandboxUtils\";\n\nexport class ModuleCache {\n nameResolutionPromises: Record<string, Promise<string>> = {}\n evaluationContextPromises: Record<string, Promise<EvaluationContext>> = {}\n\n constructor() {\n // reset cache on compile\n addListener(
|
|
1
|
+
{"version":3,"sources":["../src/moduleCache.tsx"],"sourcesContent":["// based on: https://www.bbss.dev/posts/react-learn-suspense/#fetchcache-provider\n\nimport { createContext, ReactNode, useCallback, useState } from \"react\"\nimport { EvaluationContext } from \"./sandboxTypes\";\nimport { addListener } from \"./sandboxUtils\";\nimport { COMPILE } from './generated/protocol';\n\nexport class ModuleCache {\n nameResolutionPromises: Record<string, Promise<string>> = {}\n evaluationContextPromises: Record<string, Promise<EvaluationContext>> = {}\n\n constructor() {\n // reset cache on compile\n addListener(COMPILE, () => {\n // NOTE: THIS CAUSES AN UNNECESSARY RELOAD\n // the <Include> component's module evaluation context promise is replaced\n // with a new promise for the same value when a compilation occurs which\n // doesn't affect the current module. Commenting out the following lines\n // eliminates the unnecessary component state loss.\n // this.nameResolutionPromises = {};\n // this.evaluationContextPromises = {};\n })\n }\n\n private getCacheKey(mod:EvaluationContext, moduleName:string):string {\n return `${mod.evaluation.module.filepath}|${moduleName}`;\n }\n\n resolveModuleName(moduleName:string, baseModule?:EvaluationContext):Promise<string> {\n // note: uses current module as base module if none specified by caller\n // @ts-ignore\n const mod = baseModule ?? (module as EvaluationContext);\n const cacheKey = this.getCacheKey(mod, moduleName);\n if (!(cacheKey in this.nameResolutionPromises)) {\n this.nameResolutionPromises[cacheKey] = mod.resolve(moduleName);\n }\n return this.nameResolutionPromises[cacheKey];\n }\n\n getEvaluationContext(moduleName:string, baseModule?:EvaluationContext):Promise<EvaluationContext> {\n // note: uses current module as base module if none specified by caller\n // @ts-ignore\n const mod = baseModule ?? (module as EvaluationContext);\n const cacheKey = this.getCacheKey(mod, moduleName);\n if (!(cacheKey in this.evaluationContextPromises)) {\n this.evaluationContextPromises[cacheKey] = mod.getModuleEvaluationContext(moduleName);\n }\n return this.evaluationContextPromises[cacheKey];\n\n }\n}\n\nexport const ModuleCacheContext = createContext<null|ModuleCache>(null)\n\nexport const ModuleCacheContextProvider = ({ children, moduleCache }:{ children:ReactNode, moduleCache: ModuleCache }) => {\n\n return (\n <ModuleCacheContext.Provider value={moduleCache}>\n {children}\n </ModuleCacheContext.Provider>\n )\n}\n"],"mappings":";AAyDQ;AAvDR,SAAS,qBAAuD;AAEhE,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AAEjB,MAAM,YAAY;AAAA,EAIvB,cAAc;AAHd,kCAA0D,CAAC;AAC3D,qCAAwE,CAAC;AAIvE,gBAAY,SAAS,MAAM;AAAA,IAQ3B,CAAC;AAAA,EACH;AAAA,EAEQ,YAAY,KAAuB,YAA0B;AACnE,WAAO,GAAG,IAAI,WAAW,OAAO,QAAQ,IAAI,UAAU;AAAA,EACxD;AAAA,EAEA,kBAAkB,YAAmB,YAA+C;AAGlF,UAAM,MAAM,cAAe;AAC3B,UAAM,WAAW,KAAK,YAAY,KAAK,UAAU;AACjD,QAAI,EAAE,YAAY,KAAK,yBAAyB;AAC9C,WAAK,uBAAuB,QAAQ,IAAI,IAAI,QAAQ,UAAU;AAAA,IAChE;AACA,WAAO,KAAK,uBAAuB,QAAQ;AAAA,EAC7C;AAAA,EAEA,qBAAqB,YAAmB,YAA0D;AAGhG,UAAM,MAAM,cAAe;AAC3B,UAAM,WAAW,KAAK,YAAY,KAAK,UAAU;AACjD,QAAI,EAAE,YAAY,KAAK,4BAA4B;AACjD,WAAK,0BAA0B,QAAQ,IAAI,IAAI,2BAA2B,UAAU;AAAA,IACtF;AACA,WAAO,KAAK,0BAA0B,QAAQ;AAAA,EAEhD;AACF;AAEO,MAAM,qBAAqB,cAAgC,IAAI;AAE/D,MAAM,6BAA6B,CAAC,EAAE,UAAU,YAAY,MAAuD;AAEtH,SACI,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,aAC/B,UACL;AAER;","names":[]}
|