@immediately-run/sdk 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/boot.cjs +4 -3
  2. package/dist/boot.cjs.map +1 -1
  3. package/dist/boot.js +4 -3
  4. package/dist/boot.js.map +1 -1
  5. package/dist/editor.cjs +9 -0
  6. package/dist/editor.cjs.map +1 -1
  7. package/dist/editor.d.cts +47 -1
  8. package/dist/editor.d.ts +47 -1
  9. package/dist/editor.js +6 -0
  10. package/dist/editor.js.map +1 -1
  11. package/dist/index.cjs +4 -0
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +5 -3
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/injectedBundler.cjs +49 -0
  18. package/dist/injectedBundler.cjs.map +1 -0
  19. package/dist/injectedBundler.d.cts +29 -0
  20. package/dist/injectedBundler.d.ts +29 -0
  21. package/dist/injectedBundler.js +24 -0
  22. package/dist/injectedBundler.js.map +1 -0
  23. package/dist/irMarkers.cjs +72 -0
  24. package/dist/irMarkers.cjs.map +1 -0
  25. package/dist/irMarkers.d.cts +54 -0
  26. package/dist/irMarkers.d.ts +54 -0
  27. package/dist/irMarkers.js +44 -0
  28. package/dist/irMarkers.js.map +1 -0
  29. package/dist/mountMatch.cjs +29 -0
  30. package/dist/mountMatch.cjs.map +1 -0
  31. package/dist/mountMatch.d.cts +21 -0
  32. package/dist/mountMatch.d.ts +21 -0
  33. package/dist/mountMatch.js +5 -0
  34. package/dist/mountMatch.js.map +1 -0
  35. package/dist/mounts.cjs +51 -4
  36. package/dist/mounts.cjs.map +1 -1
  37. package/dist/mounts.d.cts +145 -19
  38. package/dist/mounts.d.ts +145 -19
  39. package/dist/mounts.js +44 -3
  40. package/dist/mounts.js.map +1 -1
  41. package/dist/ready.cjs +69 -0
  42. package/dist/ready.cjs.map +1 -0
  43. package/dist/ready.d.cts +32 -0
  44. package/dist/ready.d.ts +32 -0
  45. package/dist/ready.js +41 -0
  46. package/dist/ready.js.map +1 -0
  47. package/dist/tasks.cjs +3 -0
  48. package/dist/tasks.cjs.map +1 -1
  49. package/dist/tasks.d.cts +24 -1
  50. package/dist/tasks.d.ts +24 -1
  51. package/dist/tasks.js +2 -0
  52. package/dist/tasks.js.map +1 -1
  53. package/dist/version.cjs +1 -1
  54. package/dist/version.cjs.map +1 -1
  55. package/dist/version.d.cts +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/version.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ const injectedBundler = () => {
2
+ try {
3
+ return module?.evaluation?.module?.bundler ?? null;
4
+ } catch {
5
+ return null;
6
+ }
7
+ };
8
+ const getInjectedMetadataEmitter = () => {
9
+ const b = injectedBundler();
10
+ if (b && typeof b.onMetadataChange === "function" && b.onMetadataChangeEmitter) {
11
+ return {
12
+ onMetadataChange: b.onMetadataChange,
13
+ enable: () => b.onMetadataChangeEmitter.enable()
14
+ };
15
+ }
16
+ return null;
17
+ };
18
+ const resolveMetadataSource = (injected) => injected ? { event: injected.onMetadataChange, enable: () => injected.enable() } : { event: void 0, enable: () => {
19
+ } };
20
+ export {
21
+ getInjectedMetadataEmitter,
22
+ resolveMetadataSource
23
+ };
24
+ //# sourceMappingURL=injectedBundler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (\n injected: InjectedMetadataEmitter | null,\n): MetadataSource =>\n injected\n ? { event: injected.onMetadataChange, enable: () => injected.enable() }\n : { event: undefined, enable: () => {} };\n"],"mappings":"AAyBA,MAAM,kBAAkB,MAAkB;AACxC,MAAI;AAEF,WAAO,QAAQ,YAAY,QAAQ,WAAW;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,MAAM,6BAA6B,MAAsC;AAC9E,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,qBAAqB,cAAc,EAAE,yBAAyB;AAC9E,WAAO;AAAA,MACL,kBAAkB,EAAE;AAAA,MACpB,QAAQ,MAAM,EAAE,wBAAwB,OAAO;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AACT;AAgBO,MAAM,wBAAwB,CACnC,aAEA,WACI,EAAE,OAAO,SAAS,kBAAkB,QAAQ,MAAM,SAAS,OAAO,EAAE,IACpE,EAAE,OAAO,QAAW,QAAQ,MAAM;AAAC,EAAE;","names":[]}
@@ -0,0 +1,72 @@
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 irMarkers_exports = {};
20
+ __export(irMarkers_exports, {
21
+ IR_MARKERS: () => IR_MARKERS,
22
+ isAllowedMarkerName: () => isAllowedMarkerName,
23
+ isIrMarkerName: () => isIrMarkerName,
24
+ resolveInteractive: () => resolveInteractive,
25
+ validateMarker: () => validateMarker
26
+ });
27
+ module.exports = __toCommonJS(irMarkers_exports);
28
+ const IR_MARKERS = {
29
+ "ir.open": ["url", "provider", "ns", "repo", "ref", "refKind"],
30
+ "ir.fetch": ["source", "bytes", "requestCount", "cacheHit", "httpStatus"],
31
+ "ir.mount": ["phantomCount", "writablePrimed"],
32
+ "ir.sandbox.boot": [],
33
+ "ir.transpile": ["moduleCount", "cacheHit", "bytesIn", "bytesOut"],
34
+ "ir.deps": ["depCount", "bytes", "requestCount", "cacheHit", "cdn"],
35
+ "ir.eval": ["moduleCount"],
36
+ "ir.fmp": [],
37
+ "ir.interactive": ["cold"],
38
+ "ir.verify": ["result", "blocking"],
39
+ "ir.refresh": ["bytes"]
40
+ };
41
+ const SUBMARK_RE = /^(ir\.transpile\.mod|ir\.deps\.pkg)\[[^\]]+\]$/;
42
+ const submarkAggregate = (name) => name.startsWith("ir.transpile.mod") ? "ir.transpile" : name.startsWith("ir.deps.pkg") ? "ir.deps" : null;
43
+ const isIrMarkerName = (name) => Object.prototype.hasOwnProperty.call(IR_MARKERS, name);
44
+ const isAllowedMarkerName = (name) => isIrMarkerName(name) || SUBMARK_RE.test(name);
45
+ function validateMarker(m) {
46
+ if (!m || typeof m.name !== "string") return null;
47
+ if (typeof m.at !== "number" || !Number.isFinite(m.at)) return null;
48
+ if (!isAllowedMarkerName(m.name)) return null;
49
+ const base = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name);
50
+ const allowed = IR_MARKERS[base];
51
+ const attrs = m.attrs;
52
+ if (attrs !== void 0) {
53
+ if (typeof attrs !== "object" || attrs === null) return null;
54
+ for (const key of Object.keys(attrs)) {
55
+ if (!allowed.includes(key)) return null;
56
+ }
57
+ }
58
+ return { name: m.name, at: m.at, ...attrs !== void 0 ? { attrs } : {} };
59
+ }
60
+ function resolveInteractive(rootRenderCommitAt, reportReadyAt) {
61
+ if (reportReadyAt === void 0) return rootRenderCommitAt;
62
+ return Math.max(rootRenderCommitAt, reportReadyAt);
63
+ }
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ IR_MARKERS,
67
+ isAllowedMarkerName,
68
+ isIrMarkerName,
69
+ resolveInteractive,
70
+ validateMarker
71
+ });
72
+ //# sourceMappingURL=irMarkers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;AAOA,MAAM,aAAa;AACnB,MAAM,mBAAmB,CAAC,SACxB,KAAK,WAAW,kBAAkB,IAAI,iBAAiB,KAAK,WAAW,aAAa,IAAI,YAAY;AAG/F,MAAM,iBAAiB,CAAC,SAC7B,OAAO,UAAU,eAAe,KAAK,YAAY,IAAI;AAIhD,MAAM,sBAAsB,CAAC,SAClC,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBvC,SAAS,eAAe,GAA+D;AAC5F,MAAI,CAAC,KAAK,OAAO,EAAE,SAAS,SAAU,QAAO;AAC7C,MAAI,OAAO,EAAE,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,EAAE,EAAG,QAAO;AAC/D,MAAI,CAAC,oBAAoB,EAAE,IAAI,EAAG,QAAO;AACzC,QAAM,OAAqB,eAAe,EAAE,IAAI,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACpF,QAAM,UAA6B,WAAW,IAAI;AAClD,QAAM,QAAQ,EAAE;AAChB,MAAI,UAAU,QAAW;AACvB,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAAA,IACrC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,IAAI,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG;AAC7E;AAUO,SAAS,mBAAmB,oBAA4B,eAAgC;AAC7F,MAAI,kBAAkB,OAAW,QAAO;AACxC,SAAO,KAAK,IAAI,oBAAoB,aAAa;AACnD;","names":[]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Each `ir.*` marker name → the attribute keys its payload may carry (the §3
3
+ * table). A forwarded marker is accepted only if BOTH its name is defined here AND
4
+ * every attribute key it carries is in that marker's allowed set (LP-5). An empty
5
+ * array means "no attributes" (a bare mark).
6
+ */
7
+ declare const IR_MARKERS: {
8
+ readonly "ir.open": readonly ["url", "provider", "ns", "repo", "ref", "refKind"];
9
+ readonly "ir.fetch": readonly ["source", "bytes", "requestCount", "cacheHit", "httpStatus"];
10
+ readonly "ir.mount": readonly ["phantomCount", "writablePrimed"];
11
+ readonly "ir.sandbox.boot": readonly [];
12
+ readonly "ir.transpile": readonly ["moduleCount", "cacheHit", "bytesIn", "bytesOut"];
13
+ readonly "ir.deps": readonly ["depCount", "bytes", "requestCount", "cacheHit", "cdn"];
14
+ readonly "ir.eval": readonly ["moduleCount"];
15
+ readonly "ir.fmp": readonly [];
16
+ readonly "ir.interactive": readonly ["cold"];
17
+ readonly "ir.verify": readonly ["result", "blocking"];
18
+ readonly "ir.refresh": readonly ["bytes"];
19
+ };
20
+ type IrMarkerName = keyof typeof IR_MARKERS;
21
+ /** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */
22
+ declare const isIrMarkerName: (name: string) => name is IrMarkerName;
23
+ /** Is `name` an accepted marker name — a defined top-level marker OR a recognized
24
+ * per-module/per-dep sub-mark? */
25
+ declare const isAllowedMarkerName: (name: string) => boolean;
26
+ /** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side
27
+ * `performance.now()` timestamp, and the optional attribute payload. */
28
+ interface ForwardedMarker {
29
+ name: string;
30
+ /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */
31
+ at: number;
32
+ attrs?: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name
36
+ * is in the vocabulary AND every attribute key is in that marker's schema. An
37
+ * unknown name — or a defined name carrying an out-of-schema attribute key — is
38
+ * DROPPED (returns `null`), never recorded. This is the gate against an untrusted
39
+ * sandbox minting arbitrary names/values into the host timeline (an injection
40
+ * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their
41
+ * aggregate's schema.
42
+ */
43
+ declare function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null;
44
+ /**
45
+ * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called
46
+ * `reportReady()` may only DELAY interactive — never advance it before the root
47
+ * render commits. A `reportReady()` that fires early is recorded but takes effect
48
+ * at the commit; if the app never reports, the commit alone stands. Budgets bind to
49
+ * the later of the two, so an app can't game its budget by declaring itself ready
50
+ * before it has rendered anything. PURE.
51
+ */
52
+ declare function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number;
53
+
54
+ export { type ForwardedMarker, IR_MARKERS, type IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Each `ir.*` marker name → the attribute keys its payload may carry (the §3
3
+ * table). A forwarded marker is accepted only if BOTH its name is defined here AND
4
+ * every attribute key it carries is in that marker's allowed set (LP-5). An empty
5
+ * array means "no attributes" (a bare mark).
6
+ */
7
+ declare const IR_MARKERS: {
8
+ readonly "ir.open": readonly ["url", "provider", "ns", "repo", "ref", "refKind"];
9
+ readonly "ir.fetch": readonly ["source", "bytes", "requestCount", "cacheHit", "httpStatus"];
10
+ readonly "ir.mount": readonly ["phantomCount", "writablePrimed"];
11
+ readonly "ir.sandbox.boot": readonly [];
12
+ readonly "ir.transpile": readonly ["moduleCount", "cacheHit", "bytesIn", "bytesOut"];
13
+ readonly "ir.deps": readonly ["depCount", "bytes", "requestCount", "cacheHit", "cdn"];
14
+ readonly "ir.eval": readonly ["moduleCount"];
15
+ readonly "ir.fmp": readonly [];
16
+ readonly "ir.interactive": readonly ["cold"];
17
+ readonly "ir.verify": readonly ["result", "blocking"];
18
+ readonly "ir.refresh": readonly ["bytes"];
19
+ };
20
+ type IrMarkerName = keyof typeof IR_MARKERS;
21
+ /** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */
22
+ declare const isIrMarkerName: (name: string) => name is IrMarkerName;
23
+ /** Is `name` an accepted marker name — a defined top-level marker OR a recognized
24
+ * per-module/per-dep sub-mark? */
25
+ declare const isAllowedMarkerName: (name: string) => boolean;
26
+ /** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side
27
+ * `performance.now()` timestamp, and the optional attribute payload. */
28
+ interface ForwardedMarker {
29
+ name: string;
30
+ /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */
31
+ at: number;
32
+ attrs?: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name
36
+ * is in the vocabulary AND every attribute key is in that marker's schema. An
37
+ * unknown name — or a defined name carrying an out-of-schema attribute key — is
38
+ * DROPPED (returns `null`), never recorded. This is the gate against an untrusted
39
+ * sandbox minting arbitrary names/values into the host timeline (an injection
40
+ * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their
41
+ * aggregate's schema.
42
+ */
43
+ declare function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null;
44
+ /**
45
+ * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called
46
+ * `reportReady()` may only DELAY interactive — never advance it before the root
47
+ * render commits. A `reportReady()` that fires early is recorded but takes effect
48
+ * at the commit; if the app never reports, the commit alone stands. Budgets bind to
49
+ * the later of the two, so an app can't game its budget by declaring itself ready
50
+ * before it has rendered anything. PURE.
51
+ */
52
+ declare function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number;
53
+
54
+ export { type ForwardedMarker, IR_MARKERS, type IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker };
@@ -0,0 +1,44 @@
1
+ const IR_MARKERS = {
2
+ "ir.open": ["url", "provider", "ns", "repo", "ref", "refKind"],
3
+ "ir.fetch": ["source", "bytes", "requestCount", "cacheHit", "httpStatus"],
4
+ "ir.mount": ["phantomCount", "writablePrimed"],
5
+ "ir.sandbox.boot": [],
6
+ "ir.transpile": ["moduleCount", "cacheHit", "bytesIn", "bytesOut"],
7
+ "ir.deps": ["depCount", "bytes", "requestCount", "cacheHit", "cdn"],
8
+ "ir.eval": ["moduleCount"],
9
+ "ir.fmp": [],
10
+ "ir.interactive": ["cold"],
11
+ "ir.verify": ["result", "blocking"],
12
+ "ir.refresh": ["bytes"]
13
+ };
14
+ const SUBMARK_RE = /^(ir\.transpile\.mod|ir\.deps\.pkg)\[[^\]]+\]$/;
15
+ const submarkAggregate = (name) => name.startsWith("ir.transpile.mod") ? "ir.transpile" : name.startsWith("ir.deps.pkg") ? "ir.deps" : null;
16
+ const isIrMarkerName = (name) => Object.prototype.hasOwnProperty.call(IR_MARKERS, name);
17
+ const isAllowedMarkerName = (name) => isIrMarkerName(name) || SUBMARK_RE.test(name);
18
+ function validateMarker(m) {
19
+ if (!m || typeof m.name !== "string") return null;
20
+ if (typeof m.at !== "number" || !Number.isFinite(m.at)) return null;
21
+ if (!isAllowedMarkerName(m.name)) return null;
22
+ const base = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name);
23
+ const allowed = IR_MARKERS[base];
24
+ const attrs = m.attrs;
25
+ if (attrs !== void 0) {
26
+ if (typeof attrs !== "object" || attrs === null) return null;
27
+ for (const key of Object.keys(attrs)) {
28
+ if (!allowed.includes(key)) return null;
29
+ }
30
+ }
31
+ return { name: m.name, at: m.at, ...attrs !== void 0 ? { attrs } : {} };
32
+ }
33
+ function resolveInteractive(rootRenderCommitAt, reportReadyAt) {
34
+ if (reportReadyAt === void 0) return rootRenderCommitAt;
35
+ return Math.max(rootRenderCommitAt, reportReadyAt);
36
+ }
37
+ export {
38
+ IR_MARKERS,
39
+ isAllowedMarkerName,
40
+ isIrMarkerName,
41
+ resolveInteractive,
42
+ validateMarker
43
+ };
44
+ //# sourceMappingURL=irMarkers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":"AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;AAOA,MAAM,aAAa;AACnB,MAAM,mBAAmB,CAAC,SACxB,KAAK,WAAW,kBAAkB,IAAI,iBAAiB,KAAK,WAAW,aAAa,IAAI,YAAY;AAG/F,MAAM,iBAAiB,CAAC,SAC7B,OAAO,UAAU,eAAe,KAAK,YAAY,IAAI;AAIhD,MAAM,sBAAsB,CAAC,SAClC,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBvC,SAAS,eAAe,GAA+D;AAC5F,MAAI,CAAC,KAAK,OAAO,EAAE,SAAS,SAAU,QAAO;AAC7C,MAAI,OAAO,EAAE,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,EAAE,EAAG,QAAO;AAC/D,MAAI,CAAC,oBAAoB,EAAE,IAAI,EAAG,QAAO;AACzC,QAAM,OAAqB,eAAe,EAAE,IAAI,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACpF,QAAM,UAA6B,WAAW,IAAI;AAClD,QAAM,QAAQ,EAAE;AAChB,MAAI,UAAU,QAAW;AACvB,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAAA,IACrC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,IAAI,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG;AAC7E;AAUO,SAAS,mBAAmB,oBAA4B,eAAgC;AAC7F,MAAI,kBAAkB,OAAW,QAAO;AACxC,SAAO,KAAK,IAAI,oBAAoB,aAAa;AACnD;","names":[]}
@@ -0,0 +1,29 @@
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 mountMatch_exports = {};
20
+ __export(mountMatch_exports, {
21
+ mountMatches: () => mountMatches
22
+ });
23
+ module.exports = __toCommonJS(mountMatch_exports);
24
+ const mountMatches = (mount, query) => (query.type === void 0 || mount.type === query.type) && (query.id === void 0 || mount.id === query.id) && (query.path === void 0 || mount.path === query.path) && (query.name === void 0 || mount.name === query.name);
25
+ // Annotate the CommonJS export names for ESM import in node:
26
+ 0 && (module.exports = {
27
+ mountMatches
28
+ });
29
+ //# sourceMappingURL=mountMatch.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mountMatch.ts"],"sourcesContent":["/**\n * Pure predicate behind {@link findMount} / {@link waitForMount}: does `mount`\n * satisfy every field present on `query`? An absent query field matches anything;\n * a present one must equal the mount's value. Kept framework-free and free of the\n * injected sandbox runtime so it is unit-testable in isolation (ways_of_working\n * §5 — separate pure logic from the effectful service).\n *\n * `name` (the human-readable mount label, R3-69) is matchable alongside the\n * `type`/`id`/`path` coordinates so an app/agent can locate a mount by the name a\n * user would recognise — e.g. `findMount({ name: 'Design notes' })` — rather than\n * an opaque `/mnt/{hash}` address or a non-unique space name guessed by hand.\n */\nexport interface MountMatchFields {\n type?: string;\n id?: string;\n path?: string;\n name?: string;\n}\n\nexport const mountMatches = (\n mount: MountMatchFields,\n query: MountMatchFields,\n): boolean =>\n (query.type === undefined || mount.type === query.type) &&\n (query.id === undefined || mount.id === query.id) &&\n (query.path === undefined || mount.path === query.path) &&\n (query.name === undefined || mount.name === query.name);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,eAAe,CAC1B,OACA,WAEC,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM,UACjD,MAAM,OAAO,UAAa,MAAM,OAAO,MAAM,QAC7C,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM,UACjD,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM;","names":[]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pure predicate behind {@link findMount} / {@link waitForMount}: does `mount`
3
+ * satisfy every field present on `query`? An absent query field matches anything;
4
+ * a present one must equal the mount's value. Kept framework-free and free of the
5
+ * injected sandbox runtime so it is unit-testable in isolation (ways_of_working
6
+ * §5 — separate pure logic from the effectful service).
7
+ *
8
+ * `name` (the human-readable mount label, R3-69) is matchable alongside the
9
+ * `type`/`id`/`path` coordinates so an app/agent can locate a mount by the name a
10
+ * user would recognise — e.g. `findMount({ name: 'Design notes' })` — rather than
11
+ * an opaque `/mnt/{hash}` address or a non-unique space name guessed by hand.
12
+ */
13
+ interface MountMatchFields {
14
+ type?: string;
15
+ id?: string;
16
+ path?: string;
17
+ name?: string;
18
+ }
19
+ declare const mountMatches: (mount: MountMatchFields, query: MountMatchFields) => boolean;
20
+
21
+ export { type MountMatchFields, mountMatches };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pure predicate behind {@link findMount} / {@link waitForMount}: does `mount`
3
+ * satisfy every field present on `query`? An absent query field matches anything;
4
+ * a present one must equal the mount's value. Kept framework-free and free of the
5
+ * injected sandbox runtime so it is unit-testable in isolation (ways_of_working
6
+ * §5 — separate pure logic from the effectful service).
7
+ *
8
+ * `name` (the human-readable mount label, R3-69) is matchable alongside the
9
+ * `type`/`id`/`path` coordinates so an app/agent can locate a mount by the name a
10
+ * user would recognise — e.g. `findMount({ name: 'Design notes' })` — rather than
11
+ * an opaque `/mnt/{hash}` address or a non-unique space name guessed by hand.
12
+ */
13
+ interface MountMatchFields {
14
+ type?: string;
15
+ id?: string;
16
+ path?: string;
17
+ name?: string;
18
+ }
19
+ declare const mountMatches: (mount: MountMatchFields, query: MountMatchFields) => boolean;
20
+
21
+ export { type MountMatchFields, mountMatches };
@@ -0,0 +1,5 @@
1
+ const mountMatches = (mount, query) => (query.type === void 0 || mount.type === query.type) && (query.id === void 0 || mount.id === query.id) && (query.path === void 0 || mount.path === query.path) && (query.name === void 0 || mount.name === query.name);
2
+ export {
3
+ mountMatches
4
+ };
5
+ //# sourceMappingURL=mountMatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mountMatch.ts"],"sourcesContent":["/**\n * Pure predicate behind {@link findMount} / {@link waitForMount}: does `mount`\n * satisfy every field present on `query`? An absent query field matches anything;\n * a present one must equal the mount's value. Kept framework-free and free of the\n * injected sandbox runtime so it is unit-testable in isolation (ways_of_working\n * §5 — separate pure logic from the effectful service).\n *\n * `name` (the human-readable mount label, R3-69) is matchable alongside the\n * `type`/`id`/`path` coordinates so an app/agent can locate a mount by the name a\n * user would recognise — e.g. `findMount({ name: 'Design notes' })` — rather than\n * an opaque `/mnt/{hash}` address or a non-unique space name guessed by hand.\n */\nexport interface MountMatchFields {\n type?: string;\n id?: string;\n path?: string;\n name?: string;\n}\n\nexport const mountMatches = (\n mount: MountMatchFields,\n query: MountMatchFields,\n): boolean =>\n (query.type === undefined || mount.type === query.type) &&\n (query.id === undefined || mount.id === query.id) &&\n (query.path === undefined || mount.path === query.path) &&\n (query.name === undefined || mount.name === query.name);\n"],"mappings":"AAmBO,MAAM,eAAe,CAC1B,OACA,WAEC,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM,UACjD,MAAM,OAAO,UAAa,MAAM,OAAO,MAAM,QAC7C,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM,UACjD,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM;","names":[]}
package/dist/mounts.cjs CHANGED
@@ -23,16 +23,22 @@ __export(mounts_exports, {
23
23
  getAppMountPath: () => getAppMountPath,
24
24
  getMounts: () => getMounts,
25
25
  getSpaceMembers: () => getSpaceMembers,
26
+ importSettingsFromParent: () => importSettingsFromParent,
26
27
  listAllSpaces: () => listAllSpaces,
27
28
  listGrants: () => listGrants,
29
+ listSettingsApps: () => listSettingsApps,
28
30
  listSpaces: () => listSpaces,
29
31
  lookupUser: () => lookupUser,
32
+ makeContentRef: () => makeContentRef,
30
33
  mount: () => mount,
31
34
  mountSpace: () => mountSpace,
32
35
  onMountsChange: () => onMountsChange,
33
- openAppSpace: () => openAppSpace,
36
+ openSettings: () => openSettings,
37
+ openSettingsOf: () => openSettingsOf,
34
38
  requestMount: () => requestMount,
35
39
  requestSpace: () => requestSpace,
40
+ resolveContentRef: () => resolveContentRef,
41
+ resolveContentRefs: () => resolveContentRefs,
36
42
  revokeGrant: () => revokeGrant,
37
43
  setSpaceRole: () => setSpaceRole,
38
44
  shareSpace: () => shareSpace,
@@ -45,11 +51,12 @@ module.exports = __toCommonJS(mounts_exports);
45
51
  var import_react = require("react");
46
52
  var import_sandboxUtils = require("./sandboxUtils");
47
53
  var import_hostRuntime = require("./hostRuntime");
54
+ var import_mountMatch = require("./mountMatch");
48
55
  const getAppMountPath = () => (0, import_hostRuntime.getHostRuntime)()?.appMountPath ?? "/app";
49
56
  const mountService = () => {
50
57
  return module.evaluation.module.bundler.mounts;
51
58
  };
52
- const matches = (mount2, query) => (query.type === void 0 || mount2.type === query.type) && (query.id === void 0 || mount2.id === query.id) && (query.path === void 0 || mount2.path === query.path);
59
+ const matches = (mount2, query) => (0, import_mountMatch.mountMatches)(mount2, query);
53
60
  const getMounts = () => mountService().getMounts();
54
61
  const findMount = (query) => getMounts().find((m) => matches(m, query));
55
62
  const onMountsChange = (listener) => {
@@ -83,11 +90,45 @@ const requestMountInternal = async (method, query) => {
83
90
  const mount2 = await request(method, query);
84
91
  return waitForMount({ id: mount2.id ?? mount2.path });
85
92
  };
86
- const openAppSpace = (slot = "default") => requestMountInternal("open", { slot });
87
93
  const mount = (mountId) => requestMountInternal("mount", { mount: mountId });
88
94
  const mountSpace = (query) => mount(`space:${query.spaceId}`);
89
95
  const requestMount = () => requestMountInternal("request", {});
90
96
  const requestSpace = requestMount;
97
+ const makeContentRef = (ref, opts) => ({ $cap: "file", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
98
+ const resolveContentRef = async (ref) => {
99
+ const path = await request("resolveRef", { ref });
100
+ return { path };
101
+ };
102
+ const resolveContentRefs = async (refs) => {
103
+ const paths = await request("resolveRefs", { refs });
104
+ return { paths };
105
+ };
106
+ const settingsRequest = async (method, query = {}) => {
107
+ const res = await (0, import_sandboxUtils.protocolRequest)("settings", method, [query]);
108
+ if (!res || res.ok !== true) {
109
+ const err = new Error(res?.message ?? "settings request failed");
110
+ err.code = res?.code ?? "unknown";
111
+ throw err;
112
+ }
113
+ return res.data;
114
+ };
115
+ const openSettings = async () => {
116
+ const mount2 = await settingsRequest("open");
117
+ return waitForMount({ id: mount2.id ?? mount2.path });
118
+ };
119
+ const importSettingsFromParent = async () => {
120
+ try {
121
+ const data = await settingsRequest("importFromParent");
122
+ return { ok: true, copied: data.copied };
123
+ } catch (e) {
124
+ return { ok: false, code: e.code ?? "unknown" };
125
+ }
126
+ };
127
+ const openSettingsOf = async (appKey) => {
128
+ const mount2 = await settingsRequest("openOf", { appKey });
129
+ return waitForMount({ id: mount2.id ?? mount2.path });
130
+ };
131
+ const listSettingsApps = () => settingsRequest("list");
91
132
  const createSpace = (opts = {}) => requestMountInternal("create", opts);
92
133
  const listSpaces = (opts = {}) => request("list", opts);
93
134
  const unmountSpace = async (query) => {
@@ -116,16 +157,22 @@ const revokeGrant = async (appKey, spaceId) => {
116
157
  getAppMountPath,
117
158
  getMounts,
118
159
  getSpaceMembers,
160
+ importSettingsFromParent,
119
161
  listAllSpaces,
120
162
  listGrants,
163
+ listSettingsApps,
121
164
  listSpaces,
122
165
  lookupUser,
166
+ makeContentRef,
123
167
  mount,
124
168
  mountSpace,
125
169
  onMountsChange,
126
- openAppSpace,
170
+ openSettings,
171
+ openSettingsOf,
127
172
  requestMount,
128
173
  requestSpace,
174
+ resolveContentRef,
175
+ resolveContentRefs,
129
176
  revokeGrant,
130
177
  setSpaceRole,
131
178
  shareSpace,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mounts.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { protocolRequest } from './sandboxUtils';\nimport { getHostRuntime } from './hostRuntime';\n\n/**\n * The absolute path where this app's own repository filesystem is mounted\n * (FILE_SHARING_SPEC §11.2). Prefer this over hardcoding `/app`: the repo is\n * dual-mounted at both `/app` (back-compat) and its canonical `/mnt/{hash}`\n * address, and this returns the canonical one the host reports. Falls back to\n * `/app` when the host hasn't reported a canonical path (older host / before the\n * report arrives) — both paths are live, so either resolves the same files.\n */\nexport const getAppMountPath = (): string => getHostRuntime()?.appMountPath ?? '/app';\n\n/**\n * A filesystem mount available to the sandbox, mirrored from the host window.\n *\n * Mounts appear on demand — call {@link openAppSpace} / {@link mountSpace} to ask\n * the host to mount a Firestore-backed \"space\"; it appears at `/spaces/{id}`.\n * Read or subscribe to the set, then access the files through the `fs` module at\n * the mount's `path`.\n */\nexport interface SandboxMount {\n /** Absolute path where the mount is reachable (e.g. `/spaces/{id}`). */\n path: string;\n /** Backend kind, e.g. `'firestore'`. */\n type: string;\n /** Optional stable identifier (the spaceId, for spaces). */\n id?: string;\n /**\n * Access mode of the granted view: `'rw'` (read-write) or `'ro'` (read-only).\n * A live role downgrade re-announces the same mount with `mode: 'ro'`; apps\n * observing `onMountsChange` see the change and writes start failing `EROFS`.\n * Absent on the primary repo mount (treated as read-write).\n */\n mode?: \"ro\" | \"rw\";\n}\n\n/**\n * Why a mounted filesystem was removed, surfaced on the removed descriptor so an\n * app can say *why* it vanished instead of failing mutely (auth-mount §\"mount-remove\"\n * / AM2-4):\n * - `revoked` — a durable grant was revoked (revokeGrant / consent withdrawal);\n * - `unshared` — the granting user's membership was removed (or downgraded out);\n * - `signed-out` — sign-out tore down every mount;\n * - `unmounted` — the app's own `unmountSpace` (or region teardown);\n * - `deleted` — the space was soft-deleted.\n * An older host that sends no reason is read as `'revoked'` (most conservative).\n */\nexport type MountRemoveReason =\n | \"revoked\"\n | \"unshared\"\n | \"signed-out\"\n | \"unmounted\"\n | \"deleted\";\n\n/** A descriptor delivered as REMOVED to a mounts-change listener: the mount that\n * went away, plus the `reason` it did. */\nexport interface RemovedMount extends SandboxMount {\n reason: MountRemoveReason;\n}\n\ninterface MountService {\n getMounts(): SandboxMount[];\n onChange(\n listener: (mounts: SandboxMount[], removed: RemovedMount[]) => void,\n ): { dispose(): void };\n}\n\n// `module.evaluation.module.bundler` is the sandbox bundler injected into the\n// evaluation context (same path the other SDK helpers reach for `messageBus`).\nconst mountService = (): MountService => {\n // @ts-ignore - injected by the sandbox runtime\n return module.evaluation.module.bundler.mounts;\n};\n\n/** A predicate-style matcher for {@link findMount} / {@link waitForMount}. */\nexport type MountQuery = { type?: string; id?: string; path?: string };\n\nconst matches = (mount: SandboxMount, query: MountQuery): boolean =>\n (query.type === undefined || mount.type === query.type) &&\n (query.id === undefined || mount.id === query.id) &&\n (query.path === undefined || mount.path === query.path);\n\n/**\n * Returns the mounts currently available. Poll this whenever you need a one-off\n * read; use {@link onMountsChange} or {@link useMounts} to react to changes.\n */\nexport const getMounts = (): SandboxMount[] => mountService().getMounts();\n\n/** Returns the first mount matching `query`, or `undefined`. */\nexport const findMount = (query: MountQuery): SandboxMount | undefined =>\n getMounts().find((m) => matches(m, query));\n\n/**\n * Subscribe to mount changes. The listener is invoked immediately with the\n * current mounts (and an empty `removed`), then again on every change. The second\n * argument carries the descriptors REMOVED by that change, each with its `reason`\n * (AM2-4) — so an app can react to *why* a mount vanished (e.g. tell the user a\n * shared space was `unshared` vs `deleted`). It is empty on adds and on the\n * initial replay. Returns an unsubscribe fn.\n */\nexport const onMountsChange = (\n listener: (mounts: SandboxMount[], removed: RemovedMount[]) => void,\n): (() => void) => {\n const disposable = mountService().onChange(listener);\n return () => disposable.dispose();\n};\n\n/**\n * Resolves once a mount matching `query` is present (immediately if it already\n * is). Handy for \"use it when it appears\" — e.g.\n * `await waitForMount({ type: 'firestore' })` before reading `/firestore`.\n */\nexport const waitForMount = (query: MountQuery): Promise<SandboxMount> =>\n new Promise((resolve) => {\n const unsubscribe = onMountsChange((mounts) => {\n const found = mounts.find((m) => matches(m, query));\n if (found) {\n // Defer unsubscribe so we don't dispose during the initial replay call.\n Promise.resolve().then(unsubscribe);\n resolve(found);\n }\n });\n });\n\n/** React hook returning the mounts currently available, re-rendering on change. */\nexport const useMounts = (): SandboxMount[] => {\n const [mounts, setMounts] = useState<SandboxMount[]>(getMounts);\n useEffect(() => onMountsChange(setMounts), []);\n return mounts;\n};\n\n// ---------------------------------------------------------------------------\n// Spaces — on-demand, shareable Firestore-backed filesystems.\n// The host owns all UX: if you aren't signed in, or the space doesn't exist or\n// isn't accessible, the parent window presents sign-in / create / request-access\n// and only then resolves these calls. See docs/specs/FILE_SHARING_SPEC.md.\n// ---------------------------------------------------------------------------\n\n/** Summary of a space, as returned by {@link listSpaces}. */\nexport interface SpaceInfo {\n spaceId: string;\n role?: 'owner' | 'writer' | 'reader';\n owner?: string;\n name?: string;\n}\n\n/** An error from a space operation, carrying a machine-readable `code`. */\nexport interface SpaceError extends Error {\n code:\n | 'auth-required'\n | 'cancelled'\n | 'forbidden'\n | 'not-found'\n | 'unsupported-scheme'\n | 'unknown';\n}\n\ntype SpaceResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\n// Issue a spaces protocol request, unwrapping the host's {ok,data} envelope and\n// throwing a typed SpaceError on failure.\nconst request = async <T = unknown>(\n method: string,\n query: Record<string, unknown> = {},\n): Promise<T> => {\n const res = (await protocolRequest('spaces', method, [query])) as SpaceResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'space request failed') as SpaceError;\n err.code = (res?.code as SpaceError['code']) ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Request a space mount, then wait until the host actually registers it. The\n// host announces the mount (`mount-add`) separately from the protocol reply, so\n// an immediate read could otherwise race the mount.\nconst requestMountInternal = async (\n method: string,\n query: Record<string, unknown>,\n): Promise<SandboxMount> => {\n const mount = await request<SandboxMount>(method, query);\n return waitForMount({ id: mount.id ?? mount.path });\n};\n\n/**\n * Open this app's workspace for the signed-in user (the zero-config path). The\n * `slot` names which workspace (default `'default'`); pass distinct slots for\n * multiple filesystems in one app. On a missing slot the host shows a\n * create-or-pick dialog. Rejects with a {@link SpaceError} (`.code`) on cancel.\n */\nexport const openAppSpace = (slot = 'default'): Promise<SandboxMount> =>\n requestMountInternal('open', { slot });\n\n/**\n * Mount a filesystem by its **universal mount id** (UI_AS_APPS_SPEC §3.5) —\n * `scheme:locator`, e.g. `space:{spaceId}` or `github:owner/repo@ref`. Backend-blind:\n * the host resolves the scheme. A scheme with no resolver rejects with\n * {@link SpaceError} `unsupported-scheme`.\n */\nexport const mount = (mountId: string): Promise<SandboxMount> =>\n requestMountInternal('mount', { mount: mountId });\n\n/** Mount a specific space by id (e.g. one shared with you, or from a link). A thin\n * shim over {@link mount} with the `space:` scheme. */\nexport const mountSpace = (query: { spaceId: string }): Promise<SandboxMount> =>\n mount(`space:${query.spaceId}`);\n\n/**\n * Ask the user to grant a filesystem to this app — the §8.6 powerbox. The app\n * asks; the HOST shows the user their mounts and the access choice (which mount,\n * an optional subtree, read-only vs read-write); the USER picks or declines. The\n * app never sees the list — it resolves with the single granted mount, or rejects\n * with a {@link SpaceError} (`cancelled`) if declined. The granted scope is\n * enforced host-side: the mount is chroot'd / `ro`-limited accordingly.\n *\n * Backend-general (§3.5): the picker offers whatever mounts the user has (today,\n * their spaces). Returns the granted mount by its universal id.\n */\nexport const requestMount = (): Promise<SandboxMount> =>\n requestMountInternal('request', {});\n\n/** @deprecated renamed to {@link requestMount} (backend-general, §3.5). */\nexport const requestSpace = requestMount;\n\n/** Create a brand-new space, optionally binding it to this app (a slot). */\nexport const createSpace = (\n opts: { name?: string; slot?: string; bindToApp?: boolean } = {}\n): Promise<SandboxMount> => requestMountInternal('create', opts);\n\n/** List spaces you can access — all of them, or just those bound to this app. */\nexport const listSpaces = (opts: { app?: boolean } = {}): Promise<SpaceInfo[]> =>\n request<SpaceInfo[]>('list', opts);\n\n/** Release a mounted space (stops its listener on the host). */\nexport const unmountSpace = async (query: { spaceId: string }): Promise<void> => {\n await request('unmount', query);\n};\n\n// ---------------------------------------------------------------------------\n// Space management (the space-manager app) — UI_AS_APPS_SPEC §5.2. These are\n// ELEVATED: enumerating all the user's spaces is `spaces:user`; mutating\n// membership (share/unshare/setRole) and resolving handles is `spaces:admin`.\n// The host enforces the owner-lockout invariant (a space always keeps an owner,\n// T41) and rate-limits handle lookups (L1); the OAuth/identity token never\n// crosses to the app.\n// ---------------------------------------------------------------------------\n\nexport type Role = 'owner' | 'writer' | 'reader';\n\n/** A member of a space (for the share/manage UI). */\nexport interface Member {\n /** `user:{uid}` | `group:{gid}`. */\n principal: string;\n role: Role;\n login?: string;\n avatarUrl?: string;\n}\n\n/** A handle resolved to a principal (handle → who). */\nexport interface ResolvedUser {\n uid: string;\n login: string;\n avatarUrl?: string;\n}\n\n/** Enumerate ALL the user's spaces (not just this app's) — `spaces:user`. */\nexport const listAllSpaces = (): Promise<SpaceInfo[]> => request<SpaceInfo[]>('listAll', {});\n\n/** Read a space's members one-shot — `spaces:admin`. */\nexport const getSpaceMembers = (spaceId: string): Promise<Member[]> =>\n request<Member[]>('members', { spaceId });\n\n/** Invite a user (by provider handle) to a space at a role — `spaces:admin`. The\n * host resolves the handle, so the app never sees other users' uids except the\n * one it invited. */\nexport const shareSpace = async (spaceId: string, login: string, role: Role): Promise<void> => {\n await request('share', { spaceId, login, role });\n};\n\n/** Remove a member from a space — `spaces:admin`. Refused if it would orphan the\n * space (owner-lockout, T41). */\nexport const unshareSpace = async (spaceId: string, uid: string): Promise<void> => {\n await request('unshare', { spaceId, uid });\n};\n\n/** Change a member's role — `spaces:admin`. Refused if it would drop the sole\n * owner (owner-lockout, T41). */\nexport const setSpaceRole = async (spaceId: string, uid: string, role: Role): Promise<void> => {\n await request('setRole', { spaceId, uid, role });\n};\n\n/** Resolve a provider handle to a principal (for the invite flow) — `spaces:admin`,\n * rate-limited host-side. */\nexport const lookupUser = (login: string): Promise<ResolvedUser> =>\n request<ResolvedUser>('lookupUser', { login });\n\n/** One durable grant an app holds, for the §8.11 capability audit view. */\nexport interface GrantRecord {\n /** The app's provider-qualified identity (`provider__namespace__repository`). */\n appKey: string;\n spaceId: string;\n /** Universal mount id (§3.5). */\n mountId: string;\n subtree?: string;\n mode: 'ro' | 'rw';\n name?: string;\n}\n\n/** Enumerate every (app, mount) grant the user holds — the audit view\n * (§8.11). Elevated `spaces:admin`. */\nexport const listGrants = (): Promise<GrantRecord[]> => request<GrantRecord[]>('grants', {});\n\n/** Revoke one app's grant on a space — durable (the app can't re-mount) plus a\n * best-effort live teardown. Elevated `spaces:admin`. */\nexport const revokeGrant = async (appKey: string, spaceId: string): Promise<void> => {\n await request('revokeGrant', { appKey, spaceId });\n};\n"],"mappings":"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","names":["mount"]}
1
+ {"version":3,"sources":["../src/mounts.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { protocolRequest } from './sandboxUtils';\nimport { getHostRuntime } from './hostRuntime';\nimport { mountMatches } from './mountMatch';\n// Type-only: `tasks.ts` registers a host listener at module load, so we reuse the\n// FileCap SHAPE without pulling that side effect into every `mounts` importer.\nimport type { FileCap } from './tasks';\n\n/**\n * The absolute path where this app's own repository filesystem is mounted\n * (FILE_SHARING_SPEC §11.2). Prefer this over hardcoding `/app`: the repo is\n * dual-mounted at both `/app` (back-compat) and its canonical `/mnt/{hash}`\n * address, and this returns the canonical one the host reports. Falls back to\n * `/app` when the host hasn't reported a canonical path (older host / before the\n * report arrives) — both paths are live, so either resolves the same files.\n */\nexport const getAppMountPath = (): string => getHostRuntime()?.appMountPath ?? '/app';\n\n/**\n * A filesystem mount available to the sandbox, mirrored from the host window.\n *\n * Mounts appear on demand — call {@link openSettings} for this app's own settings,\n * or {@link mountSpace} / {@link requestMount} to mount a Firestore-backed \"space\".\n * Read or subscribe to the set, then access the files through the `fs` module at\n * the mount's `path`.\n */\nexport interface SandboxMount {\n /** Absolute path where the mount is reachable (e.g. `/spaces/{id}`). */\n path: string;\n /** Backend kind, e.g. `'firestore'`. */\n type: string;\n /** Optional stable identifier (the spaceId, for spaces). */\n id?: string;\n /**\n * Access mode of the granted view: `'rw'` (read-write) or `'ro'` (read-only).\n * A live role downgrade re-announces the same mount with `mode: 'ro'`; apps\n * observing `onMountsChange` see the change and writes start failing `EROFS`.\n * Absent on the primary repo mount (treated as read-write).\n */\n mode?: \"ro\" | \"rw\";\n /**\n * Human-readable label for the mount — the space's display name, or the repo\n * label for the primary working-tree mount (R3-69). Use this to show users and\n * agents *what* a mount is: the `path` (`/mnt/{hash}`) and `id` (the spaceId)\n * are opaque, and space names are not unique, so neither alone tells you which\n * filesystem you're looking at. Absent when the host can't resolve a name\n * (older host, or a name it never learned) — fall back to `id`/`path`.\n */\n name?: string;\n /**\n * The granted scopes of this mount (plan 12 §8.7 / §F): each `{subtree, mode}`\n * is a path prefix you hold and at what access, at the mount's backend-natural\n * paths. Use it to reason about per-path writability — which subtree is `rw` —\n * WITHOUT probing `EROFS`. A single whole-mount grant is `[{ subtree: '/', mode }]`.\n * Absent on the primary repo mount and on an older host that doesn't report it.\n */\n rules?: MountRule[];\n}\n\n/** One granted scope of a mount (plan 12 §F): a backend-natural path prefix and\n * the access mode there. The most specific (longest) matching rule governs a path. */\nexport interface MountRule {\n subtree: string;\n mode: 'ro' | 'rw';\n}\n\n/**\n * Why a mounted filesystem was removed, surfaced on the removed descriptor so an\n * app can say *why* it vanished instead of failing mutely (auth-mount §\"mount-remove\"\n * / AM2-4):\n * - `revoked` — a durable grant was revoked (revokeGrant / consent withdrawal);\n * - `unshared` — the granting user's membership was removed (or downgraded out);\n * - `signed-out` — sign-out tore down every mount;\n * - `unmounted` — the app's own `unmountSpace` (or region teardown);\n * - `deleted` — the space was soft-deleted.\n * An older host that sends no reason is read as `'revoked'` (most conservative).\n */\nexport type MountRemoveReason =\n | \"revoked\"\n | \"unshared\"\n | \"signed-out\"\n | \"unmounted\"\n | \"deleted\";\n\n/** A descriptor delivered as REMOVED to a mounts-change listener: the mount that\n * went away, plus the `reason` it did. */\nexport interface RemovedMount extends SandboxMount {\n reason: MountRemoveReason;\n}\n\ninterface MountService {\n getMounts(): SandboxMount[];\n onChange(\n listener: (mounts: SandboxMount[], removed: RemovedMount[]) => void,\n ): { dispose(): void };\n}\n\n// `module.evaluation.module.bundler` is the sandbox bundler injected into the\n// evaluation context (same path the other SDK helpers reach for `messageBus`).\nconst mountService = (): MountService => {\n // @ts-ignore - injected by the sandbox runtime\n return module.evaluation.module.bundler.mounts;\n};\n\n/** A predicate-style matcher for {@link findMount} / {@link waitForMount}. Any\n * combination of coordinates; `name` matches the human-readable mount label. */\nexport type MountQuery = { type?: string; id?: string; path?: string; name?: string };\n\nconst matches = (mount: SandboxMount, query: MountQuery): boolean =>\n mountMatches(mount, query);\n\n/**\n * Returns the mounts currently available. Poll this whenever you need a one-off\n * read; use {@link onMountsChange} or {@link useMounts} to react to changes.\n * Each descriptor carries its `id` (the spaceId), `path` (`/mnt/{hash}`) and —\n * when the host can resolve it — a human-readable `name` (R3-69), so this doubles\n * as a queryable mount→space mapping for showing or locating a mount by name.\n */\nexport const getMounts = (): SandboxMount[] => mountService().getMounts();\n\n/** Returns the first mount matching `query`, or `undefined`. */\nexport const findMount = (query: MountQuery): SandboxMount | undefined =>\n getMounts().find((m) => matches(m, query));\n\n/**\n * Subscribe to mount changes. The listener is invoked immediately with the\n * current mounts (and an empty `removed`), then again on every change. The second\n * argument carries the descriptors REMOVED by that change, each with its `reason`\n * (AM2-4) — so an app can react to *why* a mount vanished (e.g. tell the user a\n * shared space was `unshared` vs `deleted`). It is empty on adds and on the\n * initial replay. Returns an unsubscribe fn.\n */\nexport const onMountsChange = (\n listener: (mounts: SandboxMount[], removed: RemovedMount[]) => void,\n): (() => void) => {\n const disposable = mountService().onChange(listener);\n return () => disposable.dispose();\n};\n\n/**\n * Resolves once a mount matching `query` is present (immediately if it already\n * is). Handy for \"use it when it appears\" — e.g.\n * `await waitForMount({ type: 'firestore' })` before reading `/firestore`.\n */\nexport const waitForMount = (query: MountQuery): Promise<SandboxMount> =>\n new Promise((resolve) => {\n const unsubscribe = onMountsChange((mounts) => {\n const found = mounts.find((m) => matches(m, query));\n if (found) {\n // Defer unsubscribe so we don't dispose during the initial replay call.\n Promise.resolve().then(unsubscribe);\n resolve(found);\n }\n });\n });\n\n/** React hook returning the mounts currently available, re-rendering on change. */\nexport const useMounts = (): SandboxMount[] => {\n const [mounts, setMounts] = useState<SandboxMount[]>(getMounts);\n useEffect(() => onMountsChange(setMounts), []);\n return mounts;\n};\n\n// ---------------------------------------------------------------------------\n// Spaces — on-demand, shareable Firestore-backed filesystems.\n// The host owns all UX: if you aren't signed in, or the space doesn't exist or\n// isn't accessible, the parent window presents sign-in / create / request-access\n// and only then resolves these calls. See docs/specs/FILE_SHARING_SPEC.md.\n// ---------------------------------------------------------------------------\n\n/** Summary of a space, as returned by {@link listSpaces}. */\nexport interface SpaceInfo {\n spaceId: string;\n role?: 'owner' | 'writer' | 'reader';\n owner?: string;\n name?: string;\n}\n\n/** An error from a space operation, carrying a machine-readable `code`. */\nexport interface SpaceError extends Error {\n code:\n | 'auth-required'\n | 'cancelled'\n | 'forbidden'\n | 'not-found'\n | 'unsupported-scheme'\n | 'unknown';\n}\n\ntype SpaceResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\n// Issue a spaces protocol request, unwrapping the host's {ok,data} envelope and\n// throwing a typed SpaceError on failure.\nconst request = async <T = unknown>(\n method: string,\n query: Record<string, unknown> = {},\n): Promise<T> => {\n const res = (await protocolRequest('spaces', method, [query])) as SpaceResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'space request failed') as SpaceError;\n err.code = (res?.code as SpaceError['code']) ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Request a space mount, then wait until the host actually registers it. The\n// host announces the mount (`mount-add`) separately from the protocol reply, so\n// an immediate read could otherwise race the mount.\nconst requestMountInternal = async (\n method: string,\n query: Record<string, unknown>,\n): Promise<SandboxMount> => {\n const mount = await request<SandboxMount>(method, query);\n return waitForMount({ id: mount.id ?? mount.path });\n};\n\n/**\n * Mount a filesystem by its **universal mount id** (UI_AS_APPS_SPEC §3.5) —\n * `scheme:locator`, e.g. `space:{spaceId}` or `github:owner/repo@ref`. Backend-blind:\n * the host resolves the scheme. A scheme with no resolver rejects with\n * {@link SpaceError} `unsupported-scheme`.\n */\nexport const mount = (mountId: string): Promise<SandboxMount> =>\n requestMountInternal('mount', { mount: mountId });\n\n/** Mount a specific space by id (e.g. one shared with you, or from a link). A thin\n * shim over {@link mount} with the `space:` scheme. */\nexport const mountSpace = (query: { spaceId: string }): Promise<SandboxMount> =>\n mount(`space:${query.spaceId}`);\n\n/**\n * Ask the user to grant a filesystem to this app — the §8.6 powerbox. The app\n * asks; the HOST shows the user their spaces and, for the chosen one, its PROJECT\n * FOLDERS (§8.7). The user picks ONE project — so a shared space opens scoped to\n * just that project, never the whole space — and makes an EXPLICIT read-only vs\n * read-write decision (there is no default). The app never sees the list; it\n * resolves with the single granted mount, or rejects with a {@link SpaceError}\n * (`cancelled`) if declined. The granted scope is enforced host-side: the mount\n * is chroot'd to the project folder and `ro`-limited accordingly, so paths\n * outside the project are unnameable and writes on a `ro` grant fail `EROFS`.\n *\n * A project folder is the macOS-bundle-like unit an app works in inside a space;\n * the host records which app a folder belongs to (a `.immediately.run/` sidecar),\n * so the picker can surface the app's own projects or let the user create a new\n * one. Observe the granted access via {@link SandboxMount.mode}.\n *\n * Backend-general (§3.5): the picker offers whatever mounts the user has (today,\n * their spaces). Returns the granted mount by its universal id.\n */\nexport const requestMount = (): Promise<SandboxMount> =>\n requestMountInternal('request', {});\n\n/** @deprecated renamed to {@link requestMount} (backend-general, §3.5). */\nexport const requestSpace = requestMount;\n\n// ── content references (plan 12 §E / FILE_SHARING §7) ────────────────────────\n\n/**\n * Build a persisted CONTENT REFERENCE to a file in a mount — a `{mountId, relPath}`\n * pointer your app serializes into ITS OWN content (a board's JSON, an MDX file's\n * frontmatter, an album manifest — the platform doesn't dictate the container) so a\n * later viewer can resolve it. It is exactly the §5.7 {@link capFile} shape: ONE\n * capability, two delivery modes — runtime delegation (a task param, authorized by\n * the caller) vs a durable reference (authorized per-viewer by {@link resolveContentRef}).\n * `relPath` is BACKEND-NATURAL, so the reference resolves to the SAME path for every\n * viewer. Cross-app/cross-project references default to `ro`.\n *\n * const ref = makeContentRef({ mountId: 'space:ACME', relPath: 'office-seating/desk.mdx' }, { mode: 'ro' });\n */\nexport const makeContentRef = (\n ref: { mountId: string; relPath: string },\n opts: { mode: 'ro' | 'rw' },\n): FileCap => ({ $cap: 'file', mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });\n\n/**\n * Resolve a content reference your app found in content it ALREADY holds (plan 12\n * §E). This is a RELAY, not a fabrication: the host honors it ONLY when your app\n * already holds a grant to `ref.mountId` (else `forbidden`) — apps follow\n * writer-authored links inside granted content; they cannot name a space from\n * nothing (T27). The host runs a per-VIEWER consent prompt (named via the owning\n * app's project sidecar), and existence is never leaked — a decline and a\n * non-existent path are indistinguishable.\n *\n * On allow, the host APPENDS a read scope for the referenced path to your grant\n * (durable; same §8.15 lifecycle) and returns the STABLE absolute `path` the file\n * is mounted at — identical for every viewer, so a path the author stored resolves\n * the same for you. Read it through the `fs` module at that path. Rejects with a\n * {@link SpaceError}: `forbidden` (you don't hold the referenced mount) or\n * `cancelled` (the viewer declined / the path doesn't exist — no oracle).\n *\n * const { path } = await resolveContentRef(ref);\n * const text = await fs.promises.readFile(path, 'utf8');\n */\nexport const resolveContentRef = async (ref: FileCap): Promise<{ path: string }> => {\n const path = await request<string>('resolveRef', { ref });\n return { path };\n};\n\n/**\n * Resolve a BATCH of content references in ONE consent round (plan 12 §E). When a\n * board opens with several embedded references, pass them all here: the host\n * coalesces them into a SINGLE consent prompt listing every target, instead of one\n * prompt per reference. Same relay gate and per-viewer semantics as\n * {@link resolveContentRef} (each ref's mount must already be held), applied to the\n * whole set — it is all-or-nothing: the user allows the batch or declines it.\n *\n * Resolves `{ paths }` with the STABLE absolute path of each ref, in input order.\n * Rejects with a {@link SpaceError}: `forbidden` (a referenced mount isn't held) or\n * `cancelled` (the viewer declined).\n *\n * const { paths } = await resolveContentRefs(board.references);\n */\nexport const resolveContentRefs = async (refs: FileCap[]): Promise<{ paths: string[] }> => {\n const paths = await request<string[]>('resolveRefs', { refs });\n return { paths };\n};\n\n// ---------------------------------------------------------------------------\n// Settings — the per-user \"~/.config\"-style space (UI_AS_APPS_SPEC §3.3/§3.5/§8.2).\n// Each app gets its OWN settings subdir, auto-provisioned and chroot'd by the host\n// (no dialog, no powerbox). Read/write it through the returned mount's filesystem\n// port — there is deliberately no key/value get/set API; settings are just files.\n// ---------------------------------------------------------------------------\n\n// Issue a `protocol-settings` request, unwrapping {ok,data} and throwing a typed\n// SpaceError on failure (mirrors `request` for the spaces surface).\nconst settingsRequest = async <T = unknown>(\n method: string,\n query: Record<string, unknown> = {},\n): Promise<T> => {\n const res = (await protocolRequest('settings', method, [query])) as SpaceResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'settings request failed') as SpaceError;\n err.code = (res?.code as SpaceError['code']) ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n/**\n * Mount this app's per-user settings — a private `~/.config`-style filesystem,\n * auto-provisioned for the signed-in user and isolated to THIS app (the host\n * chroots it; a different app can never name it). Read/write config files through\n * the returned mount. Rejects with a {@link SpaceError} (`auth-required`) when\n * signed out. Capability: baseline `settings:app`.\n */\nexport const openSettings = async (): Promise<SandboxMount> => {\n const mount = await settingsRequest<SandboxMount>('open');\n return waitForMount({ id: mount.id ?? mount.path });\n};\n\n/**\n * One-time SEED of this app's settings from the parent it declares as `forkOf`\n * (its `package.json` `immediately.run.forkOf`) — so a fork inherits your\n * preferences from the original app (UI_AS_APPS_SPEC §3.4). The host asks the user\n * to confirm (a full consent when the apps have different owners, a light confirm\n * when the same owner publishes both) and copies the parent's settings into this\n * app's own subdir, skipping any file you already have. Non-throwing: resolves\n * `{ ok:false, code }` on decline (`cancelled`), no declared parent (`forbidden`),\n * or signed-out (`auth-required`). After `{ ok:true }`, read {@link openSettings}.\n * Capability: baseline `settings:fork`.\n */\nexport const importSettingsFromParent = async (): Promise<\n { ok: true; copied: number } | { ok: false; code: string }\n> => {\n try {\n const data = await settingsRequest<{ copied: number }>('importFromParent');\n return { ok: true, copied: data.copied };\n } catch (e) {\n return { ok: false, code: (e as SpaceError).code ?? 'unknown' };\n }\n};\n\n/**\n * Mount ANOTHER app's per-user settings by its `appKey` — the elevated \"file\n * commander\" surface. Rejects `forbidden` unless this app holds the first-party-\n * only `settings:all` capability. Most apps want {@link openSettings} instead.\n */\nexport const openSettingsOf = async (appKey: string): Promise<SandboxMount> => {\n const mount = await settingsRequest<SandboxMount>('openOf', { appKey });\n return waitForMount({ id: mount.id ?? mount.path });\n};\n\n/**\n * List every app that has per-user settings — the elevated \"file commander\"\n * enumeration. Pair with {@link openSettingsOf} to mount any of them. Rejects\n * `forbidden` unless this app holds the first-party-only `settings:all`.\n */\nexport const listSettingsApps = (): Promise<string[]> =>\n settingsRequest<string[]>('list');\n\n/** Create a brand-new, empty platform-hosted space. The app reaches it (or any\n * other space) afterward through the {@link requestMount} powerbox or\n * {@link mountSpace}; there is no implicit per-app binding. */\nexport const createSpace = (\n opts: { name?: string } = {}\n): Promise<SandboxMount> => requestMountInternal('create', opts);\n\n/** List spaces you can access — all of them, or just those bound to this app. */\nexport const listSpaces = (opts: { app?: boolean } = {}): Promise<SpaceInfo[]> =>\n request<SpaceInfo[]>('list', opts);\n\n/** Release a mounted space (stops its listener on the host). */\nexport const unmountSpace = async (query: { spaceId: string }): Promise<void> => {\n await request('unmount', query);\n};\n\n// ---------------------------------------------------------------------------\n// Space management (the space-manager app) — UI_AS_APPS_SPEC §5.2. These are\n// ELEVATED: enumerating all the user's spaces is `spaces:user`; mutating\n// membership (share/unshare/setRole) and resolving handles is `spaces:admin`.\n// The host enforces the owner-lockout invariant (a space always keeps an owner,\n// T41) and rate-limits handle lookups (L1); the OAuth/identity token never\n// crosses to the app.\n// ---------------------------------------------------------------------------\n\nexport type Role = 'owner' | 'writer' | 'reader';\n\n/** A member of a space (for the share/manage UI). */\nexport interface Member {\n /** `user:{uid}` | `group:{gid}`. */\n principal: string;\n role: Role;\n login?: string;\n avatarUrl?: string;\n}\n\n/** A handle resolved to a principal (handle → who). */\nexport interface ResolvedUser {\n uid: string;\n login: string;\n avatarUrl?: string;\n}\n\n/** Enumerate ALL the user's spaces (not just this app's) — `spaces:user`. */\nexport const listAllSpaces = (): Promise<SpaceInfo[]> => request<SpaceInfo[]>('listAll', {});\n\n/** Read a space's members one-shot — `spaces:admin`. */\nexport const getSpaceMembers = (spaceId: string): Promise<Member[]> =>\n request<Member[]>('members', { spaceId });\n\n/** Invite a user (by provider handle) to a space at a role — `spaces:admin`. The\n * host resolves the handle, so the app never sees other users' uids except the\n * one it invited. */\nexport const shareSpace = async (spaceId: string, login: string, role: Role): Promise<void> => {\n await request('share', { spaceId, login, role });\n};\n\n/** Remove a member from a space — `spaces:admin`. Refused if it would orphan the\n * space (owner-lockout, T41). */\nexport const unshareSpace = async (spaceId: string, uid: string): Promise<void> => {\n await request('unshare', { spaceId, uid });\n};\n\n/** Change a member's role — `spaces:admin`. Refused if it would drop the sole\n * owner (owner-lockout, T41). */\nexport const setSpaceRole = async (spaceId: string, uid: string, role: Role): Promise<void> => {\n await request('setRole', { spaceId, uid, role });\n};\n\n/** Resolve a provider handle to a principal (for the invite flow) — `spaces:admin`,\n * rate-limited host-side. */\nexport const lookupUser = (login: string): Promise<ResolvedUser> =>\n request<ResolvedUser>('lookupUser', { login });\n\n/** One durable grant an app holds, for the §8.11 capability audit view. */\nexport interface GrantRecord {\n /** The app's provider-qualified identity (`provider__namespace__repository`). */\n appKey: string;\n spaceId: string;\n /** Universal mount id (§3.5). */\n mountId: string;\n subtree?: string;\n mode: 'ro' | 'rw';\n name?: string;\n}\n\n/** Enumerate every (app, mount) grant the user holds — the audit view\n * (§8.11). Elevated `spaces:admin`. */\nexport const listGrants = (): Promise<GrantRecord[]> => request<GrantRecord[]>('grants', {});\n\n/** Revoke one app's grant on a space — durable (the app can't re-mount) plus a\n * best-effort live teardown. Elevated `spaces:admin`. */\nexport const revokeGrant = async (appKey: string, spaceId: string): Promise<void> => {\n await request('revokeGrant', { appKey, spaceId });\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAoC;AACpC,0BAAgC;AAChC,yBAA+B;AAC/B,wBAA6B;AAatB,MAAM,kBAAkB,UAAc,mCAAe,GAAG,gBAAgB;AAmF/E,MAAM,eAAe,MAAoB;AAEvC,SAAO,OAAO,WAAW,OAAO,QAAQ;AAC1C;AAMA,MAAM,UAAU,CAACA,QAAqB,cACpC,gCAAaA,QAAO,KAAK;AASpB,MAAM,YAAY,MAAsB,aAAa,EAAE,UAAU;AAGjE,MAAM,YAAY,CAAC,UACxB,UAAU,EAAE,KAAK,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC;AAUpC,MAAM,iBAAiB,CAC5B,aACiB;AACjB,QAAM,aAAa,aAAa,EAAE,SAAS,QAAQ;AACnD,SAAO,MAAM,WAAW,QAAQ;AAClC;AAOO,MAAM,eAAe,CAAC,UAC3B,IAAI,QAAQ,CAAC,YAAY;AACvB,QAAM,cAAc,eAAe,CAAC,WAAW;AAC7C,UAAM,QAAQ,OAAO,KAAK,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC;AAClD,QAAI,OAAO;AAET,cAAQ,QAAQ,EAAE,KAAK,WAAW;AAClC,cAAQ,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AACH,CAAC;AAGI,MAAM,YAAY,MAAsB;AAC7C,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAyB,SAAS;AAC9D,8BAAU,MAAM,eAAe,SAAS,GAAG,CAAC,CAAC;AAC7C,SAAO;AACT;AAkCA,MAAM,UAAU,OACd,QACA,QAAiC,CAAC,MACnB;AACf,QAAM,MAAO,UAAM,qCAAgB,UAAU,QAAQ,CAAC,KAAK,CAAC;AAC5D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,sBAAsB;AAC5D,QAAI,OAAQ,KAAK,QAA+B;AAChD,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AAKA,MAAM,uBAAuB,OAC3B,QACA,UAC0B;AAC1B,QAAMA,SAAQ,MAAM,QAAsB,QAAQ,KAAK;AACvD,SAAO,aAAa,EAAE,IAAIA,OAAM,MAAMA,OAAM,KAAK,CAAC;AACpD;AAQO,MAAM,QAAQ,CAAC,YACpB,qBAAqB,SAAS,EAAE,OAAO,QAAQ,CAAC;AAI3C,MAAM,aAAa,CAAC,UACzB,MAAM,SAAS,MAAM,OAAO,EAAE;AAqBzB,MAAM,eAAe,MAC1B,qBAAqB,WAAW,CAAC,CAAC;AAG7B,MAAM,eAAe;AAgBrB,MAAM,iBAAiB,CAC5B,KACA,UACa,EAAE,MAAM,QAAQ,SAAS,IAAI,SAAS,SAAS,IAAI,SAAS,MAAM,KAAK,KAAK;AAqBpF,MAAM,oBAAoB,OAAO,QAA4C;AAClF,QAAM,OAAO,MAAM,QAAgB,cAAc,EAAE,IAAI,CAAC;AACxD,SAAO,EAAE,KAAK;AAChB;AAgBO,MAAM,qBAAqB,OAAO,SAAkD;AACzF,QAAM,QAAQ,MAAM,QAAkB,eAAe,EAAE,KAAK,CAAC;AAC7D,SAAO,EAAE,MAAM;AACjB;AAWA,MAAM,kBAAkB,OACtB,QACA,QAAiC,CAAC,MACnB;AACf,QAAM,MAAO,UAAM,qCAAgB,YAAY,QAAQ,CAAC,KAAK,CAAC;AAC9D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,yBAAyB;AAC/D,QAAI,OAAQ,KAAK,QAA+B;AAChD,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AASO,MAAM,eAAe,YAAmC;AAC7D,QAAMA,SAAQ,MAAM,gBAA8B,MAAM;AACxD,SAAO,aAAa,EAAE,IAAIA,OAAM,MAAMA,OAAM,KAAK,CAAC;AACpD;AAaO,MAAM,2BAA2B,YAEnC;AACH,MAAI;AACF,UAAM,OAAO,MAAM,gBAAoC,kBAAkB;AACzE,WAAO,EAAE,IAAI,MAAM,QAAQ,KAAK,OAAO;AAAA,EACzC,SAAS,GAAG;AACV,WAAO,EAAE,IAAI,OAAO,MAAO,EAAiB,QAAQ,UAAU;AAAA,EAChE;AACF;AAOO,MAAM,iBAAiB,OAAO,WAA0C;AAC7E,QAAMA,SAAQ,MAAM,gBAA8B,UAAU,EAAE,OAAO,CAAC;AACtE,SAAO,aAAa,EAAE,IAAIA,OAAM,MAAMA,OAAM,KAAK,CAAC;AACpD;AAOO,MAAM,mBAAmB,MAC9B,gBAA0B,MAAM;AAK3B,MAAM,cAAc,CACzB,OAA0B,CAAC,MACD,qBAAqB,UAAU,IAAI;AAGxD,MAAM,aAAa,CAAC,OAA0B,CAAC,MACpD,QAAqB,QAAQ,IAAI;AAG5B,MAAM,eAAe,OAAO,UAA8C;AAC/E,QAAM,QAAQ,WAAW,KAAK;AAChC;AA8BO,MAAM,gBAAgB,MAA4B,QAAqB,WAAW,CAAC,CAAC;AAGpF,MAAM,kBAAkB,CAAC,YAC9B,QAAkB,WAAW,EAAE,QAAQ,CAAC;AAKnC,MAAM,aAAa,OAAO,SAAiB,OAAe,SAA8B;AAC7F,QAAM,QAAQ,SAAS,EAAE,SAAS,OAAO,KAAK,CAAC;AACjD;AAIO,MAAM,eAAe,OAAO,SAAiB,QAA+B;AACjF,QAAM,QAAQ,WAAW,EAAE,SAAS,IAAI,CAAC;AAC3C;AAIO,MAAM,eAAe,OAAO,SAAiB,KAAa,SAA8B;AAC7F,QAAM,QAAQ,WAAW,EAAE,SAAS,KAAK,KAAK,CAAC;AACjD;AAIO,MAAM,aAAa,CAAC,UACzB,QAAsB,cAAc,EAAE,MAAM,CAAC;AAgBxC,MAAM,aAAa,MAA8B,QAAuB,UAAU,CAAC,CAAC;AAIpF,MAAM,cAAc,OAAO,QAAgB,YAAmC;AACnF,QAAM,QAAQ,eAAe,EAAE,QAAQ,QAAQ,CAAC;AAClD;","names":["mount"]}