@immediately-run/sdk 0.11.0 → 0.12.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 (45) hide show
  1. package/dist/boot.cjs +12 -1
  2. package/dist/boot.cjs.map +1 -1
  3. package/dist/boot.js +14 -3
  4. package/dist/boot.js.map +1 -1
  5. package/dist/diagnostics.cjs +51 -0
  6. package/dist/diagnostics.cjs.map +1 -0
  7. package/dist/diagnostics.d.cts +43 -0
  8. package/dist/diagnostics.d.ts +43 -0
  9. package/dist/diagnostics.js +25 -0
  10. package/dist/diagnostics.js.map +1 -0
  11. package/dist/hostRuntime.cjs.map +1 -1
  12. package/dist/hostRuntime.d.cts +5 -0
  13. package/dist/hostRuntime.d.ts +5 -0
  14. package/dist/hostRuntime.js.map +1 -1
  15. package/dist/index.cjs +4 -0
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +2 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/markers.cjs +59 -0
  22. package/dist/markers.cjs.map +1 -0
  23. package/dist/markers.d.cts +21 -0
  24. package/dist/markers.d.ts +21 -0
  25. package/dist/markers.js +32 -0
  26. package/dist/markers.js.map +1 -0
  27. package/dist/region.cjs +47 -0
  28. package/dist/region.cjs.map +1 -0
  29. package/dist/region.d.cts +14 -0
  30. package/dist/region.d.ts +14 -0
  31. package/dist/region.js +22 -0
  32. package/dist/region.js.map +1 -0
  33. package/dist/testing.cjs +74 -0
  34. package/dist/testing.cjs.map +1 -0
  35. package/dist/testing.d.cts +53 -0
  36. package/dist/testing.d.ts +53 -0
  37. package/dist/testing.js +50 -0
  38. package/dist/testing.js.map +1 -0
  39. package/dist/version.cjs +1 -1
  40. package/dist/version.cjs.map +1 -1
  41. package/dist/version.d.cts +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/dist/version.js.map +1 -1
  45. package/package.json +1 -1
package/dist/boot.cjs CHANGED
@@ -26,6 +26,7 @@ module.exports = __toCommonJS(boot_exports);
26
26
  var import_jsx_runtime = require("react/jsx-runtime");
27
27
  var import_react = require("react");
28
28
  var import_client = require("react-dom/client");
29
+ var import_markers = require("./markers");
29
30
  var import_errors = require("./components/errors");
30
31
  var import_FileRouter = require("./components/FileRouter");
31
32
  var import_MainContent = require("./components/MainContent");
@@ -85,6 +86,13 @@ const TinkerableApp = ({ routingSpec }) => {
85
86
  }, [setContext]);
86
87
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TinkerableContext.TinkerableContext, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_routing.Router, {}) });
87
88
  };
89
+ const BootMarkers = () => {
90
+ (0, import_react.useLayoutEffect)(() => {
91
+ (0, import_markers.emitMarkerOnce)("ir.fmp");
92
+ (0, import_markers.emitMarkerOnce)("ir.interactive");
93
+ }, []);
94
+ return null;
95
+ };
88
96
  const escapeForRegexp = (str) => str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
89
97
  const DEFAULT_ROUTING_SPEC = {
90
98
  routes: [
@@ -108,7 +116,10 @@ const boot = ({
108
116
  const moduleCache = new import_moduleCache.ModuleCache();
109
117
  const root = (0, import_client.createRoot)(rootElement);
110
118
  root.render(
111
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.StrictMode, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_moduleCache.ModuleCacheContextProvider, { moduleCache, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MDXProvider.MDXProvider, { components: mdxComponents, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TinkerableApp, { routingSpec }) }) }) })
119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.StrictMode, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_moduleCache.ModuleCacheContextProvider, { moduleCache, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_MDXProvider.MDXProvider, { components: mdxComponents, children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BootMarkers, {}),
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TinkerableApp, { routingSpec })
122
+ ] }) }) })
112
123
  );
113
124
  };
114
125
  // Annotate the CommonJS export names for ESM import in node:
package/dist/boot.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, StrictMode, useEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\nexport const TinkerableApp = ({ routingSpec }: { routingSpec: RoutingSpec }) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n <Router />\n </TinkerableContext>\n );\n};\n\n// from: https://stackoverflow.com/a/63838890\nconst escapeForRegexp = (str: string) => str.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, reactNode: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n reactNode: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, reactNode: <ErrorNotFound /> },\n ],\n};\n\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec = DEFAULT_ROUTING_SPEC,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={mdxComponents}>\n <TinkerableApp routingSpec={routingSpec} />\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8EM;AA9EN,mBAAoD;AACpD,oBAA2B;AAE3B,oBAA8B;AAC9B,wBAA2B;AAC3B,yBAA4B;AAC5B,2BAAuC;AACvC,0BAAiD;AACjD,6BAAkE;AAClE,yBAA4B;AAC5B,yBAAwD;AACxD,qBAAuB;AAGvB,0BAA4B;AAC5B,+BAAmD;AACnD,sBAA6B;AAO7B,MAAM,uBAAuB,CAAC,eAA8B,WAA0B;AACpF,WAAS,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,QAAI,cAAc,GAAG,MAAM,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,gBAAgB,CAAC,EAAE,YAAY,MAAoC;AAC9E,QAAM,CAAC,SAAS,UAAU,QAAI,2BAA0B,uCAAkB,WAAW,CAAC;AACtF,8BAAU,MAAM;AACd,UAAM,qBAAiB,iCAAY,aAAa,CAAC,EAAE,IAAI,MAAM;AAC3D,iBAAW,CAACA,aAAY;AACtB,cAAM,qBAAiB,mCAAcA,UAAS,GAAG;AACjD,YAAI,mBAAmBA,UAAS;AAC9B,kBAAQ;AAAA,YACN,gCAAgCA,SAAQ,gBAAgB,WAAW,OAAO,eAAe,gBAAgB,WAAW;AAAA,UACtH;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AACf,8BAAU,MAAM;AAKd,UAAM,aAAS,kDAAsB,mDAA2B,CAAC;AACjE,UAAM,cAAU;AAAA,MACd;AAAA,MACA,CAAC,EAAE,OAAO,MAA2B;AACnC;AAAA,UAAW,CAAC,gBACV,qBAAqB,YAAY,eAAe,MAAM,IAClD,cACA;AAAA,YACE,GAAG;AAAA,YACH,eAAe;AAAA;AAAA,cAEb,GAAG,YAAY;AAAA,cACf,GAAG;AAAA,YACL;AAAA,UACF;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,IACT;AACA,WAAO,OAAO;AACd,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,4CAAC,8CAAkB,OAAO,SACxB,sDAAC,yBAAO,GACV;AAEJ;AAGA,MAAM,kBAAkB,CAAC,QAAgB,IAAI,QAAQ,yBAAyB,MAAM;AAE7E,MAAM,uBAAoC;AAAA,EAC/C,QAAQ;AAAA,IACN,EAAE,MAAM,eAAe,SAAS,QAAQ,WAAW,4CAAC,kCAAY,EAAG;AAAA,IACnE;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI,OAAO,IAAI,gBAAgB,4BAAY,CAAC,mBAAoB;AAAA,MACzE,WAAW,4CAAC,gCAAW;AAAA,IACzB;AAAA,IACA,EAAE,MAAM,iBAAiB,SAAS,iBAAiB,WAAW,4CAAC,+BAAc,EAAG;AAAA,EAClF;AACF;AAEO,MAAM,OAAO,CAAC;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAChB,IAAe,CAAC,MAAM;AACpB,QAAM,cAAc,SAAS,eAAe,MAAM;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,QAAM,cAAc,IAAI,+BAAY;AACpC,QAAM,WAAO,0BAAW,WAAW;AACnC,OAAK;AAAA,IACH,4CAAC,2BACC,sDAAC,iDAA2B,aAC1B,sDAAC,kCAAY,YAAY,eACvB,sDAAC,iBAAc,aAA0B,GAC3C,GACF,GACF;AAAA,EACF;AACF;","names":["context"]}
1
+ {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\nexport const TinkerableApp = ({ routingSpec }: { routingSpec: RoutingSpec }) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n <Router />\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n// from: https://stackoverflow.com/a/63838890\nconst escapeForRegexp = (str: string) => str.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, reactNode: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n reactNode: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, reactNode: <ErrorNotFound /> },\n ],\n};\n\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec = DEFAULT_ROUTING_SPEC,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={mdxComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={routingSpec} />\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgFM;AAhFN,mBAAqE;AACrE,oBAA2B;AAE3B,qBAA+B;AAE/B,oBAA8B;AAC9B,wBAA2B;AAC3B,yBAA4B;AAC5B,2BAAuC;AACvC,0BAAiD;AACjD,6BAAkE;AAClE,yBAA4B;AAC5B,yBAAwD;AACxD,qBAAuB;AAGvB,0BAA4B;AAC5B,+BAAmD;AACnD,sBAA6B;AAO7B,MAAM,uBAAuB,CAAC,eAA8B,WAA0B;AACpF,WAAS,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,QAAI,cAAc,GAAG,MAAM,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,gBAAgB,CAAC,EAAE,YAAY,MAAoC;AAC9E,QAAM,CAAC,SAAS,UAAU,QAAI,2BAA0B,uCAAkB,WAAW,CAAC;AACtF,8BAAU,MAAM;AACd,UAAM,qBAAiB,iCAAY,aAAa,CAAC,EAAE,IAAI,MAAM;AAC3D,iBAAW,CAACA,aAAY;AACtB,cAAM,qBAAiB,mCAAcA,UAAS,GAAG;AACjD,YAAI,mBAAmBA,UAAS;AAC9B,kBAAQ;AAAA,YACN,gCAAgCA,SAAQ,gBAAgB,WAAW,OAAO,eAAe,gBAAgB,WAAW;AAAA,UACtH;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AACf,8BAAU,MAAM;AAKd,UAAM,aAAS,kDAAsB,mDAA2B,CAAC;AACjE,UAAM,cAAU;AAAA,MACd;AAAA,MACA,CAAC,EAAE,OAAO,MAA2B;AACnC;AAAA,UAAW,CAAC,gBACV,qBAAqB,YAAY,eAAe,MAAM,IAClD,cACA;AAAA,YACE,GAAG;AAAA,YACH,eAAe;AAAA;AAAA,cAEb,GAAG,YAAY;AAAA,cACf,GAAG;AAAA,YACL;AAAA,UACF;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,IACT;AACA,WAAO,OAAO;AACd,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,4CAAC,8CAAkB,OAAO,SACxB,sDAAC,yBAAO,GACV;AAEJ;AASA,MAAM,cAAc,MAAY;AAC9B,oCAAgB,MAAM;AACpB,uCAAe,QAAQ;AACvB,uCAAe,gBAAgB;AAAA,EACjC,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAGA,MAAM,kBAAkB,CAAC,QAAgB,IAAI,QAAQ,yBAAyB,MAAM;AAE7E,MAAM,uBAAoC;AAAA,EAC/C,QAAQ;AAAA,IACN,EAAE,MAAM,eAAe,SAAS,QAAQ,WAAW,4CAAC,kCAAY,EAAG;AAAA,IACnE;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI,OAAO,IAAI,gBAAgB,4BAAY,CAAC,mBAAoB;AAAA,MACzE,WAAW,4CAAC,gCAAW;AAAA,IACzB;AAAA,IACA,EAAE,MAAM,iBAAiB,SAAS,iBAAiB,WAAW,4CAAC,+BAAc,EAAG;AAAA,EAClF;AACF;AAEO,MAAM,OAAO,CAAC;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAChB,IAAe,CAAC,MAAM;AACpB,QAAM,cAAc,SAAS,eAAe,MAAM;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,QAAM,cAAc,IAAI,+BAAY;AACpC,QAAM,WAAO,0BAAW,WAAW;AACnC,OAAK;AAAA,IACH,4CAAC,2BACC,sDAAC,iDAA2B,aAC1B,uDAAC,kCAAY,YAAY,eACvB;AAAA,kDAAC,eAAY;AAAA,MACb,4CAAC,iBAAc,aAA0B;AAAA,OAC3C,GACF,GACF;AAAA,EACF;AACF;","names":["context"]}
package/dist/boot.js CHANGED
@@ -1,6 +1,7 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import { StrictMode, useEffect, useState } from "react";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { StrictMode, useEffect, useLayoutEffect, useState } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
+ import { emitMarkerOnce } from "./markers";
4
5
  import { ErrorNotFound } from "./components/errors";
5
6
  import { FileRouter } from "./components/FileRouter";
6
7
  import { MainContent } from "./components/MainContent";
@@ -60,6 +61,13 @@ const TinkerableApp = ({ routingSpec }) => {
60
61
  }, [setContext]);
61
62
  return /* @__PURE__ */ jsx(TinkerableContext, { value: context, children: /* @__PURE__ */ jsx(Router, {}) });
62
63
  };
64
+ const BootMarkers = () => {
65
+ useLayoutEffect(() => {
66
+ emitMarkerOnce("ir.fmp");
67
+ emitMarkerOnce("ir.interactive");
68
+ }, []);
69
+ return null;
70
+ };
63
71
  const escapeForRegexp = (str) => str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
64
72
  const DEFAULT_ROUTING_SPEC = {
65
73
  routes: [
@@ -83,7 +91,10 @@ const boot = ({
83
91
  const moduleCache = new ModuleCache();
84
92
  const root = createRoot(rootElement);
85
93
  root.render(
86
- /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(ModuleCacheContextProvider, { moduleCache, children: /* @__PURE__ */ jsx(MDXProvider, { components: mdxComponents, children: /* @__PURE__ */ jsx(TinkerableApp, { routingSpec }) }) }) })
94
+ /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(ModuleCacheContextProvider, { moduleCache, children: /* @__PURE__ */ jsxs(MDXProvider, { components: mdxComponents, children: [
95
+ /* @__PURE__ */ jsx(BootMarkers, {}),
96
+ /* @__PURE__ */ jsx(TinkerableApp, { routingSpec })
97
+ ] }) }) })
87
98
  );
88
99
  };
89
100
  export {
package/dist/boot.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, StrictMode, useEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\nexport const TinkerableApp = ({ routingSpec }: { routingSpec: RoutingSpec }) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n <Router />\n </TinkerableContext>\n );\n};\n\n// from: https://stackoverflow.com/a/63838890\nconst escapeForRegexp = (str: string) => str.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, reactNode: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n reactNode: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, reactNode: <ErrorNotFound /> },\n ],\n};\n\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec = DEFAULT_ROUTING_SPEC,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={mdxComponents}>\n <TinkerableApp routingSpec={routingSpec} />\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"AA8EM;AA9EN,SAAa,YAAY,WAAW,gBAAgB;AACpD,SAAS,kBAAkB;AAE3B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,8BAA8B;AACvC,SAAS,mBAAmB,qBAAqB;AACjD,SAAS,4BAA4B,6BAA6B;AAClE,SAAS,mBAAmB;AAC5B,SAAS,aAAa,kCAAkC;AACxD,SAAS,cAAc;AAGvB,SAAS,mBAAmB;AAC5B,SAAS,yBAA0C;AACnD,SAAS,oBAAoB;AAO7B,MAAM,uBAAuB,CAAC,eAA8B,WAA0B;AACpF,WAAS,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,QAAI,cAAc,GAAG,MAAM,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,gBAAgB,CAAC,EAAE,YAAY,MAAoC;AAC9E,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,kBAAkB,WAAW,CAAC;AACtF,YAAU,MAAM;AACd,UAAM,iBAAiB,YAAY,aAAa,CAAC,EAAE,IAAI,MAAM;AAC3D,iBAAW,CAACA,aAAY;AACtB,cAAM,iBAAiB,cAAcA,UAAS,GAAG;AACjD,YAAI,mBAAmBA,UAAS;AAC9B,kBAAQ;AAAA,YACN,gCAAgCA,SAAQ,gBAAgB,WAAW,OAAO,eAAe,gBAAgB,WAAW;AAAA,UACtH;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AACf,YAAU,MAAM;AAKd,UAAM,SAAS,sBAAsB,2BAA2B,CAAC;AACjE,UAAM,UAAU;AAAA,MACd;AAAA,MACA,CAAC,EAAE,OAAO,MAA2B;AACnC;AAAA,UAAW,CAAC,gBACV,qBAAqB,YAAY,eAAe,MAAM,IAClD,cACA;AAAA,YACE,GAAG;AAAA,YACH,eAAe;AAAA;AAAA,cAEb,GAAG,YAAY;AAAA,cACf,GAAG;AAAA,YACL;AAAA,UACF;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,IACT;AACA,WAAO,OAAO;AACd,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,oBAAC,qBAAkB,OAAO,SACxB,8BAAC,UAAO,GACV;AAEJ;AAGA,MAAM,kBAAkB,CAAC,QAAgB,IAAI,QAAQ,yBAAyB,MAAM;AAE7E,MAAM,uBAAoC;AAAA,EAC/C,QAAQ;AAAA,IACN,EAAE,MAAM,eAAe,SAAS,QAAQ,WAAW,oBAAC,eAAY,EAAG;AAAA,IACnE;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI,OAAO,IAAI,gBAAgB,YAAY,CAAC,mBAAoB;AAAA,MACzE,WAAW,oBAAC,cAAW;AAAA,IACzB;AAAA,IACA,EAAE,MAAM,iBAAiB,SAAS,iBAAiB,WAAW,oBAAC,iBAAc,EAAG;AAAA,EAClF;AACF;AAEO,MAAM,OAAO,CAAC;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAChB,IAAe,CAAC,MAAM;AACpB,QAAM,cAAc,SAAS,eAAe,MAAM;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,QAAM,cAAc,IAAI,YAAY;AACpC,QAAM,OAAO,WAAW,WAAW;AACnC,OAAK;AAAA,IACH,oBAAC,cACC,8BAAC,8BAA2B,aAC1B,8BAAC,eAAY,YAAY,eACvB,8BAAC,iBAAc,aAA0B,GAC3C,GACF,GACF;AAAA,EACF;AACF;","names":["context"]}
1
+ {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\nexport const TinkerableApp = ({ routingSpec }: { routingSpec: RoutingSpec }) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n <Router />\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n// from: https://stackoverflow.com/a/63838890\nconst escapeForRegexp = (str: string) => str.replace(/[.*+\\-?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, reactNode: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n reactNode: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, reactNode: <ErrorNotFound /> },\n ],\n};\n\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec = DEFAULT_ROUTING_SPEC,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={mdxComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={routingSpec} />\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"AAgFM,cAgDE,YAhDF;AAhFN,SAAa,YAAY,WAAW,iBAAiB,gBAAgB;AACrE,SAAS,kBAAkB;AAE3B,SAAS,sBAAsB;AAE/B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,8BAA8B;AACvC,SAAS,mBAAmB,qBAAqB;AACjD,SAAS,4BAA4B,6BAA6B;AAClE,SAAS,mBAAmB;AAC5B,SAAS,aAAa,kCAAkC;AACxD,SAAS,cAAc;AAGvB,SAAS,mBAAmB;AAC5B,SAAS,yBAA0C;AACnD,SAAS,oBAAoB;AAO7B,MAAM,uBAAuB,CAAC,eAA8B,WAA0B;AACpF,WAAS,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC/C,QAAI,cAAc,GAAG,MAAM,OAAO;AAChC,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,gBAAgB,CAAC,EAAE,YAAY,MAAoC;AAC9E,QAAM,CAAC,SAAS,UAAU,IAAI,SAA0B,kBAAkB,WAAW,CAAC;AACtF,YAAU,MAAM;AACd,UAAM,iBAAiB,YAAY,aAAa,CAAC,EAAE,IAAI,MAAM;AAC3D,iBAAW,CAACA,aAAY;AACtB,cAAM,iBAAiB,cAAcA,UAAS,GAAG;AACjD,YAAI,mBAAmBA,UAAS;AAC9B,kBAAQ;AAAA,YACN,gCAAgCA,SAAQ,gBAAgB,WAAW,OAAO,eAAe,gBAAgB,WAAW;AAAA,UACtH;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AACf,YAAU,MAAM;AAKd,UAAM,SAAS,sBAAsB,2BAA2B,CAAC;AACjE,UAAM,UAAU;AAAA,MACd;AAAA,MACA,CAAC,EAAE,OAAO,MAA2B;AACnC;AAAA,UAAW,CAAC,gBACV,qBAAqB,YAAY,eAAe,MAAM,IAClD,cACA;AAAA,YACE,GAAG;AAAA,YACH,eAAe;AAAA;AAAA,cAEb,GAAG,YAAY;AAAA,cACf,GAAG;AAAA,YACL;AAAA,UACF;AAAA,QACN;AAAA,MACF;AAAA,MACA,OAAO;AAAA,IACT;AACA,WAAO,OAAO;AACd,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SACE,oBAAC,qBAAkB,OAAO,SACxB,8BAAC,UAAO,GACV;AAEJ;AASA,MAAM,cAAc,MAAY;AAC9B,kBAAgB,MAAM;AACpB,mBAAe,QAAQ;AACvB,mBAAe,gBAAgB;AAAA,EACjC,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAGA,MAAM,kBAAkB,CAAC,QAAgB,IAAI,QAAQ,yBAAyB,MAAM;AAE7E,MAAM,uBAAoC;AAAA,EAC/C,QAAQ;AAAA,IACN,EAAE,MAAM,eAAe,SAAS,QAAQ,WAAW,oBAAC,eAAY,EAAG;AAAA,IACnE;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI,OAAO,IAAI,gBAAgB,YAAY,CAAC,mBAAoB;AAAA,MACzE,WAAW,oBAAC,cAAW;AAAA,IACzB;AAAA,IACA,EAAE,MAAM,iBAAiB,SAAS,iBAAiB,WAAW,oBAAC,iBAAc,EAAG;AAAA,EAClF;AACF;AAEO,MAAM,OAAO,CAAC;AAAA,EACnB,gBAAgB;AAAA,EAChB,cAAc;AAChB,IAAe,CAAC,MAAM;AACpB,QAAM,cAAc,SAAS,eAAe,MAAM;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,QAAM,cAAc,IAAI,YAAY;AACpC,QAAM,OAAO,WAAW,WAAW;AACnC,OAAK;AAAA,IACH,oBAAC,cACC,8BAAC,8BAA2B,aAC1B,+BAAC,eAAY,YAAY,eACvB;AAAA,0BAAC,eAAY;AAAA,MACb,oBAAC,iBAAc,aAA0B;AAAA,OAC3C,GACF,GACF;AAAA,EACF;AACF;","names":["context"]}
@@ -0,0 +1,51 @@
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 diagnostics_exports = {};
20
+ __export(diagnostics_exports, {
21
+ getDiagnostics: () => getDiagnostics,
22
+ onDiagnosticsChange: () => onDiagnosticsChange,
23
+ useDiagnostics: () => useDiagnostics
24
+ });
25
+ module.exports = __toCommonJS(diagnostics_exports);
26
+ var import_pushChannel = require("./pushChannel");
27
+ const EMPTY = { buildErrors: [], consoleEntries: [], provenance: null };
28
+ const channel = (0, import_pushChannel.createPushChannel)({
29
+ pushType: "diagnostics",
30
+ requestType: "request-diagnostics",
31
+ initial: EMPTY,
32
+ parse: (msg) => {
33
+ if (!Array.isArray(msg.buildErrors) || !Array.isArray(msg.consoleEntries)) return void 0;
34
+ const provenance = msg.provenance && typeof msg.provenance === "object" ? msg.provenance : null;
35
+ return {
36
+ buildErrors: msg.buildErrors,
37
+ consoleEntries: msg.consoleEntries,
38
+ provenance
39
+ };
40
+ }
41
+ });
42
+ const getDiagnostics = () => channel.get();
43
+ const onDiagnosticsChange = (listener) => channel.onChange(listener);
44
+ const useDiagnostics = () => channel.use();
45
+ // Annotate the CommonJS export names for ESM import in node:
46
+ 0 && (module.exports = {
47
+ getDiagnostics,
48
+ onDiagnosticsChange,
49
+ useDiagnostics
50
+ });
51
+ //# sourceMappingURL=diagnostics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/diagnostics.ts"],"sourcesContent":["// The `diagnostics:read` channel — app-facing surface (LLM_AND_AGENTS_SPEC §3.3/§4,\n// D4; roadmap R3-74 / P3-72).\n//\n// A *sibling* agent app (one holding `diagnostics:read`) observes the result of its\n// edits to the PREVIEWED app: the build/transpile errors from the sandbox bundler\n// and the previewed app's captured `console.*`. This is the in-browser analogue of\n// a local coding agent reading compiler/test output (P3-73's `get_diagnostics()`\n// tool). Read-only and scoped host-side to the paired previewed app's OWN\n// diagnostics — never another app's (the host channel projection enforces it; the\n// previewed app itself holds no `diagnostics:read`, so it is origin-excluded).\n//\n// Recipe-A push channel, identical get/onChange/use trio as `secrets`/`catalog`:\n// the host pushes `diagnostics` on change and answers a `request-diagnostics` poll,\n// gated per-frame by the read ACL. Inert until the host wires the channel\n// (site-main `channelBridge`); the contract ships here so the agent app (P3-73) can\n// be written against it.\nimport { createPushChannel } from './pushChannel';\n\n/** One build/transpile error from the sandbox bundler's compile of the previewed\n * app. `path` is repo-relative (leading slash) when the error is file-located. */\nexport interface BuildError {\n message: string;\n path?: string;\n line?: number;\n column?: number;\n}\n\nexport type ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';\n\n/** One captured `console.*` entry from the previewed app. The host renders the\n * console arguments to text host-side — the agent never receives live object\n * handles across the boundary. */\nexport interface ConsoleEntry {\n level: ConsoleLevel;\n text: string;\n /** Host-side timestamp (ms) at capture. */\n at: number;\n}\n\n/** Provenance (D4 / EDITOR_AS_APP_SPEC §12.3): WHICH previewed app + compile this\n * snapshot describes, so a consumer can tell stale output from fresh and never\n * conflates two apps' diagnostics. `null` until a first compile is observed. */\nexport interface DiagnosticsProvenance {\n /** The previewed app's stable key (`provider/ns/repo`). */\n appKey?: string;\n /** Monotonic id of the compile that produced these build errors. */\n compileId?: string;\n}\n\nexport interface Diagnostics {\n buildErrors: BuildError[];\n consoleEntries: ConsoleEntry[];\n provenance: DiagnosticsProvenance | null;\n}\n\n/** Value before the host answers — also the value when the app may not read the\n * channel (no `diagnostics:read`): an empty, provenance-less snapshot. */\nconst EMPTY: Diagnostics = { buildErrors: [], consoleEntries: [], provenance: null };\n\nconst channel = createPushChannel<Diagnostics>({\n pushType: 'diagnostics',\n requestType: 'request-diagnostics',\n initial: EMPTY,\n parse: (msg) => {\n // Require both arrays; tolerate an absent/partial provenance. A malformed push\n // is ignored (returns undefined) so the last good snapshot stands.\n if (!Array.isArray(msg.buildErrors) || !Array.isArray(msg.consoleEntries)) return undefined;\n const provenance =\n msg.provenance && typeof msg.provenance === 'object'\n ? (msg.provenance as DiagnosticsProvenance)\n : null;\n return {\n buildErrors: msg.buildErrors as BuildError[],\n consoleEntries: msg.consoleEntries as ConsoleEntry[],\n provenance,\n };\n },\n});\n\n/** One-off read of the previewed app's current diagnostics. Returns the empty\n * snapshot until the host answers (or if the app lacks `diagnostics:read`). Use\n * {@link onDiagnosticsChange}/{@link useDiagnostics} to react to live updates. */\nexport const getDiagnostics = (): Diagnostics => channel.get();\n\n/** Subscribe to diagnostics. Invoked immediately with the current value, then on\n * every host push. Returns an unsubscribe. */\nexport const onDiagnosticsChange = (listener: (d: Diagnostics) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook: the current diagnostics, re-rendering on every change. */\nexport const useDiagnostics = (): Diagnostics => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,yBAAkC;AAyClC,MAAM,QAAqB,EAAE,aAAa,CAAC,GAAG,gBAAgB,CAAC,GAAG,YAAY,KAAK;AAEnF,MAAM,cAAU,sCAA+B;AAAA,EAC7C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AAGd,QAAI,CAAC,MAAM,QAAQ,IAAI,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,cAAc,EAAG,QAAO;AAClF,UAAM,aACJ,IAAI,cAAc,OAAO,IAAI,eAAe,WACvC,IAAI,aACL;AACN,WAAO;AAAA,MACL,aAAa,IAAI;AAAA,MACjB,gBAAgB,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAKM,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;AAItD,MAAM,sBAAsB,CAAC,aAClC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;","names":[]}
@@ -0,0 +1,43 @@
1
+ /** One build/transpile error from the sandbox bundler's compile of the previewed
2
+ * app. `path` is repo-relative (leading slash) when the error is file-located. */
3
+ interface BuildError {
4
+ message: string;
5
+ path?: string;
6
+ line?: number;
7
+ column?: number;
8
+ }
9
+ type ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
10
+ /** One captured `console.*` entry from the previewed app. The host renders the
11
+ * console arguments to text host-side — the agent never receives live object
12
+ * handles across the boundary. */
13
+ interface ConsoleEntry {
14
+ level: ConsoleLevel;
15
+ text: string;
16
+ /** Host-side timestamp (ms) at capture. */
17
+ at: number;
18
+ }
19
+ /** Provenance (D4 / EDITOR_AS_APP_SPEC §12.3): WHICH previewed app + compile this
20
+ * snapshot describes, so a consumer can tell stale output from fresh and never
21
+ * conflates two apps' diagnostics. `null` until a first compile is observed. */
22
+ interface DiagnosticsProvenance {
23
+ /** The previewed app's stable key (`provider/ns/repo`). */
24
+ appKey?: string;
25
+ /** Monotonic id of the compile that produced these build errors. */
26
+ compileId?: string;
27
+ }
28
+ interface Diagnostics {
29
+ buildErrors: BuildError[];
30
+ consoleEntries: ConsoleEntry[];
31
+ provenance: DiagnosticsProvenance | null;
32
+ }
33
+ /** One-off read of the previewed app's current diagnostics. Returns the empty
34
+ * snapshot until the host answers (or if the app lacks `diagnostics:read`). Use
35
+ * {@link onDiagnosticsChange}/{@link useDiagnostics} to react to live updates. */
36
+ declare const getDiagnostics: () => Diagnostics;
37
+ /** Subscribe to diagnostics. Invoked immediately with the current value, then on
38
+ * every host push. Returns an unsubscribe. */
39
+ declare const onDiagnosticsChange: (listener: (d: Diagnostics) => void) => (() => void);
40
+ /** React hook: the current diagnostics, re-rendering on every change. */
41
+ declare const useDiagnostics: () => Diagnostics;
42
+
43
+ export { type BuildError, type ConsoleEntry, type ConsoleLevel, type Diagnostics, type DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics };
@@ -0,0 +1,43 @@
1
+ /** One build/transpile error from the sandbox bundler's compile of the previewed
2
+ * app. `path` is repo-relative (leading slash) when the error is file-located. */
3
+ interface BuildError {
4
+ message: string;
5
+ path?: string;
6
+ line?: number;
7
+ column?: number;
8
+ }
9
+ type ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
10
+ /** One captured `console.*` entry from the previewed app. The host renders the
11
+ * console arguments to text host-side — the agent never receives live object
12
+ * handles across the boundary. */
13
+ interface ConsoleEntry {
14
+ level: ConsoleLevel;
15
+ text: string;
16
+ /** Host-side timestamp (ms) at capture. */
17
+ at: number;
18
+ }
19
+ /** Provenance (D4 / EDITOR_AS_APP_SPEC §12.3): WHICH previewed app + compile this
20
+ * snapshot describes, so a consumer can tell stale output from fresh and never
21
+ * conflates two apps' diagnostics. `null` until a first compile is observed. */
22
+ interface DiagnosticsProvenance {
23
+ /** The previewed app's stable key (`provider/ns/repo`). */
24
+ appKey?: string;
25
+ /** Monotonic id of the compile that produced these build errors. */
26
+ compileId?: string;
27
+ }
28
+ interface Diagnostics {
29
+ buildErrors: BuildError[];
30
+ consoleEntries: ConsoleEntry[];
31
+ provenance: DiagnosticsProvenance | null;
32
+ }
33
+ /** One-off read of the previewed app's current diagnostics. Returns the empty
34
+ * snapshot until the host answers (or if the app lacks `diagnostics:read`). Use
35
+ * {@link onDiagnosticsChange}/{@link useDiagnostics} to react to live updates. */
36
+ declare const getDiagnostics: () => Diagnostics;
37
+ /** Subscribe to diagnostics. Invoked immediately with the current value, then on
38
+ * every host push. Returns an unsubscribe. */
39
+ declare const onDiagnosticsChange: (listener: (d: Diagnostics) => void) => (() => void);
40
+ /** React hook: the current diagnostics, re-rendering on every change. */
41
+ declare const useDiagnostics: () => Diagnostics;
42
+
43
+ export { type BuildError, type ConsoleEntry, type ConsoleLevel, type Diagnostics, type DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics };
@@ -0,0 +1,25 @@
1
+ import { createPushChannel } from "./pushChannel";
2
+ const EMPTY = { buildErrors: [], consoleEntries: [], provenance: null };
3
+ const channel = createPushChannel({
4
+ pushType: "diagnostics",
5
+ requestType: "request-diagnostics",
6
+ initial: EMPTY,
7
+ parse: (msg) => {
8
+ if (!Array.isArray(msg.buildErrors) || !Array.isArray(msg.consoleEntries)) return void 0;
9
+ const provenance = msg.provenance && typeof msg.provenance === "object" ? msg.provenance : null;
10
+ return {
11
+ buildErrors: msg.buildErrors,
12
+ consoleEntries: msg.consoleEntries,
13
+ provenance
14
+ };
15
+ }
16
+ });
17
+ const getDiagnostics = () => channel.get();
18
+ const onDiagnosticsChange = (listener) => channel.onChange(listener);
19
+ const useDiagnostics = () => channel.use();
20
+ export {
21
+ getDiagnostics,
22
+ onDiagnosticsChange,
23
+ useDiagnostics
24
+ };
25
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/diagnostics.ts"],"sourcesContent":["// The `diagnostics:read` channel — app-facing surface (LLM_AND_AGENTS_SPEC §3.3/§4,\n// D4; roadmap R3-74 / P3-72).\n//\n// A *sibling* agent app (one holding `diagnostics:read`) observes the result of its\n// edits to the PREVIEWED app: the build/transpile errors from the sandbox bundler\n// and the previewed app's captured `console.*`. This is the in-browser analogue of\n// a local coding agent reading compiler/test output (P3-73's `get_diagnostics()`\n// tool). Read-only and scoped host-side to the paired previewed app's OWN\n// diagnostics — never another app's (the host channel projection enforces it; the\n// previewed app itself holds no `diagnostics:read`, so it is origin-excluded).\n//\n// Recipe-A push channel, identical get/onChange/use trio as `secrets`/`catalog`:\n// the host pushes `diagnostics` on change and answers a `request-diagnostics` poll,\n// gated per-frame by the read ACL. Inert until the host wires the channel\n// (site-main `channelBridge`); the contract ships here so the agent app (P3-73) can\n// be written against it.\nimport { createPushChannel } from './pushChannel';\n\n/** One build/transpile error from the sandbox bundler's compile of the previewed\n * app. `path` is repo-relative (leading slash) when the error is file-located. */\nexport interface BuildError {\n message: string;\n path?: string;\n line?: number;\n column?: number;\n}\n\nexport type ConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';\n\n/** One captured `console.*` entry from the previewed app. The host renders the\n * console arguments to text host-side — the agent never receives live object\n * handles across the boundary. */\nexport interface ConsoleEntry {\n level: ConsoleLevel;\n text: string;\n /** Host-side timestamp (ms) at capture. */\n at: number;\n}\n\n/** Provenance (D4 / EDITOR_AS_APP_SPEC §12.3): WHICH previewed app + compile this\n * snapshot describes, so a consumer can tell stale output from fresh and never\n * conflates two apps' diagnostics. `null` until a first compile is observed. */\nexport interface DiagnosticsProvenance {\n /** The previewed app's stable key (`provider/ns/repo`). */\n appKey?: string;\n /** Monotonic id of the compile that produced these build errors. */\n compileId?: string;\n}\n\nexport interface Diagnostics {\n buildErrors: BuildError[];\n consoleEntries: ConsoleEntry[];\n provenance: DiagnosticsProvenance | null;\n}\n\n/** Value before the host answers — also the value when the app may not read the\n * channel (no `diagnostics:read`): an empty, provenance-less snapshot. */\nconst EMPTY: Diagnostics = { buildErrors: [], consoleEntries: [], provenance: null };\n\nconst channel = createPushChannel<Diagnostics>({\n pushType: 'diagnostics',\n requestType: 'request-diagnostics',\n initial: EMPTY,\n parse: (msg) => {\n // Require both arrays; tolerate an absent/partial provenance. A malformed push\n // is ignored (returns undefined) so the last good snapshot stands.\n if (!Array.isArray(msg.buildErrors) || !Array.isArray(msg.consoleEntries)) return undefined;\n const provenance =\n msg.provenance && typeof msg.provenance === 'object'\n ? (msg.provenance as DiagnosticsProvenance)\n : null;\n return {\n buildErrors: msg.buildErrors as BuildError[],\n consoleEntries: msg.consoleEntries as ConsoleEntry[],\n provenance,\n };\n },\n});\n\n/** One-off read of the previewed app's current diagnostics. Returns the empty\n * snapshot until the host answers (or if the app lacks `diagnostics:read`). Use\n * {@link onDiagnosticsChange}/{@link useDiagnostics} to react to live updates. */\nexport const getDiagnostics = (): Diagnostics => channel.get();\n\n/** Subscribe to diagnostics. Invoked immediately with the current value, then on\n * every host push. Returns an unsubscribe. */\nexport const onDiagnosticsChange = (listener: (d: Diagnostics) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook: the current diagnostics, re-rendering on every change. */\nexport const useDiagnostics = (): Diagnostics => channel.use();\n"],"mappings":"AAgBA,SAAS,yBAAyB;AAyClC,MAAM,QAAqB,EAAE,aAAa,CAAC,GAAG,gBAAgB,CAAC,GAAG,YAAY,KAAK;AAEnF,MAAM,UAAU,kBAA+B;AAAA,EAC7C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAQ;AAGd,QAAI,CAAC,MAAM,QAAQ,IAAI,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,cAAc,EAAG,QAAO;AAClF,UAAM,aACJ,IAAI,cAAc,OAAO,IAAI,eAAe,WACvC,IAAI,aACL;AACN,WAAO;AAAA,MACL,aAAa,IAAI;AAAA,MACjB,gBAAgB,IAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAKM,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;AAItD,MAAM,sBAAsB,CAAC,aAClC,QAAQ,SAAS,QAAQ;AAGpB,MAAM,iBAAiB,MAAmB,QAAQ,IAAI;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hostRuntime.ts"],"sourcesContent":["// Runtime-discovery global (SDK_PACKAGING_SPEC §4) — a LEAF module that imports\n// nothing, so it can be read by both `sandboxUtils` (transport resolver) and\n// `runtime` (handshake) without forming an import cycle. `sandboxUtils → runtime`\n// + `runtime → sandboxUtils` was a cycle the sandbox bundler cannot evaluate\n// (infinite re-require → \"Maximum call stack size exceeded\"); routing the shared\n// `getHostRuntime` through this leaf breaks it.\n\n/** The sandbox runtime's pre-evaluation discovery global (§4). */\nexport interface ImmediatelyRunGlobal {\n /** Sandbox-runtime protocol version (semver). */\n runtimeVersion?: string;\n /** postMessage envelope/protocol version. */\n protocolVersion?: string;\n /** The host channel the SDK talks over (MessagePort | message bus). */\n transport?: unknown;\n /** Resolves when ports arrive, if they arrive async after register-frame. */\n ready?: Promise<void>;\n /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);\n * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */\n appMountPath?: string;\n}\n\n/**\n * Read the sandbox runtime's discovery global (§4), or null when absent — in which\n * case the SDK uses the current INJECTED path (`module.evaluation.*`). Lets the SDK\n * detect a host too old/new and fail closed (§6) once the global ships.\n */\nexport function getHostRuntime(): ImmediatelyRunGlobal | null {\n try {\n return (globalThis as { __immediatelyRun__?: ImmediatelyRunGlobal }).__immediatelyRun__ ?? null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BO,SAAS,iBAA8C;AAC5D,MAAI;AACF,WAAQ,WAA6D,sBAAsB;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/hostRuntime.ts"],"sourcesContent":["// Runtime-discovery global (SDK_PACKAGING_SPEC §4) — a LEAF module that imports\n// nothing, so it can be read by both `sandboxUtils` (transport resolver) and\n// `runtime` (handshake) without forming an import cycle. `sandboxUtils → runtime`\n// + `runtime → sandboxUtils` was a cycle the sandbox bundler cannot evaluate\n// (infinite re-require → \"Maximum call stack size exceeded\"); routing the shared\n// `getHostRuntime` through this leaf breaks it.\n\n/** The sandbox runtime's pre-evaluation discovery global (§4). */\nexport interface ImmediatelyRunGlobal {\n /** Sandbox-runtime protocol version (semver). */\n runtimeVersion?: string;\n /** postMessage envelope/protocol version. */\n protocolVersion?: string;\n /** The host channel the SDK talks over (MessagePort | message bus). */\n transport?: unknown;\n /** Resolves when ports arrive, if they arrive async after register-frame. */\n ready?: Promise<void>;\n /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);\n * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */\n appMountPath?: string;\n /** The chrome region this app instance occupies, e.g. `\"panel.agent\"` or\n * `\"stage.conversation\"` (UI_AS_APPS_SPEC §4.1); surfaced via `getRegion()`.\n * Absent for a standalone app, in local dev, or on a host that doesn't report\n * it. Descriptive only — it grants nothing. */\n region?: string;\n}\n\n/**\n * Read the sandbox runtime's discovery global (§4), or null when absent — in which\n * case the SDK uses the current INJECTED path (`module.evaluation.*`). Lets the SDK\n * detect a host too old/new and fail closed (§6) once the global ships.\n */\nexport function getHostRuntime(): ImmediatelyRunGlobal | null {\n try {\n return (globalThis as { __immediatelyRun__?: ImmediatelyRunGlobal }).__immediatelyRun__ ?? null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCO,SAAS,iBAA8C;AAC5D,MAAI;AACF,WAAQ,WAA6D,sBAAsB;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -11,6 +11,11 @@ interface ImmediatelyRunGlobal {
11
11
  /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);
12
12
  * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */
13
13
  appMountPath?: string;
14
+ /** The chrome region this app instance occupies, e.g. `"panel.agent"` or
15
+ * `"stage.conversation"` (UI_AS_APPS_SPEC §4.1); surfaced via `getRegion()`.
16
+ * Absent for a standalone app, in local dev, or on a host that doesn't report
17
+ * it. Descriptive only — it grants nothing. */
18
+ region?: string;
14
19
  }
15
20
  /**
16
21
  * Read the sandbox runtime's discovery global (§4), or null when absent — in which
@@ -11,6 +11,11 @@ interface ImmediatelyRunGlobal {
11
11
  /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);
12
12
  * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */
13
13
  appMountPath?: string;
14
+ /** The chrome region this app instance occupies, e.g. `"panel.agent"` or
15
+ * `"stage.conversation"` (UI_AS_APPS_SPEC §4.1); surfaced via `getRegion()`.
16
+ * Absent for a standalone app, in local dev, or on a host that doesn't report
17
+ * it. Descriptive only — it grants nothing. */
18
+ region?: string;
14
19
  }
15
20
  /**
16
21
  * Read the sandbox runtime's discovery global (§4), or null when absent — in which
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hostRuntime.ts"],"sourcesContent":["// Runtime-discovery global (SDK_PACKAGING_SPEC §4) — a LEAF module that imports\n// nothing, so it can be read by both `sandboxUtils` (transport resolver) and\n// `runtime` (handshake) without forming an import cycle. `sandboxUtils → runtime`\n// + `runtime → sandboxUtils` was a cycle the sandbox bundler cannot evaluate\n// (infinite re-require → \"Maximum call stack size exceeded\"); routing the shared\n// `getHostRuntime` through this leaf breaks it.\n\n/** The sandbox runtime's pre-evaluation discovery global (§4). */\nexport interface ImmediatelyRunGlobal {\n /** Sandbox-runtime protocol version (semver). */\n runtimeVersion?: string;\n /** postMessage envelope/protocol version. */\n protocolVersion?: string;\n /** The host channel the SDK talks over (MessagePort | message bus). */\n transport?: unknown;\n /** Resolves when ports arrive, if they arrive async after register-frame. */\n ready?: Promise<void>;\n /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);\n * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */\n appMountPath?: string;\n}\n\n/**\n * Read the sandbox runtime's discovery global (§4), or null when absent — in which\n * case the SDK uses the current INJECTED path (`module.evaluation.*`). Lets the SDK\n * detect a host too old/new and fail closed (§6) once the global ships.\n */\nexport function getHostRuntime(): ImmediatelyRunGlobal | null {\n try {\n return (globalThis as { __immediatelyRun__?: ImmediatelyRunGlobal }).__immediatelyRun__ ?? null;\n } catch {\n return null;\n }\n}\n"],"mappings":"AA2BO,SAAS,iBAA8C;AAC5D,MAAI;AACF,WAAQ,WAA6D,sBAAsB;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/hostRuntime.ts"],"sourcesContent":["// Runtime-discovery global (SDK_PACKAGING_SPEC §4) — a LEAF module that imports\n// nothing, so it can be read by both `sandboxUtils` (transport resolver) and\n// `runtime` (handshake) without forming an import cycle. `sandboxUtils → runtime`\n// + `runtime → sandboxUtils` was a cycle the sandbox bundler cannot evaluate\n// (infinite re-require → \"Maximum call stack size exceeded\"); routing the shared\n// `getHostRuntime` through this leaf breaks it.\n\n/** The sandbox runtime's pre-evaluation discovery global (§4). */\nexport interface ImmediatelyRunGlobal {\n /** Sandbox-runtime protocol version (semver). */\n runtimeVersion?: string;\n /** postMessage envelope/protocol version. */\n protocolVersion?: string;\n /** The host channel the SDK talks over (MessagePort | message bus). */\n transport?: unknown;\n /** Resolves when ports arrive, if they arrive async after register-frame. */\n ready?: Promise<void>;\n /** Canonical `/mnt/{hash}` path of the app's own repo mount (FILE_SHARING §11.2);\n * surfaced to apps via `getAppMountPath()`. Absent until the host reports it. */\n appMountPath?: string;\n /** The chrome region this app instance occupies, e.g. `\"panel.agent\"` or\n * `\"stage.conversation\"` (UI_AS_APPS_SPEC §4.1); surfaced via `getRegion()`.\n * Absent for a standalone app, in local dev, or on a host that doesn't report\n * it. Descriptive only — it grants nothing. */\n region?: string;\n}\n\n/**\n * Read the sandbox runtime's discovery global (§4), or null when absent — in which\n * case the SDK uses the current INJECTED path (`module.evaluation.*`). Lets the SDK\n * detect a host too old/new and fail closed (§6) once the global ships.\n */\nexport function getHostRuntime(): ImmediatelyRunGlobal | null {\n try {\n return (globalThis as { __immediatelyRun__?: ImmediatelyRunGlobal }).__immediatelyRun__ ?? null;\n } catch {\n return null;\n }\n}\n"],"mappings":"AAgCO,SAAS,iBAA8C;AAC5D,MAAI;AACF,WAAQ,WAA6D,sBAAsB;AAAA,EAC7F,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ __reExport(index_exports, require("./theme"), module.exports);
26
26
  __reExport(index_exports, require("./editorContext"), module.exports);
27
27
  __reExport(index_exports, require("./editor"), module.exports);
28
28
  __reExport(index_exports, require("./formFactor"), module.exports);
29
+ __reExport(index_exports, require("./region"), module.exports);
29
30
  __reExport(index_exports, require("./mounts"), module.exports);
30
31
  __reExport(index_exports, require("./contribute"), module.exports);
31
32
  __reExport(index_exports, require("./catalog"), module.exports);
@@ -33,6 +34,7 @@ __reExport(index_exports, require("./ipc"), module.exports);
33
34
  __reExport(index_exports, require("./dnd"), module.exports);
34
35
  __reExport(index_exports, require("./netFetch"), module.exports);
35
36
  __reExport(index_exports, require("./secrets"), module.exports);
37
+ __reExport(index_exports, require("./diagnostics"), module.exports);
36
38
  __reExport(index_exports, require("./tasks"), module.exports);
37
39
  __reExport(index_exports, require("./runtime"), module.exports);
38
40
  __reExport(index_exports, require("./irMarkers"), module.exports);
@@ -52,6 +54,7 @@ __reExport(index_exports, require("./sandboxTypes"), module.exports);
52
54
  ...require("./editorContext"),
53
55
  ...require("./editor"),
54
56
  ...require("./formFactor"),
57
+ ...require("./region"),
55
58
  ...require("./mounts"),
56
59
  ...require("./contribute"),
57
60
  ...require("./catalog"),
@@ -59,6 +62,7 @@ __reExport(index_exports, require("./sandboxTypes"), module.exports);
59
62
  ...require("./dnd"),
60
63
  ...require("./netFetch"),
61
64
  ...require("./secrets"),
65
+ ...require("./diagnostics"),
62
66
  ...require("./tasks"),
63
67
  ...require("./runtime"),
64
68
  ...require("./irMarkers"),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAIA,0BAAc,uCAJd;AAKA,0BAAc,oBALd;AAMA,0BAAc,mBANd;AAOA,0BAAc,oBAPd;AAQA,0BAAc,4BARd;AASA,0BAAc,qBATd;AAUA,0BAAc,yBAVd;AAWA,0BAAc,qBAXd;AAYA,0BAAc,yBAZd;AAaA,0BAAc,sBAbd;AAcA,0BAAc,kBAdd;AAeA,0BAAc,kBAfd;AAgBA,0BAAc,uBAhBd;AAiBA,0BAAc,sBAjBd;AAkBA,0BAAc,oBAlBd;AAmBA,0BAAc,sBAnBd;AAoBA,0BAAc,wBApBd;AAqBA,0BAAc,oBArBd;AAsBA,0BAAc,6BAtBd;AAuBA,0BAAc,2BAvBd;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './diagnostics';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAIA,0BAAc,uCAJd;AAKA,0BAAc,oBALd;AAMA,0BAAc,mBANd;AAOA,0BAAc,oBAPd;AAQA,0BAAc,4BARd;AASA,0BAAc,qBATd;AAUA,0BAAc,yBAVd;AAWA,0BAAc,qBAXd;AAYA,0BAAc,qBAZd;AAaA,0BAAc,yBAbd;AAcA,0BAAc,sBAdd;AAeA,0BAAc,kBAfd;AAgBA,0BAAc,kBAhBd;AAiBA,0BAAc,uBAjBd;AAkBA,0BAAc,sBAlBd;AAmBA,0BAAc,0BAnBd;AAoBA,0BAAc,oBApBd;AAqBA,0BAAc,sBArBd;AAsBA,0BAAc,wBAtBd;AAuBA,0BAAc,oBAvBd;AAwBA,0BAAc,6BAxBd;AAyBA,0BAAc,2BAzBd;","names":[]}
package/dist/index.d.cts CHANGED
@@ -9,6 +9,7 @@ export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme
9
9
  export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.cjs';
10
10
  export { EditTarget, EditorOpenError, EditorSessionError, EditorWriteError, RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile } from './editor.cjs';
11
11
  export { FormFactor, FormFactorClass, Orientation, getFormFactor, onFormFactorChange, useFormFactor } from './formFactor.cjs';
12
+ export { getRegion, useRegion } from './region.cjs';
12
13
  export { GrantRecord, Member, MountQuery, MountRemoveReason, MountRule, RemovedMount, ResolvedUser, Role, SandboxMount, SpaceError, SpaceInfo, createSpace, findMount, getAppMountPath, getMounts, getSpaceMembers, importSettingsFromParent, listAllSpaces, listGrants, listSettingsApps, listSpaces, lookupUser, makeContentRef, mount, mountSpace, onMountsChange, openSettings, openSettingsOf, requestMount, requestSpace, resolveContentRef, resolveContentRefs, revokeGrant, setSpaceRole, shareSpace, unmountSpace, unshareSpace, useMounts, waitForMount } from './mounts.cjs';
13
14
  export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.cjs';
14
15
  export { ApiMethod, getCatalog, invoke, invokeStream, onCatalogChange, useCatalog } from './catalog.cjs';
@@ -16,6 +17,7 @@ export { RegionMessage, onRegionMessage, postToRegion, useRegionMessage } from '
16
17
  export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop, startItemDrag, useDroppedItem } from './dnd.cjs';
17
18
  export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.cjs';
18
19
  export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.cjs';
20
+ export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.cjs';
19
21
  export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.cjs';
20
22
  export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.cjs';
21
23
  export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.cjs';
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme
9
9
  export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.js';
10
10
  export { EditTarget, EditorOpenError, EditorSessionError, EditorWriteError, RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile } from './editor.js';
11
11
  export { FormFactor, FormFactorClass, Orientation, getFormFactor, onFormFactorChange, useFormFactor } from './formFactor.js';
12
+ export { getRegion, useRegion } from './region.js';
12
13
  export { GrantRecord, Member, MountQuery, MountRemoveReason, MountRule, RemovedMount, ResolvedUser, Role, SandboxMount, SpaceError, SpaceInfo, createSpace, findMount, getAppMountPath, getMounts, getSpaceMembers, importSettingsFromParent, listAllSpaces, listGrants, listSettingsApps, listSpaces, lookupUser, makeContentRef, mount, mountSpace, onMountsChange, openSettings, openSettingsOf, requestMount, requestSpace, resolveContentRef, resolveContentRefs, revokeGrant, setSpaceRole, shareSpace, unmountSpace, unshareSpace, useMounts, waitForMount } from './mounts.js';
13
14
  export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.js';
14
15
  export { ApiMethod, getCatalog, invoke, invokeStream, onCatalogChange, useCatalog } from './catalog.js';
@@ -16,6 +17,7 @@ export { RegionMessage, onRegionMessage, postToRegion, useRegionMessage } from '
16
17
  export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop, startItemDrag, useDroppedItem } from './dnd.js';
17
18
  export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.js';
18
19
  export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.js';
20
+ export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.js';
19
21
  export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.js';
20
22
  export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.js';
21
23
  export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.js';
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./theme";
9
9
  export * from "./editorContext";
10
10
  export * from "./editor";
11
11
  export * from "./formFactor";
12
+ export * from "./region";
12
13
  export * from "./mounts";
13
14
  export * from "./contribute";
14
15
  export * from "./catalog";
@@ -16,6 +17,7 @@ export * from "./ipc";
16
17
  export * from "./dnd";
17
18
  export * from "./netFetch";
18
19
  export * from "./secrets";
20
+ export * from "./diagnostics";
19
21
  export * from "./tasks";
20
22
  export * from "./runtime";
21
23
  export * from "./irMarkers";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './diagnostics';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
@@ -0,0 +1,59 @@
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 markers_exports = {};
20
+ __export(markers_exports, {
21
+ __resetMarkers: () => __resetMarkers,
22
+ __setMarkerDeps: () => __setMarkerDeps,
23
+ emitMarker: () => emitMarker,
24
+ emitMarkerOnce: () => emitMarkerOnce
25
+ });
26
+ module.exports = __toCommonJS(markers_exports);
27
+ var import_sandboxUtils = require("./sandboxUtils");
28
+ var import_irMarkers = require("./irMarkers");
29
+ const realNow = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
30
+ const defaultDeps = { send: import_sandboxUtils.sendMessage, now: realNow };
31
+ let deps = defaultDeps;
32
+ const emitted = /* @__PURE__ */ new Set();
33
+ function emitMarker(name, attrs) {
34
+ if (!(0, import_irMarkers.isIrMarkerName)(name)) return;
35
+ try {
36
+ deps.send("ir-marker", { name, at: deps.now(), ...attrs !== void 0 ? { attrs } : {} });
37
+ } catch {
38
+ }
39
+ }
40
+ function emitMarkerOnce(name, attrs) {
41
+ if (emitted.has(name)) return;
42
+ emitted.add(name);
43
+ emitMarker(name, attrs);
44
+ }
45
+ function __setMarkerDeps(d) {
46
+ deps = { ...defaultDeps, ...d };
47
+ }
48
+ function __resetMarkers() {
49
+ deps = defaultDeps;
50
+ emitted.clear();
51
+ }
52
+ // Annotate the CommonJS export names for ESM import in node:
53
+ 0 && (module.exports = {
54
+ __resetMarkers,
55
+ __setMarkerDeps,
56
+ emitMarker,
57
+ emitMarkerOnce
58
+ });
59
+ //# sourceMappingURL=markers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markers.ts"],"sourcesContent":["// Runtime emission of the boot `ir.*` markers (LOAD_PROFILING_SPEC §3/§3.2, R3-46).\n//\n// The host already MERGED the receive end: it validates a forwarded\n// `{ type:'ir-marker', name, at, attrs? }` against the LP-5 allowlist\n// (`irMarkers.ts`) and brackets request→interactive, treating an accepted\n// `ir.interactive` as the root-render-commit signal (site-main\n// `routePerfMessage`/`perfTimeline`). This module is the SANDBOX-side emitter for\n// the boot marks the SDK owns — `ir.fmp` (first meaningful paint) and\n// `ir.interactive` (root render commit) — forwarded over the same transport as\n// `reportReady()` (`ready.ts`). The per-marker timestamp is the sandbox-relative\n// `performance.now()` at emission (§3.2).\n//\n// Mirrors `ready.ts`'s dependency seam so the emission is unit-testable without a\n// host transport, and is idempotent per name so React StrictMode's double-invoke\n// (or any re-commit) can't mint a boot mark twice.\nimport { sendMessage as defaultSend } from './sandboxUtils';\nimport { isIrMarkerName, type IrMarkerName } from './irMarkers';\n\ninterface MarkerDeps {\n send: (type: string, data?: Record<string, unknown>) => void;\n now: () => number;\n}\n\nconst realNow = (): number =>\n typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n\nconst defaultDeps: MarkerDeps = { send: defaultSend, now: realNow };\n\nlet deps: MarkerDeps = defaultDeps;\nconst emitted = new Set<string>();\n\n/**\n * Forward one `ir.*` marker to the host (`ir-marker`). The host re-validates against\n * the allowlist, so a bad name/attr is dropped there; we only emit names the SDK\n * itself owns. A transport that isn't ready yet is swallowed — a missing boot mark\n * must never break the app's boot.\n */\nexport function emitMarker(name: IrMarkerName, attrs?: Record<string, unknown>): void {\n if (!isIrMarkerName(name)) return;\n try {\n deps.send('ir-marker', { name, at: deps.now(), ...(attrs !== undefined ? { attrs } : {}) });\n } catch {\n /* transport not ready — boot continues; the host simply lacks this mark */\n }\n}\n\n/** Emit a boot one-shot at most once per name (idempotent across StrictMode/re-commit). */\nexport function emitMarkerOnce(name: IrMarkerName, attrs?: Record<string, unknown>): void {\n if (emitted.has(name)) return;\n emitted.add(name);\n emitMarker(name, attrs);\n}\n\n/** Test seam: override the transport/clock. */\nexport function __setMarkerDeps(d: Partial<MarkerDeps>): void {\n deps = { ...defaultDeps, ...d };\n}\n\n/** Test seam: reset module state (the once-guard + deps) between cases. */\nexport function __resetMarkers(): void {\n deps = defaultDeps;\n emitted.clear();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,0BAA2C;AAC3C,uBAAkD;AAOlD,MAAM,UAAU,MACd,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,aAC7D,YAAY,IAAI,IAChB,KAAK,IAAI;AAEf,MAAM,cAA0B,EAAE,MAAM,oBAAAA,aAAa,KAAK,QAAQ;AAElE,IAAI,OAAmB;AACvB,MAAM,UAAU,oBAAI,IAAY;AAQzB,SAAS,WAAW,MAAoB,OAAuC;AACpF,MAAI,KAAC,iCAAe,IAAI,EAAG;AAC3B,MAAI;AACF,SAAK,KAAK,aAAa,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAAA,EAC5F,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,eAAe,MAAoB,OAAuC;AACxF,MAAI,QAAQ,IAAI,IAAI,EAAG;AACvB,UAAQ,IAAI,IAAI;AAChB,aAAW,MAAM,KAAK;AACxB;AAGO,SAAS,gBAAgB,GAA8B;AAC5D,SAAO,EAAE,GAAG,aAAa,GAAG,EAAE;AAChC;AAGO,SAAS,iBAAuB;AACrC,SAAO;AACP,UAAQ,MAAM;AAChB;","names":["defaultSend"]}
@@ -0,0 +1,21 @@
1
+ import { IrMarkerName } from './irMarkers.cjs';
2
+
3
+ interface MarkerDeps {
4
+ send: (type: string, data?: Record<string, unknown>) => void;
5
+ now: () => number;
6
+ }
7
+ /**
8
+ * Forward one `ir.*` marker to the host (`ir-marker`). The host re-validates against
9
+ * the allowlist, so a bad name/attr is dropped there; we only emit names the SDK
10
+ * itself owns. A transport that isn't ready yet is swallowed — a missing boot mark
11
+ * must never break the app's boot.
12
+ */
13
+ declare function emitMarker(name: IrMarkerName, attrs?: Record<string, unknown>): void;
14
+ /** Emit a boot one-shot at most once per name (idempotent across StrictMode/re-commit). */
15
+ declare function emitMarkerOnce(name: IrMarkerName, attrs?: Record<string, unknown>): void;
16
+ /** Test seam: override the transport/clock. */
17
+ declare function __setMarkerDeps(d: Partial<MarkerDeps>): void;
18
+ /** Test seam: reset module state (the once-guard + deps) between cases. */
19
+ declare function __resetMarkers(): void;
20
+
21
+ export { __resetMarkers, __setMarkerDeps, emitMarker, emitMarkerOnce };
@@ -0,0 +1,21 @@
1
+ import { IrMarkerName } from './irMarkers.js';
2
+
3
+ interface MarkerDeps {
4
+ send: (type: string, data?: Record<string, unknown>) => void;
5
+ now: () => number;
6
+ }
7
+ /**
8
+ * Forward one `ir.*` marker to the host (`ir-marker`). The host re-validates against
9
+ * the allowlist, so a bad name/attr is dropped there; we only emit names the SDK
10
+ * itself owns. A transport that isn't ready yet is swallowed — a missing boot mark
11
+ * must never break the app's boot.
12
+ */
13
+ declare function emitMarker(name: IrMarkerName, attrs?: Record<string, unknown>): void;
14
+ /** Emit a boot one-shot at most once per name (idempotent across StrictMode/re-commit). */
15
+ declare function emitMarkerOnce(name: IrMarkerName, attrs?: Record<string, unknown>): void;
16
+ /** Test seam: override the transport/clock. */
17
+ declare function __setMarkerDeps(d: Partial<MarkerDeps>): void;
18
+ /** Test seam: reset module state (the once-guard + deps) between cases. */
19
+ declare function __resetMarkers(): void;
20
+
21
+ export { __resetMarkers, __setMarkerDeps, emitMarker, emitMarkerOnce };
@@ -0,0 +1,32 @@
1
+ import { sendMessage as defaultSend } from "./sandboxUtils";
2
+ import { isIrMarkerName } from "./irMarkers";
3
+ const realNow = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
4
+ const defaultDeps = { send: defaultSend, now: realNow };
5
+ let deps = defaultDeps;
6
+ const emitted = /* @__PURE__ */ new Set();
7
+ function emitMarker(name, attrs) {
8
+ if (!isIrMarkerName(name)) return;
9
+ try {
10
+ deps.send("ir-marker", { name, at: deps.now(), ...attrs !== void 0 ? { attrs } : {} });
11
+ } catch {
12
+ }
13
+ }
14
+ function emitMarkerOnce(name, attrs) {
15
+ if (emitted.has(name)) return;
16
+ emitted.add(name);
17
+ emitMarker(name, attrs);
18
+ }
19
+ function __setMarkerDeps(d) {
20
+ deps = { ...defaultDeps, ...d };
21
+ }
22
+ function __resetMarkers() {
23
+ deps = defaultDeps;
24
+ emitted.clear();
25
+ }
26
+ export {
27
+ __resetMarkers,
28
+ __setMarkerDeps,
29
+ emitMarker,
30
+ emitMarkerOnce
31
+ };
32
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/markers.ts"],"sourcesContent":["// Runtime emission of the boot `ir.*` markers (LOAD_PROFILING_SPEC §3/§3.2, R3-46).\n//\n// The host already MERGED the receive end: it validates a forwarded\n// `{ type:'ir-marker', name, at, attrs? }` against the LP-5 allowlist\n// (`irMarkers.ts`) and brackets request→interactive, treating an accepted\n// `ir.interactive` as the root-render-commit signal (site-main\n// `routePerfMessage`/`perfTimeline`). This module is the SANDBOX-side emitter for\n// the boot marks the SDK owns — `ir.fmp` (first meaningful paint) and\n// `ir.interactive` (root render commit) — forwarded over the same transport as\n// `reportReady()` (`ready.ts`). The per-marker timestamp is the sandbox-relative\n// `performance.now()` at emission (§3.2).\n//\n// Mirrors `ready.ts`'s dependency seam so the emission is unit-testable without a\n// host transport, and is idempotent per name so React StrictMode's double-invoke\n// (or any re-commit) can't mint a boot mark twice.\nimport { sendMessage as defaultSend } from './sandboxUtils';\nimport { isIrMarkerName, type IrMarkerName } from './irMarkers';\n\ninterface MarkerDeps {\n send: (type: string, data?: Record<string, unknown>) => void;\n now: () => number;\n}\n\nconst realNow = (): number =>\n typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n\nconst defaultDeps: MarkerDeps = { send: defaultSend, now: realNow };\n\nlet deps: MarkerDeps = defaultDeps;\nconst emitted = new Set<string>();\n\n/**\n * Forward one `ir.*` marker to the host (`ir-marker`). The host re-validates against\n * the allowlist, so a bad name/attr is dropped there; we only emit names the SDK\n * itself owns. A transport that isn't ready yet is swallowed — a missing boot mark\n * must never break the app's boot.\n */\nexport function emitMarker(name: IrMarkerName, attrs?: Record<string, unknown>): void {\n if (!isIrMarkerName(name)) return;\n try {\n deps.send('ir-marker', { name, at: deps.now(), ...(attrs !== undefined ? { attrs } : {}) });\n } catch {\n /* transport not ready — boot continues; the host simply lacks this mark */\n }\n}\n\n/** Emit a boot one-shot at most once per name (idempotent across StrictMode/re-commit). */\nexport function emitMarkerOnce(name: IrMarkerName, attrs?: Record<string, unknown>): void {\n if (emitted.has(name)) return;\n emitted.add(name);\n emitMarker(name, attrs);\n}\n\n/** Test seam: override the transport/clock. */\nexport function __setMarkerDeps(d: Partial<MarkerDeps>): void {\n deps = { ...defaultDeps, ...d };\n}\n\n/** Test seam: reset module state (the once-guard + deps) between cases. */\nexport function __resetMarkers(): void {\n deps = defaultDeps;\n emitted.clear();\n}\n"],"mappings":"AAeA,SAAS,eAAe,mBAAmB;AAC3C,SAAS,sBAAyC;AAOlD,MAAM,UAAU,MACd,OAAO,gBAAgB,eAAe,OAAO,YAAY,QAAQ,aAC7D,YAAY,IAAI,IAChB,KAAK,IAAI;AAEf,MAAM,cAA0B,EAAE,MAAM,aAAa,KAAK,QAAQ;AAElE,IAAI,OAAmB;AACvB,MAAM,UAAU,oBAAI,IAAY;AAQzB,SAAS,WAAW,MAAoB,OAAuC;AACpF,MAAI,CAAC,eAAe,IAAI,EAAG;AAC3B,MAAI;AACF,SAAK,KAAK,aAAa,EAAE,MAAM,IAAI,KAAK,IAAI,GAAG,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAAA,EAC5F,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,eAAe,MAAoB,OAAuC;AACxF,MAAI,QAAQ,IAAI,IAAI,EAAG;AACvB,UAAQ,IAAI,IAAI;AAChB,aAAW,MAAM,KAAK;AACxB;AAGO,SAAS,gBAAgB,GAA8B;AAC5D,SAAO,EAAE,GAAG,aAAa,GAAG,EAAE;AAChC;AAGO,SAAS,iBAAuB;AACrC,SAAO;AACP,UAAQ,MAAM;AAChB;","names":[]}
@@ -0,0 +1,47 @@
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 region_exports = {};
20
+ __export(region_exports, {
21
+ getRegion: () => getRegion,
22
+ useRegion: () => useRegion
23
+ });
24
+ module.exports = __toCommonJS(region_exports);
25
+ var import_react = require("react");
26
+ var import_hostRuntime = require("./hostRuntime");
27
+ const getRegion = () => (0, import_hostRuntime.getHostRuntime)()?.region ?? null;
28
+ const useRegion = () => {
29
+ const [region, setRegion] = (0, import_react.useState)(getRegion);
30
+ (0, import_react.useEffect)(() => {
31
+ if (region !== null) return;
32
+ let live = true;
33
+ void (0, import_hostRuntime.getHostRuntime)()?.ready?.then(() => {
34
+ if (live) setRegion(getRegion());
35
+ });
36
+ return () => {
37
+ live = false;
38
+ };
39
+ }, [region]);
40
+ return region;
41
+ };
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ getRegion,
45
+ useRegion
46
+ });
47
+ //# sourceMappingURL=region.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/region.ts"],"sourcesContent":["// Region-awareness (UI_AS_APPS_SPEC §4.1). The host can mount the SAME app in more\n// than one chrome region — e.g. the agents activity puts one app in BOTH the panel\n// slot (`panel.agent`, the conversation list) and the stage slot\n// (`stage.conversation`, the selected conversation). `getRegion()` lets that one app\n// tell the slots apart and render the right view.\n//\n// This is descriptive only: the region id is a non-secret string the host already\n// knows. It grants nothing and gates nothing — it just names where the app is\n// mounted. The host reports it on the §4 discovery global beside `appMountPath`.\n\nimport { useEffect, useState } from 'react';\nimport { getHostRuntime } from './hostRuntime';\n\n/**\n * The chrome region this app instance is mounted in (e.g. `\"panel.agent\"`,\n * `\"stage.conversation\"`), or `null` when unknown — a standalone app, local\n * `vite dev`, or an older host that doesn't report it.\n */\nexport const getRegion = (): string | null => getHostRuntime()?.region ?? null;\n\n/**\n * React hook form of {@link getRegion}. The region is fixed for an app instance's\n * lifetime, but the discovery global can arrive just after first paint, so this\n * re-reads once the host runtime's `ready` promise resolves.\n */\nexport const useRegion = (): string | null => {\n const [region, setRegion] = useState<string | null>(getRegion);\n useEffect(() => {\n if (region !== null) return;\n let live = true;\n void getHostRuntime()?.ready?.then(() => {\n if (live) setRegion(getRegion());\n });\n return () => {\n live = false;\n };\n }, [region]);\n return region;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,mBAAoC;AACpC,yBAA+B;AAOxB,MAAM,YAAY,UAAqB,mCAAe,GAAG,UAAU;AAOnE,MAAM,YAAY,MAAqB;AAC5C,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAwB,SAAS;AAC7D,8BAAU,MAAM;AACd,QAAI,WAAW,KAAM;AACrB,QAAI,OAAO;AACX,aAAK,mCAAe,GAAG,OAAO,KAAK,MAAM;AACvC,UAAI,KAAM,WAAU,UAAU,CAAC;AAAA,IACjC,CAAC;AACD,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AACX,SAAO;AACT;","names":[]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The chrome region this app instance is mounted in (e.g. `"panel.agent"`,
3
+ * `"stage.conversation"`), or `null` when unknown — a standalone app, local
4
+ * `vite dev`, or an older host that doesn't report it.
5
+ */
6
+ declare const getRegion: () => string | null;
7
+ /**
8
+ * React hook form of {@link getRegion}. The region is fixed for an app instance's
9
+ * lifetime, but the discovery global can arrive just after first paint, so this
10
+ * re-reads once the host runtime's `ready` promise resolves.
11
+ */
12
+ declare const useRegion: () => string | null;
13
+
14
+ export { getRegion, useRegion };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The chrome region this app instance is mounted in (e.g. `"panel.agent"`,
3
+ * `"stage.conversation"`), or `null` when unknown — a standalone app, local
4
+ * `vite dev`, or an older host that doesn't report it.
5
+ */
6
+ declare const getRegion: () => string | null;
7
+ /**
8
+ * React hook form of {@link getRegion}. The region is fixed for an app instance's
9
+ * lifetime, but the discovery global can arrive just after first paint, so this
10
+ * re-reads once the host runtime's `ready` promise resolves.
11
+ */
12
+ declare const useRegion: () => string | null;
13
+
14
+ export { getRegion, useRegion };
package/dist/region.js ADDED
@@ -0,0 +1,22 @@
1
+ import { useEffect, useState } from "react";
2
+ import { getHostRuntime } from "./hostRuntime";
3
+ const getRegion = () => getHostRuntime()?.region ?? null;
4
+ const useRegion = () => {
5
+ const [region, setRegion] = useState(getRegion);
6
+ useEffect(() => {
7
+ if (region !== null) return;
8
+ let live = true;
9
+ void getHostRuntime()?.ready?.then(() => {
10
+ if (live) setRegion(getRegion());
11
+ });
12
+ return () => {
13
+ live = false;
14
+ };
15
+ }, [region]);
16
+ return region;
17
+ };
18
+ export {
19
+ getRegion,
20
+ useRegion
21
+ };
22
+ //# sourceMappingURL=region.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/region.ts"],"sourcesContent":["// Region-awareness (UI_AS_APPS_SPEC §4.1). The host can mount the SAME app in more\n// than one chrome region — e.g. the agents activity puts one app in BOTH the panel\n// slot (`panel.agent`, the conversation list) and the stage slot\n// (`stage.conversation`, the selected conversation). `getRegion()` lets that one app\n// tell the slots apart and render the right view.\n//\n// This is descriptive only: the region id is a non-secret string the host already\n// knows. It grants nothing and gates nothing — it just names where the app is\n// mounted. The host reports it on the §4 discovery global beside `appMountPath`.\n\nimport { useEffect, useState } from 'react';\nimport { getHostRuntime } from './hostRuntime';\n\n/**\n * The chrome region this app instance is mounted in (e.g. `\"panel.agent\"`,\n * `\"stage.conversation\"`), or `null` when unknown — a standalone app, local\n * `vite dev`, or an older host that doesn't report it.\n */\nexport const getRegion = (): string | null => getHostRuntime()?.region ?? null;\n\n/**\n * React hook form of {@link getRegion}. The region is fixed for an app instance's\n * lifetime, but the discovery global can arrive just after first paint, so this\n * re-reads once the host runtime's `ready` promise resolves.\n */\nexport const useRegion = (): string | null => {\n const [region, setRegion] = useState<string | null>(getRegion);\n useEffect(() => {\n if (region !== null) return;\n let live = true;\n void getHostRuntime()?.ready?.then(() => {\n if (live) setRegion(getRegion());\n });\n return () => {\n live = false;\n };\n }, [region]);\n return region;\n};\n"],"mappings":"AAUA,SAAS,WAAW,gBAAgB;AACpC,SAAS,sBAAsB;AAOxB,MAAM,YAAY,MAAqB,eAAe,GAAG,UAAU;AAOnE,MAAM,YAAY,MAAqB;AAC5C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,SAAS;AAC7D,YAAU,MAAM;AACd,QAAI,WAAW,KAAM;AACrB,QAAI,OAAO;AACX,SAAK,eAAe,GAAG,OAAO,KAAK,MAAM;AACvC,UAAI,KAAM,WAAU,UAAU,CAAC;AAAA,IACjC,CAAC;AACD,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AACX,SAAO;AACT;","names":[]}
@@ -0,0 +1,74 @@
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 testing_exports = {};
20
+ __export(testing_exports, {
21
+ createMockHost: () => createMockHost
22
+ });
23
+ module.exports = __toCommonJS(testing_exports);
24
+ const GLOBAL_KEY = "__immediatelyRun__";
25
+ function createMockHost() {
26
+ const handlers = /* @__PURE__ */ new Set();
27
+ const sent = [];
28
+ const protocolCalls = [];
29
+ const responders = /* @__PURE__ */ new Map();
30
+ const key = (protocol, method) => `${protocol}::${method}`;
31
+ const transport = {
32
+ sendMessage(type, data = {}) {
33
+ sent.push({ type, data });
34
+ },
35
+ protocolRequest(protocol, method, params) {
36
+ protocolCalls.push({ protocol, method, params });
37
+ const responder = responders.get(key(protocol, method));
38
+ if (!responder) {
39
+ return Promise.reject(
40
+ new Error(`mockHost: no stub for protocolRequest('${protocol}', '${method}')`)
41
+ );
42
+ }
43
+ return Promise.resolve().then(() => responder(params));
44
+ },
45
+ onMessage(handler) {
46
+ handlers.add(handler);
47
+ return { dispose: () => handlers.delete(handler) };
48
+ }
49
+ };
50
+ const g = globalThis;
51
+ return {
52
+ transport,
53
+ install(extras = {}) {
54
+ g[GLOBAL_KEY] = { ...extras, transport };
55
+ },
56
+ uninstall() {
57
+ delete g[GLOBAL_KEY];
58
+ },
59
+ emit(msg) {
60
+ for (const handler of [...handlers]) handler(msg);
61
+ },
62
+ stubProtocol(protocol, method, responder) {
63
+ responders.set(key(protocol, method), responder);
64
+ },
65
+ sent,
66
+ protocolCalls,
67
+ handlerCount: () => handlers.size
68
+ };
69
+ }
70
+ // Annotate the CommonJS export names for ESM import in node:
71
+ 0 && (module.exports = {
72
+ createMockHost
73
+ });
74
+ //# sourceMappingURL=testing.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// Public testing utility — `@immediately-run/sdk/testing` (TESTING_AUTOMATION_SPEC\n// §3/§7). A mock host transport so apps built on this SDK can unit-test their host\n// interaction in CI, with no live bundler/host: drive the SDK's REAL transport path\n// by installing this at the §4 discovery global, then `emit()` host→app pushes and\n// assert your app reacts.\n//\n// import { createMockHost } from '@immediately-run/sdk/testing';\n// const host = createMockHost();\n// host.install(); // SDK now resolves this transport\n// host.stubProtocol('spaces', 'list', () => ({ ok: true, data: [] }));\n// // …render your app / call SDK functions…\n// host.emit({ type: 'mount-add', mount: { path: '/spaces/a', type: 'firestore', id: 'a' } });\n// expect(host.sent).toContainEqual({ type: 'request-mounts', data: {} });\n// host.uninstall();\n//\n// It implements the `sandboxUtils` host transport (sendMessage / protocolRequest /\n// onMessage), so tests exercise the actual `transport()` resolution, the\n// `addListener` type-filter, and `protocolRequest` round-trips — higher fidelity\n// than mocking `./sandboxUtils` wholesale.\n\n/** A message pushed host → app, as it arrives on `onMessage`. */\nexport interface HostMessage {\n type: string;\n [k: string]: unknown;\n}\n\n/** An outbound `sendMessage` captured by the mock. */\nexport interface SentMessage {\n type: string;\n data: Record<string, unknown>;\n}\n\n/** An outbound `protocolRequest` captured by the mock. */\nexport interface ProtocolCall {\n protocol: string;\n method: string;\n params: unknown[];\n}\n\n/** Responder stub for a `protocolRequest(protocol, method, …)`. */\nexport type ProtocolResponder = (params: unknown[]) => unknown;\n\n/** Extra §4 discovery-global fields to publish alongside `transport` on install. */\nexport interface MockHostGlobalExtras {\n runtimeVersion?: string;\n protocolVersion?: string;\n appMountPath?: string;\n}\n\ninterface ImmediatelyRunGlobal extends MockHostGlobalExtras {\n transport?: unknown;\n}\n\n/** The controllable mock host returned by {@link createMockHost}. */\nexport interface MockHost {\n /** The §4 host transport object (also published on the global by `install`). */\n transport: {\n sendMessage(type: string, data?: Record<string, unknown>): void;\n protocolRequest(protocol: string, method: string, params: unknown[]): Promise<unknown>;\n onMessage(handler: (msg: HostMessage) => void): { dispose(): void };\n };\n /** Publish the transport at `globalThis.__immediatelyRun__` (npm-fetched path). */\n install(extras?: MockHostGlobalExtras): void;\n /** Remove the discovery global (call in `afterEach`). */\n uninstall(): void;\n /** Simulate a host → app push; delivered to every live `onMessage` handler. */\n emit(msg: HostMessage): void;\n /** Register a response for a `protocolRequest`; unstubbed calls reject. */\n stubProtocol(protocol: string, method: string, responder: ProtocolResponder): void;\n /** Every `sendMessage` the SDK made, in order. */\n readonly sent: SentMessage[];\n /** Every `protocolRequest` the SDK made, in order. */\n readonly protocolCalls: ProtocolCall[];\n /** Count of live `onMessage` listeners (leak checks). */\n handlerCount(): number;\n}\n\nconst GLOBAL_KEY = '__immediatelyRun__';\n\n/** Build a controllable mock host. Nothing is global until you call `install()`. */\nexport function createMockHost(): MockHost {\n const handlers = new Set<(msg: HostMessage) => void>();\n const sent: SentMessage[] = [];\n const protocolCalls: ProtocolCall[] = [];\n const responders = new Map<string, ProtocolResponder>();\n const key = (protocol: string, method: string) => `${protocol}::${method}`;\n\n const transport: MockHost['transport'] = {\n sendMessage(type, data = {}) {\n sent.push({ type, data });\n },\n protocolRequest(protocol, method, params) {\n protocolCalls.push({ protocol, method, params });\n const responder = responders.get(key(protocol, method));\n if (!responder) {\n return Promise.reject(\n new Error(`mockHost: no stub for protocolRequest('${protocol}', '${method}')`),\n );\n }\n // Resolve async so callers observe real microtask ordering.\n return Promise.resolve().then(() => responder(params));\n },\n onMessage(handler) {\n handlers.add(handler);\n return { dispose: () => handlers.delete(handler) };\n },\n };\n\n const g = globalThis as { [GLOBAL_KEY]?: ImmediatelyRunGlobal };\n\n return {\n transport,\n install(extras = {}) {\n g[GLOBAL_KEY] = { ...extras, transport };\n },\n uninstall() {\n delete g[GLOBAL_KEY];\n },\n emit(msg) {\n // Copy so a handler that unsubscribes mid-emit doesn't mutate the live set.\n for (const handler of [...handlers]) handler(msg);\n },\n stubProtocol(protocol, method, responder) {\n responders.set(key(protocol, method), responder);\n },\n sent,\n protocolCalls,\n handlerCount: () => handlers.size,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6EA,MAAM,aAAa;AAGZ,SAAS,iBAA2B;AACzC,QAAM,WAAW,oBAAI,IAAgC;AACrD,QAAM,OAAsB,CAAC;AAC7B,QAAM,gBAAgC,CAAC;AACvC,QAAM,aAAa,oBAAI,IAA+B;AACtD,QAAM,MAAM,CAAC,UAAkB,WAAmB,GAAG,QAAQ,KAAK,MAAM;AAExE,QAAM,YAAmC;AAAA,IACvC,YAAY,MAAM,OAAO,CAAC,GAAG;AAC3B,WAAK,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,IAC1B;AAAA,IACA,gBAAgB,UAAU,QAAQ,QAAQ;AACxC,oBAAc,KAAK,EAAE,UAAU,QAAQ,OAAO,CAAC;AAC/C,YAAM,YAAY,WAAW,IAAI,IAAI,UAAU,MAAM,CAAC;AACtD,UAAI,CAAC,WAAW;AACd,eAAO,QAAQ;AAAA,UACb,IAAI,MAAM,0CAA0C,QAAQ,OAAO,MAAM,IAAI;AAAA,QAC/E;AAAA,MACF;AAEA,aAAO,QAAQ,QAAQ,EAAE,KAAK,MAAM,UAAU,MAAM,CAAC;AAAA,IACvD;AAAA,IACA,UAAU,SAAS;AACjB,eAAS,IAAI,OAAO;AACpB,aAAO,EAAE,SAAS,MAAM,SAAS,OAAO,OAAO,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,IAAI;AAEV,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,SAAS,CAAC,GAAG;AACnB,QAAE,UAAU,IAAI,EAAE,GAAG,QAAQ,UAAU;AAAA,IACzC;AAAA,IACA,YAAY;AACV,aAAO,EAAE,UAAU;AAAA,IACrB;AAAA,IACA,KAAK,KAAK;AAER,iBAAW,WAAW,CAAC,GAAG,QAAQ,EAAG,SAAQ,GAAG;AAAA,IAClD;AAAA,IACA,aAAa,UAAU,QAAQ,WAAW;AACxC,iBAAW,IAAI,IAAI,UAAU,MAAM,GAAG,SAAS;AAAA,IACjD;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,MAAM,SAAS;AAAA,EAC/B;AACF;","names":[]}
@@ -0,0 +1,53 @@
1
+ /** A message pushed host → app, as it arrives on `onMessage`. */
2
+ interface HostMessage {
3
+ type: string;
4
+ [k: string]: unknown;
5
+ }
6
+ /** An outbound `sendMessage` captured by the mock. */
7
+ interface SentMessage {
8
+ type: string;
9
+ data: Record<string, unknown>;
10
+ }
11
+ /** An outbound `protocolRequest` captured by the mock. */
12
+ interface ProtocolCall {
13
+ protocol: string;
14
+ method: string;
15
+ params: unknown[];
16
+ }
17
+ /** Responder stub for a `protocolRequest(protocol, method, …)`. */
18
+ type ProtocolResponder = (params: unknown[]) => unknown;
19
+ /** Extra §4 discovery-global fields to publish alongside `transport` on install. */
20
+ interface MockHostGlobalExtras {
21
+ runtimeVersion?: string;
22
+ protocolVersion?: string;
23
+ appMountPath?: string;
24
+ }
25
+ /** The controllable mock host returned by {@link createMockHost}. */
26
+ interface MockHost {
27
+ /** The §4 host transport object (also published on the global by `install`). */
28
+ transport: {
29
+ sendMessage(type: string, data?: Record<string, unknown>): void;
30
+ protocolRequest(protocol: string, method: string, params: unknown[]): Promise<unknown>;
31
+ onMessage(handler: (msg: HostMessage) => void): {
32
+ dispose(): void;
33
+ };
34
+ };
35
+ /** Publish the transport at `globalThis.__immediatelyRun__` (npm-fetched path). */
36
+ install(extras?: MockHostGlobalExtras): void;
37
+ /** Remove the discovery global (call in `afterEach`). */
38
+ uninstall(): void;
39
+ /** Simulate a host → app push; delivered to every live `onMessage` handler. */
40
+ emit(msg: HostMessage): void;
41
+ /** Register a response for a `protocolRequest`; unstubbed calls reject. */
42
+ stubProtocol(protocol: string, method: string, responder: ProtocolResponder): void;
43
+ /** Every `sendMessage` the SDK made, in order. */
44
+ readonly sent: SentMessage[];
45
+ /** Every `protocolRequest` the SDK made, in order. */
46
+ readonly protocolCalls: ProtocolCall[];
47
+ /** Count of live `onMessage` listeners (leak checks). */
48
+ handlerCount(): number;
49
+ }
50
+ /** Build a controllable mock host. Nothing is global until you call `install()`. */
51
+ declare function createMockHost(): MockHost;
52
+
53
+ export { type HostMessage, type MockHost, type MockHostGlobalExtras, type ProtocolCall, type ProtocolResponder, type SentMessage, createMockHost };
@@ -0,0 +1,53 @@
1
+ /** A message pushed host → app, as it arrives on `onMessage`. */
2
+ interface HostMessage {
3
+ type: string;
4
+ [k: string]: unknown;
5
+ }
6
+ /** An outbound `sendMessage` captured by the mock. */
7
+ interface SentMessage {
8
+ type: string;
9
+ data: Record<string, unknown>;
10
+ }
11
+ /** An outbound `protocolRequest` captured by the mock. */
12
+ interface ProtocolCall {
13
+ protocol: string;
14
+ method: string;
15
+ params: unknown[];
16
+ }
17
+ /** Responder stub for a `protocolRequest(protocol, method, …)`. */
18
+ type ProtocolResponder = (params: unknown[]) => unknown;
19
+ /** Extra §4 discovery-global fields to publish alongside `transport` on install. */
20
+ interface MockHostGlobalExtras {
21
+ runtimeVersion?: string;
22
+ protocolVersion?: string;
23
+ appMountPath?: string;
24
+ }
25
+ /** The controllable mock host returned by {@link createMockHost}. */
26
+ interface MockHost {
27
+ /** The §4 host transport object (also published on the global by `install`). */
28
+ transport: {
29
+ sendMessage(type: string, data?: Record<string, unknown>): void;
30
+ protocolRequest(protocol: string, method: string, params: unknown[]): Promise<unknown>;
31
+ onMessage(handler: (msg: HostMessage) => void): {
32
+ dispose(): void;
33
+ };
34
+ };
35
+ /** Publish the transport at `globalThis.__immediatelyRun__` (npm-fetched path). */
36
+ install(extras?: MockHostGlobalExtras): void;
37
+ /** Remove the discovery global (call in `afterEach`). */
38
+ uninstall(): void;
39
+ /** Simulate a host → app push; delivered to every live `onMessage` handler. */
40
+ emit(msg: HostMessage): void;
41
+ /** Register a response for a `protocolRequest`; unstubbed calls reject. */
42
+ stubProtocol(protocol: string, method: string, responder: ProtocolResponder): void;
43
+ /** Every `sendMessage` the SDK made, in order. */
44
+ readonly sent: SentMessage[];
45
+ /** Every `protocolRequest` the SDK made, in order. */
46
+ readonly protocolCalls: ProtocolCall[];
47
+ /** Count of live `onMessage` listeners (leak checks). */
48
+ handlerCount(): number;
49
+ }
50
+ /** Build a controllable mock host. Nothing is global until you call `install()`. */
51
+ declare function createMockHost(): MockHost;
52
+
53
+ export { type HostMessage, type MockHost, type MockHostGlobalExtras, type ProtocolCall, type ProtocolResponder, type SentMessage, createMockHost };
@@ -0,0 +1,50 @@
1
+ const GLOBAL_KEY = "__immediatelyRun__";
2
+ function createMockHost() {
3
+ const handlers = /* @__PURE__ */ new Set();
4
+ const sent = [];
5
+ const protocolCalls = [];
6
+ const responders = /* @__PURE__ */ new Map();
7
+ const key = (protocol, method) => `${protocol}::${method}`;
8
+ const transport = {
9
+ sendMessage(type, data = {}) {
10
+ sent.push({ type, data });
11
+ },
12
+ protocolRequest(protocol, method, params) {
13
+ protocolCalls.push({ protocol, method, params });
14
+ const responder = responders.get(key(protocol, method));
15
+ if (!responder) {
16
+ return Promise.reject(
17
+ new Error(`mockHost: no stub for protocolRequest('${protocol}', '${method}')`)
18
+ );
19
+ }
20
+ return Promise.resolve().then(() => responder(params));
21
+ },
22
+ onMessage(handler) {
23
+ handlers.add(handler);
24
+ return { dispose: () => handlers.delete(handler) };
25
+ }
26
+ };
27
+ const g = globalThis;
28
+ return {
29
+ transport,
30
+ install(extras = {}) {
31
+ g[GLOBAL_KEY] = { ...extras, transport };
32
+ },
33
+ uninstall() {
34
+ delete g[GLOBAL_KEY];
35
+ },
36
+ emit(msg) {
37
+ for (const handler of [...handlers]) handler(msg);
38
+ },
39
+ stubProtocol(protocol, method, responder) {
40
+ responders.set(key(protocol, method), responder);
41
+ },
42
+ sent,
43
+ protocolCalls,
44
+ handlerCount: () => handlers.size
45
+ };
46
+ }
47
+ export {
48
+ createMockHost
49
+ };
50
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// Public testing utility — `@immediately-run/sdk/testing` (TESTING_AUTOMATION_SPEC\n// §3/§7). A mock host transport so apps built on this SDK can unit-test their host\n// interaction in CI, with no live bundler/host: drive the SDK's REAL transport path\n// by installing this at the §4 discovery global, then `emit()` host→app pushes and\n// assert your app reacts.\n//\n// import { createMockHost } from '@immediately-run/sdk/testing';\n// const host = createMockHost();\n// host.install(); // SDK now resolves this transport\n// host.stubProtocol('spaces', 'list', () => ({ ok: true, data: [] }));\n// // …render your app / call SDK functions…\n// host.emit({ type: 'mount-add', mount: { path: '/spaces/a', type: 'firestore', id: 'a' } });\n// expect(host.sent).toContainEqual({ type: 'request-mounts', data: {} });\n// host.uninstall();\n//\n// It implements the `sandboxUtils` host transport (sendMessage / protocolRequest /\n// onMessage), so tests exercise the actual `transport()` resolution, the\n// `addListener` type-filter, and `protocolRequest` round-trips — higher fidelity\n// than mocking `./sandboxUtils` wholesale.\n\n/** A message pushed host → app, as it arrives on `onMessage`. */\nexport interface HostMessage {\n type: string;\n [k: string]: unknown;\n}\n\n/** An outbound `sendMessage` captured by the mock. */\nexport interface SentMessage {\n type: string;\n data: Record<string, unknown>;\n}\n\n/** An outbound `protocolRequest` captured by the mock. */\nexport interface ProtocolCall {\n protocol: string;\n method: string;\n params: unknown[];\n}\n\n/** Responder stub for a `protocolRequest(protocol, method, …)`. */\nexport type ProtocolResponder = (params: unknown[]) => unknown;\n\n/** Extra §4 discovery-global fields to publish alongside `transport` on install. */\nexport interface MockHostGlobalExtras {\n runtimeVersion?: string;\n protocolVersion?: string;\n appMountPath?: string;\n}\n\ninterface ImmediatelyRunGlobal extends MockHostGlobalExtras {\n transport?: unknown;\n}\n\n/** The controllable mock host returned by {@link createMockHost}. */\nexport interface MockHost {\n /** The §4 host transport object (also published on the global by `install`). */\n transport: {\n sendMessage(type: string, data?: Record<string, unknown>): void;\n protocolRequest(protocol: string, method: string, params: unknown[]): Promise<unknown>;\n onMessage(handler: (msg: HostMessage) => void): { dispose(): void };\n };\n /** Publish the transport at `globalThis.__immediatelyRun__` (npm-fetched path). */\n install(extras?: MockHostGlobalExtras): void;\n /** Remove the discovery global (call in `afterEach`). */\n uninstall(): void;\n /** Simulate a host → app push; delivered to every live `onMessage` handler. */\n emit(msg: HostMessage): void;\n /** Register a response for a `protocolRequest`; unstubbed calls reject. */\n stubProtocol(protocol: string, method: string, responder: ProtocolResponder): void;\n /** Every `sendMessage` the SDK made, in order. */\n readonly sent: SentMessage[];\n /** Every `protocolRequest` the SDK made, in order. */\n readonly protocolCalls: ProtocolCall[];\n /** Count of live `onMessage` listeners (leak checks). */\n handlerCount(): number;\n}\n\nconst GLOBAL_KEY = '__immediatelyRun__';\n\n/** Build a controllable mock host. Nothing is global until you call `install()`. */\nexport function createMockHost(): MockHost {\n const handlers = new Set<(msg: HostMessage) => void>();\n const sent: SentMessage[] = [];\n const protocolCalls: ProtocolCall[] = [];\n const responders = new Map<string, ProtocolResponder>();\n const key = (protocol: string, method: string) => `${protocol}::${method}`;\n\n const transport: MockHost['transport'] = {\n sendMessage(type, data = {}) {\n sent.push({ type, data });\n },\n protocolRequest(protocol, method, params) {\n protocolCalls.push({ protocol, method, params });\n const responder = responders.get(key(protocol, method));\n if (!responder) {\n return Promise.reject(\n new Error(`mockHost: no stub for protocolRequest('${protocol}', '${method}')`),\n );\n }\n // Resolve async so callers observe real microtask ordering.\n return Promise.resolve().then(() => responder(params));\n },\n onMessage(handler) {\n handlers.add(handler);\n return { dispose: () => handlers.delete(handler) };\n },\n };\n\n const g = globalThis as { [GLOBAL_KEY]?: ImmediatelyRunGlobal };\n\n return {\n transport,\n install(extras = {}) {\n g[GLOBAL_KEY] = { ...extras, transport };\n },\n uninstall() {\n delete g[GLOBAL_KEY];\n },\n emit(msg) {\n // Copy so a handler that unsubscribes mid-emit doesn't mutate the live set.\n for (const handler of [...handlers]) handler(msg);\n },\n stubProtocol(protocol, method, responder) {\n responders.set(key(protocol, method), responder);\n },\n sent,\n protocolCalls,\n handlerCount: () => handlers.size,\n };\n}\n"],"mappings":"AA6EA,MAAM,aAAa;AAGZ,SAAS,iBAA2B;AACzC,QAAM,WAAW,oBAAI,IAAgC;AACrD,QAAM,OAAsB,CAAC;AAC7B,QAAM,gBAAgC,CAAC;AACvC,QAAM,aAAa,oBAAI,IAA+B;AACtD,QAAM,MAAM,CAAC,UAAkB,WAAmB,GAAG,QAAQ,KAAK,MAAM;AAExE,QAAM,YAAmC;AAAA,IACvC,YAAY,MAAM,OAAO,CAAC,GAAG;AAC3B,WAAK,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,IAC1B;AAAA,IACA,gBAAgB,UAAU,QAAQ,QAAQ;AACxC,oBAAc,KAAK,EAAE,UAAU,QAAQ,OAAO,CAAC;AAC/C,YAAM,YAAY,WAAW,IAAI,IAAI,UAAU,MAAM,CAAC;AACtD,UAAI,CAAC,WAAW;AACd,eAAO,QAAQ;AAAA,UACb,IAAI,MAAM,0CAA0C,QAAQ,OAAO,MAAM,IAAI;AAAA,QAC/E;AAAA,MACF;AAEA,aAAO,QAAQ,QAAQ,EAAE,KAAK,MAAM,UAAU,MAAM,CAAC;AAAA,IACvD;AAAA,IACA,UAAU,SAAS;AACjB,eAAS,IAAI,OAAO;AACpB,aAAO,EAAE,SAAS,MAAM,SAAS,OAAO,OAAO,EAAE;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,IAAI;AAEV,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,SAAS,CAAC,GAAG;AACnB,QAAE,UAAU,IAAI,EAAE,GAAG,QAAQ,UAAU;AAAA,IACzC;AAAA,IACA,YAAY;AACV,aAAO,EAAE,UAAU;AAAA,IACrB;AAAA,IACA,KAAK,KAAK;AAER,iBAAW,WAAW,CAAC,GAAG,QAAQ,EAAG,SAAQ,GAAG;AAAA,IAClD;AAAA,IACA,aAAa,UAAU,QAAQ,WAAW;AACxC,iBAAW,IAAI,IAAI,UAAU,MAAM,GAAG,SAAS;AAAA,IACjD;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,MAAM,SAAS;AAAA,EAC/B;AACF;","names":[]}
package/dist/version.cjs CHANGED
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  SDK_VERSION: () => SDK_VERSION
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const SDK_VERSION = "0.11.0";
24
+ const SDK_VERSION = "0.12.0";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  SDK_VERSION
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.11.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.12.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.11.0";
2
+ declare const SDK_VERSION = "0.12.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.11.0";
2
+ declare const SDK_VERSION = "0.12.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const SDK_VERSION = "0.11.0";
1
+ const SDK_VERSION = "0.12.0";
2
2
  export {
3
3
  SDK_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.11.0';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.12.0';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immediately-run/sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Runtime SDK for code executing inside an immediately.run sandbox.",
5
5
  "license": "MIT",
6
6
  "repository": "github:immediately-run/immediately-run-sdk",