@immediately-run/sdk 0.8.1 → 0.10.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.
- package/dist/boot.cjs +4 -3
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.js +4 -3
- package/dist/boot.js.map +1 -1
- package/dist/editor.cjs +9 -0
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.d.cts +47 -1
- package/dist/editor.d.ts +47 -1
- package/dist/editor.js +6 -0
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/injectedBundler.cjs +49 -0
- package/dist/injectedBundler.cjs.map +1 -0
- package/dist/injectedBundler.d.cts +29 -0
- package/dist/injectedBundler.d.ts +29 -0
- package/dist/injectedBundler.js +24 -0
- package/dist/injectedBundler.js.map +1 -0
- package/dist/irMarkers.cjs +72 -0
- package/dist/irMarkers.cjs.map +1 -0
- package/dist/irMarkers.d.cts +54 -0
- package/dist/irMarkers.d.ts +54 -0
- package/dist/irMarkers.js +44 -0
- package/dist/irMarkers.js.map +1 -0
- package/dist/mountMatch.cjs +29 -0
- package/dist/mountMatch.cjs.map +1 -0
- package/dist/mountMatch.d.cts +21 -0
- package/dist/mountMatch.d.ts +21 -0
- package/dist/mountMatch.js +5 -0
- package/dist/mountMatch.js.map +1 -0
- package/dist/mounts.cjs +106 -6
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.d.cts +145 -19
- package/dist/mounts.d.ts +145 -19
- package/dist/mounts.js +100 -6
- package/dist/mounts.js.map +1 -1
- package/dist/ready.cjs +69 -0
- package/dist/ready.cjs.map +1 -0
- package/dist/ready.d.cts +32 -0
- package/dist/ready.d.ts +32 -0
- package/dist/ready.js +41 -0
- package/dist/ready.js.map +1 -0
- package/dist/tasks.cjs +3 -0
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.d.cts +24 -1
- package/dist/tasks.d.ts +24 -1
- package/dist/tasks.js +2 -0
- package/dist/tasks.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 +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
|
-
|
|
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,65 @@ 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
|
-
const
|
|
50
|
-
|
|
56
|
+
const mountKey = (m) => m.id ?? m.path;
|
|
57
|
+
const MOUNT_REMOVE_REASONS = /* @__PURE__ */ new Set([
|
|
58
|
+
"revoked",
|
|
59
|
+
"unshared",
|
|
60
|
+
"signed-out",
|
|
61
|
+
"unmounted",
|
|
62
|
+
"deleted"
|
|
63
|
+
]);
|
|
64
|
+
const asMountRemoveReason = (value) => typeof value === "string" && MOUNT_REMOVE_REASONS.has(value) ? value : "revoked";
|
|
65
|
+
const injectedMountService = () => {
|
|
66
|
+
try {
|
|
67
|
+
const svc = module?.evaluation?.module?.bundler?.mounts;
|
|
68
|
+
return svc && typeof svc.getMounts === "function" ? svc : null;
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
let transportSvc = null;
|
|
74
|
+
const transportMountService = () => {
|
|
75
|
+
if (transportSvc) return transportSvc;
|
|
76
|
+
let mounts = [];
|
|
77
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
78
|
+
const fire = (removed) => {
|
|
79
|
+
for (const l of [...listeners]) l(mounts, removed);
|
|
80
|
+
};
|
|
81
|
+
(0, import_sandboxUtils.addListener)("mount-add", (msg) => {
|
|
82
|
+
const mount2 = msg.mount;
|
|
83
|
+
if (!mount2) return;
|
|
84
|
+
const key = mountKey(mount2);
|
|
85
|
+
mounts = [...mounts.filter((m) => mountKey(m) !== key), mount2];
|
|
86
|
+
fire([]);
|
|
87
|
+
});
|
|
88
|
+
(0, import_sandboxUtils.addListener)("mount-remove", (msg) => {
|
|
89
|
+
const key = msg.id ?? msg.path;
|
|
90
|
+
if (key == null) return;
|
|
91
|
+
const reason = asMountRemoveReason(msg.reason);
|
|
92
|
+
const removed = mounts.filter((m) => mountKey(m) === key).map((m) => ({ ...m, reason }));
|
|
93
|
+
if (removed.length === 0) return;
|
|
94
|
+
mounts = mounts.filter((m) => mountKey(m) !== key);
|
|
95
|
+
fire(removed);
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
(0, import_sandboxUtils.sendMessage)("request-mounts");
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
transportSvc = {
|
|
102
|
+
getMounts: () => mounts,
|
|
103
|
+
onChange: (listener) => {
|
|
104
|
+
listeners.add(listener);
|
|
105
|
+
listener(mounts, []);
|
|
106
|
+
return { dispose: () => listeners.delete(listener) };
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
return transportSvc;
|
|
51
110
|
};
|
|
52
|
-
const
|
|
111
|
+
const mountService = () => injectedMountService() ?? transportMountService();
|
|
112
|
+
const matches = (mount2, query) => (0, import_mountMatch.mountMatches)(mount2, query);
|
|
53
113
|
const getMounts = () => mountService().getMounts();
|
|
54
114
|
const findMount = (query) => getMounts().find((m) => matches(m, query));
|
|
55
115
|
const onMountsChange = (listener) => {
|
|
@@ -83,11 +143,45 @@ const requestMountInternal = async (method, query) => {
|
|
|
83
143
|
const mount2 = await request(method, query);
|
|
84
144
|
return waitForMount({ id: mount2.id ?? mount2.path });
|
|
85
145
|
};
|
|
86
|
-
const openAppSpace = (slot = "default") => requestMountInternal("open", { slot });
|
|
87
146
|
const mount = (mountId) => requestMountInternal("mount", { mount: mountId });
|
|
88
147
|
const mountSpace = (query) => mount(`space:${query.spaceId}`);
|
|
89
148
|
const requestMount = () => requestMountInternal("request", {});
|
|
90
149
|
const requestSpace = requestMount;
|
|
150
|
+
const makeContentRef = (ref, opts) => ({ $cap: "file", mountId: ref.mountId, relPath: ref.relPath, mode: opts.mode });
|
|
151
|
+
const resolveContentRef = async (ref) => {
|
|
152
|
+
const path = await request("resolveRef", { ref });
|
|
153
|
+
return { path };
|
|
154
|
+
};
|
|
155
|
+
const resolveContentRefs = async (refs) => {
|
|
156
|
+
const paths = await request("resolveRefs", { refs });
|
|
157
|
+
return { paths };
|
|
158
|
+
};
|
|
159
|
+
const settingsRequest = async (method, query = {}) => {
|
|
160
|
+
const res = await (0, import_sandboxUtils.protocolRequest)("settings", method, [query]);
|
|
161
|
+
if (!res || res.ok !== true) {
|
|
162
|
+
const err = new Error(res?.message ?? "settings request failed");
|
|
163
|
+
err.code = res?.code ?? "unknown";
|
|
164
|
+
throw err;
|
|
165
|
+
}
|
|
166
|
+
return res.data;
|
|
167
|
+
};
|
|
168
|
+
const openSettings = async () => {
|
|
169
|
+
const mount2 = await settingsRequest("open");
|
|
170
|
+
return waitForMount({ id: mount2.id ?? mount2.path });
|
|
171
|
+
};
|
|
172
|
+
const importSettingsFromParent = async () => {
|
|
173
|
+
try {
|
|
174
|
+
const data = await settingsRequest("importFromParent");
|
|
175
|
+
return { ok: true, copied: data.copied };
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return { ok: false, code: e.code ?? "unknown" };
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const openSettingsOf = async (appKey) => {
|
|
181
|
+
const mount2 = await settingsRequest("openOf", { appKey });
|
|
182
|
+
return waitForMount({ id: mount2.id ?? mount2.path });
|
|
183
|
+
};
|
|
184
|
+
const listSettingsApps = () => settingsRequest("list");
|
|
91
185
|
const createSpace = (opts = {}) => requestMountInternal("create", opts);
|
|
92
186
|
const listSpaces = (opts = {}) => request("list", opts);
|
|
93
187
|
const unmountSpace = async (query) => {
|
|
@@ -116,16 +210,22 @@ const revokeGrant = async (appKey, spaceId) => {
|
|
|
116
210
|
getAppMountPath,
|
|
117
211
|
getMounts,
|
|
118
212
|
getSpaceMembers,
|
|
213
|
+
importSettingsFromParent,
|
|
119
214
|
listAllSpaces,
|
|
120
215
|
listGrants,
|
|
216
|
+
listSettingsApps,
|
|
121
217
|
listSpaces,
|
|
122
218
|
lookupUser,
|
|
219
|
+
makeContentRef,
|
|
123
220
|
mount,
|
|
124
221
|
mountSpace,
|
|
125
222
|
onMountsChange,
|
|
126
|
-
|
|
223
|
+
openSettings,
|
|
224
|
+
openSettingsOf,
|
|
127
225
|
requestMount,
|
|
128
226
|
requestSpace,
|
|
227
|
+
resolveContentRef,
|
|
228
|
+
resolveContentRefs,
|
|
129
229
|
revokeGrant,
|
|
130
230
|
setSpaceRole,
|
|
131
231
|
shareSpace,
|