@immediately-run/sdk 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/boot.cjs +4 -3
  2. package/dist/boot.cjs.map +1 -1
  3. package/dist/boot.js +4 -3
  4. package/dist/boot.js.map +1 -1
  5. package/dist/editor.cjs +9 -0
  6. package/dist/editor.cjs.map +1 -1
  7. package/dist/editor.d.cts +47 -1
  8. package/dist/editor.d.ts +47 -1
  9. package/dist/editor.js +6 -0
  10. package/dist/editor.js.map +1 -1
  11. package/dist/index.cjs +4 -0
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +5 -3
  14. package/dist/index.d.ts +5 -3
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/injectedBundler.cjs +49 -0
  18. package/dist/injectedBundler.cjs.map +1 -0
  19. package/dist/injectedBundler.d.cts +29 -0
  20. package/dist/injectedBundler.d.ts +29 -0
  21. package/dist/injectedBundler.js +24 -0
  22. package/dist/injectedBundler.js.map +1 -0
  23. package/dist/irMarkers.cjs +72 -0
  24. package/dist/irMarkers.cjs.map +1 -0
  25. package/dist/irMarkers.d.cts +54 -0
  26. package/dist/irMarkers.d.ts +54 -0
  27. package/dist/irMarkers.js +44 -0
  28. package/dist/irMarkers.js.map +1 -0
  29. package/dist/mountMatch.cjs +29 -0
  30. package/dist/mountMatch.cjs.map +1 -0
  31. package/dist/mountMatch.d.cts +21 -0
  32. package/dist/mountMatch.d.ts +21 -0
  33. package/dist/mountMatch.js +5 -0
  34. package/dist/mountMatch.js.map +1 -0
  35. package/dist/mounts.cjs +51 -4
  36. package/dist/mounts.cjs.map +1 -1
  37. package/dist/mounts.d.cts +145 -19
  38. package/dist/mounts.d.ts +145 -19
  39. package/dist/mounts.js +44 -3
  40. package/dist/mounts.js.map +1 -1
  41. package/dist/ready.cjs +69 -0
  42. package/dist/ready.cjs.map +1 -0
  43. package/dist/ready.d.cts +32 -0
  44. package/dist/ready.d.ts +32 -0
  45. package/dist/ready.js +41 -0
  46. package/dist/ready.js.map +1 -0
  47. package/dist/tasks.cjs +3 -0
  48. package/dist/tasks.cjs.map +1 -1
  49. package/dist/tasks.d.cts +24 -1
  50. package/dist/tasks.d.ts +24 -1
  51. package/dist/tasks.js +2 -0
  52. package/dist/tasks.js.map +1 -1
  53. package/dist/version.cjs +1 -1
  54. package/dist/version.cjs.map +1 -1
  55. package/dist/version.d.cts +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/dist/version.js.map +1 -1
  59. package/package.json +1 -1
package/dist/boot.cjs CHANGED
@@ -31,6 +31,7 @@ var import_FileRouter = require("./components/FileRouter");
31
31
  var import_MainContent = require("./components/MainContent");
32
32
  var import_MDXComponents = require("./components/MDXComponents");
33
33
  var import_contextUtils = require("./contextUtils");
34
+ var import_injectedBundler = require("./injectedBundler");
34
35
  var import_MDXProvider = require("./MDXProvider");
35
36
  var import_moduleCache = require("./moduleCache");
36
37
  var import_routing = require("./routing");
@@ -62,6 +63,7 @@ const TinkerableApp = ({ routingSpec }) => {
62
63
  return removeListener;
63
64
  }, [setContext]);
64
65
  (0, import_react.useEffect)(() => {
66
+ const source = (0, import_injectedBundler.resolveMetadataSource)((0, import_injectedBundler.getInjectedMetadataEmitter)());
65
67
  const dispose = (0, import_sandboxUtils.addListener)(
66
68
  "metadata-update",
67
69
  ({ update }) => {
@@ -76,10 +78,9 @@ const TinkerableApp = ({ routingSpec }) => {
76
78
  }
77
79
  );
78
80
  },
79
- // @ts-ignore
80
- module.evaluation.module.bundler.onMetadataChange
81
+ source.event
81
82
  );
82
- module.evaluation.module.bundler.onMetadataChangeEmitter.enable();
83
+ source.enable();
83
84
  return dispose;
84
85
  }, [setContext]);
85
86
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TinkerableContext.TinkerableContext, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_routing.Router, {}) });
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 { 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 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 // @ts-ignore\n module.evaluation.module.bundler.onMetadataChange\n );\n // @ts-ignore\n module.evaluation.module.bundler.onMetadataChangeEmitter.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;AA0EM;AA1EN,mBAAoD;AACpD,oBAA2B;AAE3B,oBAA8B;AAC9B,wBAA2B;AAC3B,yBAA4B;AAC5B,2BAAuC;AACvC,0BAAiD;AACjD,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;AACd,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;AAAA,MAEA,OAAO,WAAW,OAAO,QAAQ;AAAA,IACnC;AAEA,WAAO,WAAW,OAAO,QAAQ,wBAAwB,OAAO;AAChE,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, 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"]}
package/dist/boot.js CHANGED
@@ -6,6 +6,7 @@ import { FileRouter } from "./components/FileRouter";
6
6
  import { MainContent } from "./components/MainContent";
7
7
  import { DEFAULT_MDX_COMPONENTS } from "./components/MDXComponents";
8
8
  import { getInitialContext, updateContext } from "./contextUtils";
9
+ import { getInjectedMetadataEmitter, resolveMetadataSource } from "./injectedBundler";
9
10
  import { MDXProvider } from "./MDXProvider";
10
11
  import { ModuleCache, ModuleCacheContextProvider } from "./moduleCache";
11
12
  import { Router } from "./routing";
@@ -37,6 +38,7 @@ const TinkerableApp = ({ routingSpec }) => {
37
38
  return removeListener;
38
39
  }, [setContext]);
39
40
  useEffect(() => {
41
+ const source = resolveMetadataSource(getInjectedMetadataEmitter());
40
42
  const dispose = addListener(
41
43
  "metadata-update",
42
44
  ({ update }) => {
@@ -51,10 +53,9 @@ const TinkerableApp = ({ routingSpec }) => {
51
53
  }
52
54
  );
53
55
  },
54
- // @ts-ignore
55
- module.evaluation.module.bundler.onMetadataChange
56
+ source.event
56
57
  );
57
- module.evaluation.module.bundler.onMetadataChangeEmitter.enable();
58
+ source.enable();
58
59
  return dispose;
59
60
  }, [setContext]);
60
61
  return /* @__PURE__ */ jsx(TinkerableContext, { value: context, children: /* @__PURE__ */ jsx(Router, {}) });
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 { 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 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 // @ts-ignore\n module.evaluation.module.bundler.onMetadataChange\n );\n // @ts-ignore\n module.evaluation.module.bundler.onMetadataChangeEmitter.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":"AA0EM;AA1EN,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,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;AACd,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;AAAA,MAEA,OAAO,WAAW,OAAO,QAAQ;AAAA,IACnC;AAEA,WAAO,WAAW,OAAO,QAAQ,wBAAwB,OAAO;AAChE,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, 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"]}
package/dist/editor.cjs CHANGED
@@ -18,11 +18,14 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var editor_exports = {};
20
20
  __export(editor_exports, {
21
+ closeFile: () => closeFile,
21
22
  createFile: () => createFile,
22
23
  createFolder: () => createFolder,
23
24
  deleteEntry: () => deleteEntry,
24
25
  openInEditor: () => openInEditor,
25
26
  renameEntry: () => renameEntry,
27
+ requestEdit: () => requestEdit,
28
+ setActiveFile: () => setActiveFile,
26
29
  uploadFile: () => uploadFile
27
30
  });
28
31
  module.exports = __toCommonJS(editor_exports);
@@ -36,6 +39,9 @@ const editorRequest = async (method, arg) => {
36
39
  }
37
40
  };
38
41
  const openInEditor = (path) => editorRequest("open", { path });
42
+ const requestEdit = (target) => editorRequest("requestEdit", target ? { ...target } : {});
43
+ const setActiveFile = (path) => editorRequest("setActive", { path });
44
+ const closeFile = (path) => editorRequest("close", { path });
39
45
  const createFile = (path) => editorRequest("createFile", { path });
40
46
  const createFolder = (path) => editorRequest("createFolder", { path });
41
47
  const deleteEntry = (path) => editorRequest("deleteEntry", { path });
@@ -43,11 +49,14 @@ const renameEntry = (from, to) => editorRequest("rename", { from, to });
43
49
  const uploadFile = (path, bytes) => editorRequest("upload", { path, bytes });
44
50
  // Annotate the CommonJS export names for ESM import in node:
45
51
  0 && (module.exports = {
52
+ closeFile,
46
53
  createFile,
47
54
  createFolder,
48
55
  deleteEntry,
49
56
  openInEditor,
50
57
  renameEntry,
58
+ requestEdit,
59
+ setActiveFile,
51
60
  uploadFile
52
61
  });
53
62
  //# sourceMappingURL=editor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["import { protocolRequest } from './sandboxUtils';\n\n/**\n * Open a working-tree file in the immediately.run host editor (UI_AS_APPS_SPEC §4 —\n * the file explorer's click-to-open). This is an INTENT: the app asks, the HOST\n * validates the path and drives the CodeMirror editor — the editor itself stays\n * host-owned (§2 recursion boundary), so an app can never own or script it beyond\n * \"please show this file\".\n *\n * Requires the elevated `editor:open` capability — a previewed app does not hold it\n * (it must not move the host's focus), so only a system app whose binding grants it\n * (the file explorer) can call this; anyone else is refused at the gate.\n */\n\n/** An error from {@link openInEditor}, carrying a machine-readable `.code`. */\nexport interface EditorOpenError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:open`\n | 'not-found' // no such file in the live working tree (the host never creates)\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session to open files in\n | 'unknown';\n}\n\ntype EditorResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\nconst editorRequest = async (\n method: string,\n arg: Record<string, unknown>,\n): Promise<void> => {\n const res = (await protocolRequest('editor', method, [arg])) as EditorResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `editor ${method} failed`) as EditorWriteError;\n err.code = (res?.code as EditorWriteError['code']) ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to open `path` (a repo-relative working-tree path, e.g. `src/App.tsx`\n * or `/src/App.tsx`) in the editor. Resolves once the editor switches to it; rejects\n * with an {@link EditorOpenError} (`.code`) if the path is invalid, missing, or this\n * app may not open files.\n */\nexport const openInEditor = (path: string): Promise<void> => editorRequest('open', { path });\n\n// ---------------------------------------------------------------------------\n// Working-tree mutation (UI_AS_APPS_SPEC §4 / EDITOR_AS_APP_SPEC §5.2). The file\n// explorer NAMES a working-tree path and the HOST performs the COW write (and\n// refreshes the preview) — the app holds no write port; it asks. Gated by the\n// first-party `editor:write` capability, so only a first-party chrome app (the\n// file explorer) can call these; anyone else is refused at the gate.\n// ---------------------------------------------------------------------------\n\n/** An error from a working-tree mutation, carrying a machine-readable `.code`. */\nexport interface EditorWriteError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:write` (first-party-only)\n | 'not-found' // the target file/folder does not exist (delete/rename)\n | 'exists' // the target already exists (create/rename would clobber)\n | 'protected' // the host refuses to delete this file (e.g. package.json)\n | 'too-large' // an upload exceeds the host's size limit\n | 'invalid-params' // a path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Create an empty working-tree file at `path` and open it. Rejects `exists` if a\n * file is already there. */\nexport const createFile = (path: string): Promise<void> => editorRequest('createFile', { path });\n\n/** Create a working-tree folder at `path` (materialised with a `.gitkeep`). */\nexport const createFolder = (path: string): Promise<void> =>\n editorRequest('createFolder', { path });\n\n/** Delete a working-tree file, or a folder and everything under it. Rejects\n * `protected` for files the host won't remove, `not-found` if absent. */\nexport const deleteEntry = (path: string): Promise<void> => editorRequest('deleteEntry', { path });\n\n/** Rename/move a working-tree file from `from` to `to`. Rejects `exists` if `to`\n * is taken, `not-found` if `from` is absent. */\nexport const renameEntry = (from: string, to: string): Promise<void> =>\n editorRequest('rename', { from, to });\n\n/** Upload binary/text `bytes` to a working-tree file at `path`. Rejects\n * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */\nexport const uploadFile = (path: string, bytes: Uint8Array): Promise<void> =>\n editorRequest('upload', { path, bytes });\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAgC;AA4BhC,MAAM,gBAAgB,OACpB,QACA,QACkB;AAClB,QAAM,MAAO,UAAM,qCAAgB,UAAU,QAAQ,CAAC,GAAG,CAAC;AAC1D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU,MAAM,SAAS;AAC/D,QAAI,OAAQ,KAAK,QAAqC;AACtD,UAAM;AAAA,EACR;AACF;AAQO,MAAM,eAAe,CAAC,SAAgC,cAAc,QAAQ,EAAE,KAAK,CAAC;AAyBpF,MAAM,aAAa,CAAC,SAAgC,cAAc,cAAc,EAAE,KAAK,CAAC;AAGxF,MAAM,eAAe,CAAC,SAC3B,cAAc,gBAAgB,EAAE,KAAK,CAAC;AAIjC,MAAM,cAAc,CAAC,SAAgC,cAAc,eAAe,EAAE,KAAK,CAAC;AAI1F,MAAM,cAAc,CAAC,MAAc,OACxC,cAAc,UAAU,EAAE,MAAM,GAAG,CAAC;AAI/B,MAAM,aAAa,CAAC,MAAc,UACvC,cAAc,UAAU,EAAE,MAAM,MAAM,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["import { protocolRequest } from './sandboxUtils';\n\n/**\n * Open a working-tree file in the immediately.run host editor (UI_AS_APPS_SPEC §4 —\n * the file explorer's click-to-open). This is an INTENT: the app asks, the HOST\n * validates the path and drives the CodeMirror editor — the editor itself stays\n * host-owned (§2 recursion boundary), so an app can never own or script it beyond\n * \"please show this file\".\n *\n * Requires the elevated `editor:open` capability — a previewed app does not hold it\n * (it must not move the host's focus), so only a system app whose binding grants it\n * (the file explorer) can call this; anyone else is refused at the gate.\n */\n\n/** An error from {@link openInEditor}, carrying a machine-readable `.code`. */\nexport interface EditorOpenError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:open`\n | 'not-found' // no such file in the live working tree (the host never creates)\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session to open files in\n | 'unknown';\n}\n\ntype EditorResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\nconst editorRequest = async (\n method: string,\n arg: Record<string, unknown>,\n): Promise<void> => {\n const res = (await protocolRequest('editor', method, [arg])) as EditorResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `editor ${method} failed`) as EditorWriteError;\n err.code = (res?.code as EditorWriteError['code']) ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to open `path` (a repo-relative working-tree path, e.g. `src/App.tsx`\n * or `/src/App.tsx`) in the editor. Resolves once the editor switches to it; rejects\n * with an {@link EditorOpenError} (`.code`) if the path is invalid, missing, or this\n * app may not open files.\n */\nexport const openInEditor = (path: string): Promise<void> => editorRequest('open', { path });\n\n/**\n * Where to land when entering the edit experience (EDITOR_FIRST_EDITING_SPEC §6\n * Delta A). v1 supports only an optional repo-relative `path` in the CURRENT repo\n * (self-scoped — the app you are already running; the host navigates within the\n * current route, never to another repo). A URI or `..` path is refused\n * `invalid-params`. Editing a file in one of your *mounts* (a space) is the\n * `edit-file` task, not this.\n */\nexport interface EditTarget {\n /** A repo-relative working-tree path in the current repo to focus once in edit\n * mode (e.g. `src/App.tsx`). Omit to edit the current route's entry. */\n path?: string;\n}\n\n/** An error from {@link requestEdit}, carrying a machine-readable `.code`. */\nexport interface RequestEditError extends Error {\n code:\n | 'read-only' // editing isn't possible here (a `ro` mount / anonymous viewer) — HIDE the affordance\n | 'forbidden' // the host refuses (e.g. a cross-repo / out-of-scope target)\n | 'invalid-params' // the target was malformed (URI / `..` / a non-current repo)\n | 'no-target' // there is no host editor session to enter\n | 'unknown';\n}\n\n/**\n * Ask the host to enter the **edit experience** for the app you are running —\n * the present→edit transition (`/present/...` → `/edit/...`) an app cannot make\n * itself. This is an INTENT (§2 recursion boundary): the app asks, the HOST\n * performs the visible, user-observable navigation and draws all editor chrome;\n * the app never navigates or paints chrome.\n *\n * Use it to offer an \"edit this\" affordance from a run/present-mode app that opens\n * the app's own source in the platform editor — instead of shipping a bespoke\n * in-app editor (EDITOR_FIRST_EDITING_SPEC §1).\n *\n * Resolves once the host begins the transition; rejects with a\n * {@link RequestEditError} (`.code`). Treat `read-only`/`forbidden` as \"editing is\n * not available — hide the affordance,\" never as an error to surface to the user.\n */\nexport const requestEdit = (target?: EditTarget): Promise<void> =>\n editorRequest('requestEdit', target ? { ...target } : {});\n\n// ---------------------------------------------------------------------------\n// Editor SESSION management (EDITOR_AS_APP_SPEC §5.1; editor-as-app plan Phase\n// 03). Unlike `openInEditor` (the explorer's cross-app intent, `editor:open`),\n// these drive the editor's OWN open-tab set + active file, so they are gated by\n// the editor app's `editor:document` capability — a file explorer holding only\n// `editor:open` cannot call them. The host re-validates the path against the live\n// working tree; the editor itself stays host-owned (§2 recursion boundary).\n// ---------------------------------------------------------------------------\n\n/** An error from a session intent ({@link setActiveFile} / {@link closeFile}),\n * carrying a machine-readable `.code`. */\nexport interface EditorSessionError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:document`\n | 'not-found' // no such file in the live working tree\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Switch the editor's active file to `path`, opening it (adding a tab) if it is\n * not already open — native `setActiveFile` parity. Rejects with an\n * {@link EditorSessionError} (`.code`) if the path is missing/invalid or this app\n * lacks `editor:document`. */\nexport const setActiveFile = (path: string): Promise<void> =>\n editorRequest('setActive', { path });\n\n/** Close `path`'s tab in the editor (remove it from the open set) — native\n * `closeFile` parity. Rejects with an {@link EditorSessionError} (`.code`). */\nexport const closeFile = (path: string): Promise<void> => editorRequest('close', { path });\n\n// ---------------------------------------------------------------------------\n// Working-tree mutation (UI_AS_APPS_SPEC §4 / EDITOR_AS_APP_SPEC §5.2). The file\n// explorer NAMES a working-tree path and the HOST performs the COW write (and\n// refreshes the preview) — the app holds no write port; it asks. Gated by the\n// first-party `editor:write` capability, so only a first-party chrome app (the\n// file explorer) can call these; anyone else is refused at the gate.\n// ---------------------------------------------------------------------------\n\n/** An error from a working-tree mutation, carrying a machine-readable `.code`. */\nexport interface EditorWriteError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:write` (first-party-only)\n | 'not-found' // the target file/folder does not exist (delete/rename)\n | 'exists' // the target already exists (create/rename would clobber)\n | 'protected' // the host refuses to delete this file (e.g. package.json)\n | 'too-large' // an upload exceeds the host's size limit\n | 'invalid-params' // a path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Create an empty working-tree file at `path` and open it. Rejects `exists` if a\n * file is already there. */\nexport const createFile = (path: string): Promise<void> => editorRequest('createFile', { path });\n\n/** Create a working-tree folder at `path` (materialised with a `.gitkeep`). */\nexport const createFolder = (path: string): Promise<void> =>\n editorRequest('createFolder', { path });\n\n/** Delete a working-tree file, or a folder and everything under it. Rejects\n * `protected` for files the host won't remove, `not-found` if absent. */\nexport const deleteEntry = (path: string): Promise<void> => editorRequest('deleteEntry', { path });\n\n/** Rename/move a working-tree file from `from` to `to`. Rejects `exists` if `to`\n * is taken, `not-found` if `from` is absent. */\nexport const renameEntry = (from: string, to: string): Promise<void> =>\n editorRequest('rename', { from, to });\n\n/** Upload binary/text `bytes` to a working-tree file at `path`. Rejects\n * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */\nexport const uploadFile = (path: string, bytes: Uint8Array): Promise<void> =>\n editorRequest('upload', { path, bytes });\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAgC;AA4BhC,MAAM,gBAAgB,OACpB,QACA,QACkB;AAClB,QAAM,MAAO,UAAM,qCAAgB,UAAU,QAAQ,CAAC,GAAG,CAAC;AAC1D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU,MAAM,SAAS;AAC/D,QAAI,OAAQ,KAAK,QAAqC;AACtD,UAAM;AAAA,EACR;AACF;AAQO,MAAM,eAAe,CAAC,SAAgC,cAAc,QAAQ,EAAE,KAAK,CAAC;AAyCpF,MAAM,cAAc,CAAC,WAC1B,cAAc,eAAe,SAAS,EAAE,GAAG,OAAO,IAAI,CAAC,CAAC;AA0BnD,MAAM,gBAAgB,CAAC,SAC5B,cAAc,aAAa,EAAE,KAAK,CAAC;AAI9B,MAAM,YAAY,CAAC,SAAgC,cAAc,SAAS,EAAE,KAAK,CAAC;AAyBlF,MAAM,aAAa,CAAC,SAAgC,cAAc,cAAc,EAAE,KAAK,CAAC;AAGxF,MAAM,eAAe,CAAC,SAC3B,cAAc,gBAAgB,EAAE,KAAK,CAAC;AAIjC,MAAM,cAAc,CAAC,SAAgC,cAAc,eAAe,EAAE,KAAK,CAAC;AAI1F,MAAM,cAAc,CAAC,MAAc,OACxC,cAAc,UAAU,EAAE,MAAM,GAAG,CAAC;AAI/B,MAAM,aAAa,CAAC,MAAc,UACvC,cAAc,UAAU,EAAE,MAAM,MAAM,CAAC;","names":[]}
package/dist/editor.d.cts CHANGED
@@ -20,6 +20,52 @@ interface EditorOpenError extends Error {
20
20
  * app may not open files.
21
21
  */
22
22
  declare const openInEditor: (path: string) => Promise<void>;
23
+ /**
24
+ * Where to land when entering the edit experience (EDITOR_FIRST_EDITING_SPEC §6
25
+ * Delta A). v1 supports only an optional repo-relative `path` in the CURRENT repo
26
+ * (self-scoped — the app you are already running; the host navigates within the
27
+ * current route, never to another repo). A URI or `..` path is refused
28
+ * `invalid-params`. Editing a file in one of your *mounts* (a space) is the
29
+ * `edit-file` task, not this.
30
+ */
31
+ interface EditTarget {
32
+ /** A repo-relative working-tree path in the current repo to focus once in edit
33
+ * mode (e.g. `src/App.tsx`). Omit to edit the current route's entry. */
34
+ path?: string;
35
+ }
36
+ /** An error from {@link requestEdit}, carrying a machine-readable `.code`. */
37
+ interface RequestEditError extends Error {
38
+ code: 'read-only' | 'forbidden' | 'invalid-params' | 'no-target' | 'unknown';
39
+ }
40
+ /**
41
+ * Ask the host to enter the **edit experience** for the app you are running —
42
+ * the present→edit transition (`/present/...` → `/edit/...`) an app cannot make
43
+ * itself. This is an INTENT (§2 recursion boundary): the app asks, the HOST
44
+ * performs the visible, user-observable navigation and draws all editor chrome;
45
+ * the app never navigates or paints chrome.
46
+ *
47
+ * Use it to offer an "edit this" affordance from a run/present-mode app that opens
48
+ * the app's own source in the platform editor — instead of shipping a bespoke
49
+ * in-app editor (EDITOR_FIRST_EDITING_SPEC §1).
50
+ *
51
+ * Resolves once the host begins the transition; rejects with a
52
+ * {@link RequestEditError} (`.code`). Treat `read-only`/`forbidden` as "editing is
53
+ * not available — hide the affordance," never as an error to surface to the user.
54
+ */
55
+ declare const requestEdit: (target?: EditTarget) => Promise<void>;
56
+ /** An error from a session intent ({@link setActiveFile} / {@link closeFile}),
57
+ * carrying a machine-readable `.code`. */
58
+ interface EditorSessionError extends Error {
59
+ code: 'forbidden' | 'not-found' | 'invalid-params' | 'no-target' | 'unknown';
60
+ }
61
+ /** Switch the editor's active file to `path`, opening it (adding a tab) if it is
62
+ * not already open — native `setActiveFile` parity. Rejects with an
63
+ * {@link EditorSessionError} (`.code`) if the path is missing/invalid or this app
64
+ * lacks `editor:document`. */
65
+ declare const setActiveFile: (path: string) => Promise<void>;
66
+ /** Close `path`'s tab in the editor (remove it from the open set) — native
67
+ * `closeFile` parity. Rejects with an {@link EditorSessionError} (`.code`). */
68
+ declare const closeFile: (path: string) => Promise<void>;
23
69
  /** An error from a working-tree mutation, carrying a machine-readable `.code`. */
24
70
  interface EditorWriteError extends Error {
25
71
  code: 'forbidden' | 'not-found' | 'exists' | 'protected' | 'too-large' | 'invalid-params' | 'no-target' | 'unknown';
@@ -39,4 +85,4 @@ declare const renameEntry: (from: string, to: string) => Promise<void>;
39
85
  * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */
40
86
  declare const uploadFile: (path: string, bytes: Uint8Array) => Promise<void>;
41
87
 
42
- export { type EditorOpenError, type EditorWriteError, createFile, createFolder, deleteEntry, openInEditor, renameEntry, uploadFile };
88
+ export { type EditTarget, type EditorOpenError, type EditorSessionError, type EditorWriteError, type RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile };
package/dist/editor.d.ts CHANGED
@@ -20,6 +20,52 @@ interface EditorOpenError extends Error {
20
20
  * app may not open files.
21
21
  */
22
22
  declare const openInEditor: (path: string) => Promise<void>;
23
+ /**
24
+ * Where to land when entering the edit experience (EDITOR_FIRST_EDITING_SPEC §6
25
+ * Delta A). v1 supports only an optional repo-relative `path` in the CURRENT repo
26
+ * (self-scoped — the app you are already running; the host navigates within the
27
+ * current route, never to another repo). A URI or `..` path is refused
28
+ * `invalid-params`. Editing a file in one of your *mounts* (a space) is the
29
+ * `edit-file` task, not this.
30
+ */
31
+ interface EditTarget {
32
+ /** A repo-relative working-tree path in the current repo to focus once in edit
33
+ * mode (e.g. `src/App.tsx`). Omit to edit the current route's entry. */
34
+ path?: string;
35
+ }
36
+ /** An error from {@link requestEdit}, carrying a machine-readable `.code`. */
37
+ interface RequestEditError extends Error {
38
+ code: 'read-only' | 'forbidden' | 'invalid-params' | 'no-target' | 'unknown';
39
+ }
40
+ /**
41
+ * Ask the host to enter the **edit experience** for the app you are running —
42
+ * the present→edit transition (`/present/...` → `/edit/...`) an app cannot make
43
+ * itself. This is an INTENT (§2 recursion boundary): the app asks, the HOST
44
+ * performs the visible, user-observable navigation and draws all editor chrome;
45
+ * the app never navigates or paints chrome.
46
+ *
47
+ * Use it to offer an "edit this" affordance from a run/present-mode app that opens
48
+ * the app's own source in the platform editor — instead of shipping a bespoke
49
+ * in-app editor (EDITOR_FIRST_EDITING_SPEC §1).
50
+ *
51
+ * Resolves once the host begins the transition; rejects with a
52
+ * {@link RequestEditError} (`.code`). Treat `read-only`/`forbidden` as "editing is
53
+ * not available — hide the affordance," never as an error to surface to the user.
54
+ */
55
+ declare const requestEdit: (target?: EditTarget) => Promise<void>;
56
+ /** An error from a session intent ({@link setActiveFile} / {@link closeFile}),
57
+ * carrying a machine-readable `.code`. */
58
+ interface EditorSessionError extends Error {
59
+ code: 'forbidden' | 'not-found' | 'invalid-params' | 'no-target' | 'unknown';
60
+ }
61
+ /** Switch the editor's active file to `path`, opening it (adding a tab) if it is
62
+ * not already open — native `setActiveFile` parity. Rejects with an
63
+ * {@link EditorSessionError} (`.code`) if the path is missing/invalid or this app
64
+ * lacks `editor:document`. */
65
+ declare const setActiveFile: (path: string) => Promise<void>;
66
+ /** Close `path`'s tab in the editor (remove it from the open set) — native
67
+ * `closeFile` parity. Rejects with an {@link EditorSessionError} (`.code`). */
68
+ declare const closeFile: (path: string) => Promise<void>;
23
69
  /** An error from a working-tree mutation, carrying a machine-readable `.code`. */
24
70
  interface EditorWriteError extends Error {
25
71
  code: 'forbidden' | 'not-found' | 'exists' | 'protected' | 'too-large' | 'invalid-params' | 'no-target' | 'unknown';
@@ -39,4 +85,4 @@ declare const renameEntry: (from: string, to: string) => Promise<void>;
39
85
  * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */
40
86
  declare const uploadFile: (path: string, bytes: Uint8Array) => Promise<void>;
41
87
 
42
- export { type EditorOpenError, type EditorWriteError, createFile, createFolder, deleteEntry, openInEditor, renameEntry, uploadFile };
88
+ export { type EditTarget, type EditorOpenError, type EditorSessionError, type EditorWriteError, type RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile };
package/dist/editor.js CHANGED
@@ -8,17 +8,23 @@ const editorRequest = async (method, arg) => {
8
8
  }
9
9
  };
10
10
  const openInEditor = (path) => editorRequest("open", { path });
11
+ const requestEdit = (target) => editorRequest("requestEdit", target ? { ...target } : {});
12
+ const setActiveFile = (path) => editorRequest("setActive", { path });
13
+ const closeFile = (path) => editorRequest("close", { path });
11
14
  const createFile = (path) => editorRequest("createFile", { path });
12
15
  const createFolder = (path) => editorRequest("createFolder", { path });
13
16
  const deleteEntry = (path) => editorRequest("deleteEntry", { path });
14
17
  const renameEntry = (from, to) => editorRequest("rename", { from, to });
15
18
  const uploadFile = (path, bytes) => editorRequest("upload", { path, bytes });
16
19
  export {
20
+ closeFile,
17
21
  createFile,
18
22
  createFolder,
19
23
  deleteEntry,
20
24
  openInEditor,
21
25
  renameEntry,
26
+ requestEdit,
27
+ setActiveFile,
22
28
  uploadFile
23
29
  };
24
30
  //# sourceMappingURL=editor.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["import { protocolRequest } from './sandboxUtils';\n\n/**\n * Open a working-tree file in the immediately.run host editor (UI_AS_APPS_SPEC §4 —\n * the file explorer's click-to-open). This is an INTENT: the app asks, the HOST\n * validates the path and drives the CodeMirror editor — the editor itself stays\n * host-owned (§2 recursion boundary), so an app can never own or script it beyond\n * \"please show this file\".\n *\n * Requires the elevated `editor:open` capability — a previewed app does not hold it\n * (it must not move the host's focus), so only a system app whose binding grants it\n * (the file explorer) can call this; anyone else is refused at the gate.\n */\n\n/** An error from {@link openInEditor}, carrying a machine-readable `.code`. */\nexport interface EditorOpenError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:open`\n | 'not-found' // no such file in the live working tree (the host never creates)\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session to open files in\n | 'unknown';\n}\n\ntype EditorResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\nconst editorRequest = async (\n method: string,\n arg: Record<string, unknown>,\n): Promise<void> => {\n const res = (await protocolRequest('editor', method, [arg])) as EditorResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `editor ${method} failed`) as EditorWriteError;\n err.code = (res?.code as EditorWriteError['code']) ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to open `path` (a repo-relative working-tree path, e.g. `src/App.tsx`\n * or `/src/App.tsx`) in the editor. Resolves once the editor switches to it; rejects\n * with an {@link EditorOpenError} (`.code`) if the path is invalid, missing, or this\n * app may not open files.\n */\nexport const openInEditor = (path: string): Promise<void> => editorRequest('open', { path });\n\n// ---------------------------------------------------------------------------\n// Working-tree mutation (UI_AS_APPS_SPEC §4 / EDITOR_AS_APP_SPEC §5.2). The file\n// explorer NAMES a working-tree path and the HOST performs the COW write (and\n// refreshes the preview) — the app holds no write port; it asks. Gated by the\n// first-party `editor:write` capability, so only a first-party chrome app (the\n// file explorer) can call these; anyone else is refused at the gate.\n// ---------------------------------------------------------------------------\n\n/** An error from a working-tree mutation, carrying a machine-readable `.code`. */\nexport interface EditorWriteError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:write` (first-party-only)\n | 'not-found' // the target file/folder does not exist (delete/rename)\n | 'exists' // the target already exists (create/rename would clobber)\n | 'protected' // the host refuses to delete this file (e.g. package.json)\n | 'too-large' // an upload exceeds the host's size limit\n | 'invalid-params' // a path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Create an empty working-tree file at `path` and open it. Rejects `exists` if a\n * file is already there. */\nexport const createFile = (path: string): Promise<void> => editorRequest('createFile', { path });\n\n/** Create a working-tree folder at `path` (materialised with a `.gitkeep`). */\nexport const createFolder = (path: string): Promise<void> =>\n editorRequest('createFolder', { path });\n\n/** Delete a working-tree file, or a folder and everything under it. Rejects\n * `protected` for files the host won't remove, `not-found` if absent. */\nexport const deleteEntry = (path: string): Promise<void> => editorRequest('deleteEntry', { path });\n\n/** Rename/move a working-tree file from `from` to `to`. Rejects `exists` if `to`\n * is taken, `not-found` if `from` is absent. */\nexport const renameEntry = (from: string, to: string): Promise<void> =>\n editorRequest('rename', { from, to });\n\n/** Upload binary/text `bytes` to a working-tree file at `path`. Rejects\n * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */\nexport const uploadFile = (path: string, bytes: Uint8Array): Promise<void> =>\n editorRequest('upload', { path, bytes });\n"],"mappings":"AAAA,SAAS,uBAAuB;AA4BhC,MAAM,gBAAgB,OACpB,QACA,QACkB;AAClB,QAAM,MAAO,MAAM,gBAAgB,UAAU,QAAQ,CAAC,GAAG,CAAC;AAC1D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU,MAAM,SAAS;AAC/D,QAAI,OAAQ,KAAK,QAAqC;AACtD,UAAM;AAAA,EACR;AACF;AAQO,MAAM,eAAe,CAAC,SAAgC,cAAc,QAAQ,EAAE,KAAK,CAAC;AAyBpF,MAAM,aAAa,CAAC,SAAgC,cAAc,cAAc,EAAE,KAAK,CAAC;AAGxF,MAAM,eAAe,CAAC,SAC3B,cAAc,gBAAgB,EAAE,KAAK,CAAC;AAIjC,MAAM,cAAc,CAAC,SAAgC,cAAc,eAAe,EAAE,KAAK,CAAC;AAI1F,MAAM,cAAc,CAAC,MAAc,OACxC,cAAc,UAAU,EAAE,MAAM,GAAG,CAAC;AAI/B,MAAM,aAAa,CAAC,MAAc,UACvC,cAAc,UAAU,EAAE,MAAM,MAAM,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["import { protocolRequest } from './sandboxUtils';\n\n/**\n * Open a working-tree file in the immediately.run host editor (UI_AS_APPS_SPEC §4 —\n * the file explorer's click-to-open). This is an INTENT: the app asks, the HOST\n * validates the path and drives the CodeMirror editor — the editor itself stays\n * host-owned (§2 recursion boundary), so an app can never own or script it beyond\n * \"please show this file\".\n *\n * Requires the elevated `editor:open` capability — a previewed app does not hold it\n * (it must not move the host's focus), so only a system app whose binding grants it\n * (the file explorer) can call this; anyone else is refused at the gate.\n */\n\n/** An error from {@link openInEditor}, carrying a machine-readable `.code`. */\nexport interface EditorOpenError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:open`\n | 'not-found' // no such file in the live working tree (the host never creates)\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session to open files in\n | 'unknown';\n}\n\ntype EditorResult =\n | { ok: true; data: unknown }\n | { ok: false; code: string; message: string };\n\nconst editorRequest = async (\n method: string,\n arg: Record<string, unknown>,\n): Promise<void> => {\n const res = (await protocolRequest('editor', method, [arg])) as EditorResult;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `editor ${method} failed`) as EditorWriteError;\n err.code = (res?.code as EditorWriteError['code']) ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to open `path` (a repo-relative working-tree path, e.g. `src/App.tsx`\n * or `/src/App.tsx`) in the editor. Resolves once the editor switches to it; rejects\n * with an {@link EditorOpenError} (`.code`) if the path is invalid, missing, or this\n * app may not open files.\n */\nexport const openInEditor = (path: string): Promise<void> => editorRequest('open', { path });\n\n/**\n * Where to land when entering the edit experience (EDITOR_FIRST_EDITING_SPEC §6\n * Delta A). v1 supports only an optional repo-relative `path` in the CURRENT repo\n * (self-scoped — the app you are already running; the host navigates within the\n * current route, never to another repo). A URI or `..` path is refused\n * `invalid-params`. Editing a file in one of your *mounts* (a space) is the\n * `edit-file` task, not this.\n */\nexport interface EditTarget {\n /** A repo-relative working-tree path in the current repo to focus once in edit\n * mode (e.g. `src/App.tsx`). Omit to edit the current route's entry. */\n path?: string;\n}\n\n/** An error from {@link requestEdit}, carrying a machine-readable `.code`. */\nexport interface RequestEditError extends Error {\n code:\n | 'read-only' // editing isn't possible here (a `ro` mount / anonymous viewer) — HIDE the affordance\n | 'forbidden' // the host refuses (e.g. a cross-repo / out-of-scope target)\n | 'invalid-params' // the target was malformed (URI / `..` / a non-current repo)\n | 'no-target' // there is no host editor session to enter\n | 'unknown';\n}\n\n/**\n * Ask the host to enter the **edit experience** for the app you are running —\n * the present→edit transition (`/present/...` → `/edit/...`) an app cannot make\n * itself. This is an INTENT (§2 recursion boundary): the app asks, the HOST\n * performs the visible, user-observable navigation and draws all editor chrome;\n * the app never navigates or paints chrome.\n *\n * Use it to offer an \"edit this\" affordance from a run/present-mode app that opens\n * the app's own source in the platform editor — instead of shipping a bespoke\n * in-app editor (EDITOR_FIRST_EDITING_SPEC §1).\n *\n * Resolves once the host begins the transition; rejects with a\n * {@link RequestEditError} (`.code`). Treat `read-only`/`forbidden` as \"editing is\n * not available — hide the affordance,\" never as an error to surface to the user.\n */\nexport const requestEdit = (target?: EditTarget): Promise<void> =>\n editorRequest('requestEdit', target ? { ...target } : {});\n\n// ---------------------------------------------------------------------------\n// Editor SESSION management (EDITOR_AS_APP_SPEC §5.1; editor-as-app plan Phase\n// 03). Unlike `openInEditor` (the explorer's cross-app intent, `editor:open`),\n// these drive the editor's OWN open-tab set + active file, so they are gated by\n// the editor app's `editor:document` capability — a file explorer holding only\n// `editor:open` cannot call them. The host re-validates the path against the live\n// working tree; the editor itself stays host-owned (§2 recursion boundary).\n// ---------------------------------------------------------------------------\n\n/** An error from a session intent ({@link setActiveFile} / {@link closeFile}),\n * carrying a machine-readable `.code`. */\nexport interface EditorSessionError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:document`\n | 'not-found' // no such file in the live working tree\n | 'invalid-params' // the path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Switch the editor's active file to `path`, opening it (adding a tab) if it is\n * not already open — native `setActiveFile` parity. Rejects with an\n * {@link EditorSessionError} (`.code`) if the path is missing/invalid or this app\n * lacks `editor:document`. */\nexport const setActiveFile = (path: string): Promise<void> =>\n editorRequest('setActive', { path });\n\n/** Close `path`'s tab in the editor (remove it from the open set) — native\n * `closeFile` parity. Rejects with an {@link EditorSessionError} (`.code`). */\nexport const closeFile = (path: string): Promise<void> => editorRequest('close', { path });\n\n// ---------------------------------------------------------------------------\n// Working-tree mutation (UI_AS_APPS_SPEC §4 / EDITOR_AS_APP_SPEC §5.2). The file\n// explorer NAMES a working-tree path and the HOST performs the COW write (and\n// refreshes the preview) — the app holds no write port; it asks. Gated by the\n// first-party `editor:write` capability, so only a first-party chrome app (the\n// file explorer) can call these; anyone else is refused at the gate.\n// ---------------------------------------------------------------------------\n\n/** An error from a working-tree mutation, carrying a machine-readable `.code`. */\nexport interface EditorWriteError extends Error {\n code:\n | 'forbidden' // the frame lacks `editor:write` (first-party-only)\n | 'not-found' // the target file/folder does not exist (delete/rename)\n | 'exists' // the target already exists (create/rename would clobber)\n | 'protected' // the host refuses to delete this file (e.g. package.json)\n | 'too-large' // an upload exceeds the host's size limit\n | 'invalid-params' // a path was empty / contained `..` / looked like a URI\n | 'no-target' // there is no host editor session\n | 'unknown';\n}\n\n/** Create an empty working-tree file at `path` and open it. Rejects `exists` if a\n * file is already there. */\nexport const createFile = (path: string): Promise<void> => editorRequest('createFile', { path });\n\n/** Create a working-tree folder at `path` (materialised with a `.gitkeep`). */\nexport const createFolder = (path: string): Promise<void> =>\n editorRequest('createFolder', { path });\n\n/** Delete a working-tree file, or a folder and everything under it. Rejects\n * `protected` for files the host won't remove, `not-found` if absent. */\nexport const deleteEntry = (path: string): Promise<void> => editorRequest('deleteEntry', { path });\n\n/** Rename/move a working-tree file from `from` to `to`. Rejects `exists` if `to`\n * is taken, `not-found` if `from` is absent. */\nexport const renameEntry = (from: string, to: string): Promise<void> =>\n editorRequest('rename', { from, to });\n\n/** Upload binary/text `bytes` to a working-tree file at `path`. Rejects\n * `too-large` past the host's size limit. The bytes are transferred (zero-copy). */\nexport const uploadFile = (path: string, bytes: Uint8Array): Promise<void> =>\n editorRequest('upload', { path, bytes });\n"],"mappings":"AAAA,SAAS,uBAAuB;AA4BhC,MAAM,gBAAgB,OACpB,QACA,QACkB;AAClB,QAAM,MAAO,MAAM,gBAAgB,UAAU,QAAQ,CAAC,GAAG,CAAC;AAC1D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU,MAAM,SAAS;AAC/D,QAAI,OAAQ,KAAK,QAAqC;AACtD,UAAM;AAAA,EACR;AACF;AAQO,MAAM,eAAe,CAAC,SAAgC,cAAc,QAAQ,EAAE,KAAK,CAAC;AAyCpF,MAAM,cAAc,CAAC,WAC1B,cAAc,eAAe,SAAS,EAAE,GAAG,OAAO,IAAI,CAAC,CAAC;AA0BnD,MAAM,gBAAgB,CAAC,SAC5B,cAAc,aAAa,EAAE,KAAK,CAAC;AAI9B,MAAM,YAAY,CAAC,SAAgC,cAAc,SAAS,EAAE,KAAK,CAAC;AAyBlF,MAAM,aAAa,CAAC,SAAgC,cAAc,cAAc,EAAE,KAAK,CAAC;AAGxF,MAAM,eAAe,CAAC,SAC3B,cAAc,gBAAgB,EAAE,KAAK,CAAC;AAIjC,MAAM,cAAc,CAAC,SAAgC,cAAc,eAAe,EAAE,KAAK,CAAC;AAI1F,MAAM,cAAc,CAAC,MAAc,OACxC,cAAc,UAAU,EAAE,MAAM,GAAG,CAAC;AAI/B,MAAM,aAAa,CAAC,MAAc,UACvC,cAAc,UAAU,EAAE,MAAM,MAAM,CAAC;","names":[]}
package/dist/index.cjs CHANGED
@@ -34,6 +34,8 @@ __reExport(index_exports, require("./netFetch"), module.exports);
34
34
  __reExport(index_exports, require("./secrets"), module.exports);
35
35
  __reExport(index_exports, require("./tasks"), module.exports);
36
36
  __reExport(index_exports, require("./runtime"), module.exports);
37
+ __reExport(index_exports, require("./irMarkers"), module.exports);
38
+ __reExport(index_exports, require("./ready"), module.exports);
37
39
  __reExport(index_exports, require("./protocolStream"), module.exports);
38
40
  __reExport(index_exports, require("./sandboxTypes"), module.exports);
39
41
  // Annotate the CommonJS export names for ESM import in node:
@@ -57,6 +59,8 @@ __reExport(index_exports, require("./sandboxTypes"), module.exports);
57
59
  ...require("./secrets"),
58
60
  ...require("./tasks"),
59
61
  ...require("./runtime"),
62
+ ...require("./irMarkers"),
63
+ ...require("./ready"),
60
64
  ...require("./protocolStream"),
61
65
  ...require("./sandboxTypes")
62
66
  });
@@ -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 './netFetch';\nexport * from './secrets';\nexport * from './tasks';\nexport * from './runtime';\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,uBAfd;AAgBA,0BAAc,sBAhBd;AAiBA,0BAAc,oBAjBd;AAkBA,0BAAc,sBAlBd;AAmBA,0BAAc,6BAnBd;AAoBA,0BAAc,2BApBd;","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 './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\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,uBAfd;AAgBA,0BAAc,sBAhBd;AAiBA,0BAAc,oBAjBd;AAkBA,0BAAc,sBAlBd;AAmBA,0BAAc,wBAnBd;AAoBA,0BAAc,oBApBd;AAqBA,0BAAc,6BArBd;AAsBA,0BAAc,2BAtBd;","names":[]}
package/dist/index.d.cts CHANGED
@@ -7,16 +7,18 @@ export { useFileMetadata, useMetadataQuery } from './hooks.cjs';
7
7
  export { AuthState, AuthStatus, SandboxUser, getAuthState, onAuthChange, useAuth } from './auth.cjs';
8
8
  export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme } from './theme.cjs';
9
9
  export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.cjs';
10
- export { EditorOpenError, EditorWriteError, createFile, createFolder, deleteEntry, openInEditor, renameEntry, uploadFile } from './editor.cjs';
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 { GrantRecord, Member, MountQuery, MountRemoveReason, RemovedMount, ResolvedUser, Role, SandboxMount, SpaceError, SpaceInfo, createSpace, findMount, getAppMountPath, getMounts, getSpaceMembers, listAllSpaces, listGrants, listSpaces, lookupUser, mount, mountSpace, onMountsChange, openAppSpace, requestMount, requestSpace, revokeGrant, setSpaceRole, shareSpace, unmountSpace, unshareSpace, useMounts, waitForMount } from './mounts.cjs';
12
+ 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
13
  export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.cjs';
14
14
  export { ApiMethod, getCatalog, invoke, invokeStream, onCatalogChange, useCatalog } from './catalog.cjs';
15
15
  export { RegionMessage, onRegionMessage, postToRegion, useRegionMessage } from './ipc.cjs';
16
16
  export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.cjs';
17
17
  export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.cjs';
18
- export { FileCap, TaskInput, cancelTask, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.cjs';
18
+ export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.cjs';
19
19
  export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.cjs';
20
+ export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.cjs';
21
+ export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.cjs';
20
22
  export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.cjs';
21
23
  export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.cjs';
22
24
  export { ImmediatelyRunGlobal, getHostRuntime } from './hostRuntime.cjs';
package/dist/index.d.ts CHANGED
@@ -7,16 +7,18 @@ export { useFileMetadata, useMetadataQuery } from './hooks.js';
7
7
  export { AuthState, AuthStatus, SandboxUser, getAuthState, onAuthChange, useAuth } from './auth.js';
8
8
  export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme } from './theme.js';
9
9
  export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.js';
10
- export { EditorOpenError, EditorWriteError, createFile, createFolder, deleteEntry, openInEditor, renameEntry, uploadFile } from './editor.js';
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 { GrantRecord, Member, MountQuery, MountRemoveReason, RemovedMount, ResolvedUser, Role, SandboxMount, SpaceError, SpaceInfo, createSpace, findMount, getAppMountPath, getMounts, getSpaceMembers, listAllSpaces, listGrants, listSpaces, lookupUser, mount, mountSpace, onMountsChange, openAppSpace, requestMount, requestSpace, revokeGrant, setSpaceRole, shareSpace, unmountSpace, unshareSpace, useMounts, waitForMount } from './mounts.js';
12
+ 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
13
  export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.js';
14
14
  export { ApiMethod, getCatalog, invoke, invokeStream, onCatalogChange, useCatalog } from './catalog.js';
15
15
  export { RegionMessage, onRegionMessage, postToRegion, useRegionMessage } from './ipc.js';
16
16
  export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.js';
17
17
  export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.js';
18
- export { FileCap, TaskInput, cancelTask, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.js';
18
+ export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask, getTaskInput, invokeTask, useTaskInput } from './tasks.js';
19
19
  export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.js';
20
+ export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.js';
21
+ export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.js';
20
22
  export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.js';
21
23
  export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.js';
22
24
  export { ImmediatelyRunGlobal, getHostRuntime } from './hostRuntime.js';
package/dist/index.js CHANGED
@@ -17,6 +17,8 @@ export * from "./netFetch";
17
17
  export * from "./secrets";
18
18
  export * from "./tasks";
19
19
  export * from "./runtime";
20
+ export * from "./irMarkers";
21
+ export * from "./ready";
20
22
  export * from "./protocolStream";
21
23
  export * from "./sandboxTypes";
22
24
  //# sourceMappingURL=index.js.map
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 './netFetch';\nexport * from './secrets';\nexport * from './tasks';\nexport * from './runtime';\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;","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 './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\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;","names":[]}
@@ -0,0 +1,49 @@
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 injectedBundler_exports = {};
20
+ __export(injectedBundler_exports, {
21
+ getInjectedMetadataEmitter: () => getInjectedMetadataEmitter,
22
+ resolveMetadataSource: () => resolveMetadataSource
23
+ });
24
+ module.exports = __toCommonJS(injectedBundler_exports);
25
+ const injectedBundler = () => {
26
+ try {
27
+ return module?.evaluation?.module?.bundler ?? null;
28
+ } catch {
29
+ return null;
30
+ }
31
+ };
32
+ const getInjectedMetadataEmitter = () => {
33
+ const b = injectedBundler();
34
+ if (b && typeof b.onMetadataChange === "function" && b.onMetadataChangeEmitter) {
35
+ return {
36
+ onMetadataChange: b.onMetadataChange,
37
+ enable: () => b.onMetadataChangeEmitter.enable()
38
+ };
39
+ }
40
+ return null;
41
+ };
42
+ const resolveMetadataSource = (injected) => injected ? { event: injected.onMetadataChange, enable: () => injected.enable() } : { event: void 0, enable: () => {
43
+ } };
44
+ // Annotate the CommonJS export names for ESM import in node:
45
+ 0 && (module.exports = {
46
+ getInjectedMetadataEmitter,
47
+ resolveMetadataSource
48
+ });
49
+ //# sourceMappingURL=injectedBundler.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (\n injected: InjectedMetadataEmitter | null,\n): MetadataSource =>\n injected\n ? { event: injected.onMetadataChange, enable: () => injected.enable() }\n : { event: undefined, enable: () => {} };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBA,MAAM,kBAAkB,MAAkB;AACxC,MAAI;AAEF,WAAO,QAAQ,YAAY,QAAQ,WAAW;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,MAAM,6BAA6B,MAAsC;AAC9E,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,qBAAqB,cAAc,EAAE,yBAAyB;AAC9E,WAAO;AAAA,MACL,kBAAkB,EAAE;AAAA,MACpB,QAAQ,MAAM,EAAE,wBAAwB,OAAO;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AACT;AAgBO,MAAM,wBAAwB,CACnC,aAEA,WACI,EAAE,OAAO,SAAS,kBAAkB,QAAQ,MAAM,SAAS,OAAO,EAAE,IACpE,EAAE,OAAO,QAAW,QAAQ,MAAM;AAAC,EAAE;","names":[]}
@@ -0,0 +1,29 @@
1
+ /** vscode-style Event source: subscribe with a listener, get a disposable back. */
2
+ type EventSource = (listener: (msg: any) => void) => {
3
+ dispose(): void;
4
+ };
5
+ /** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`
6
+ * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */
7
+ interface InjectedMetadataEmitter {
8
+ onMetadataChange: EventSource;
9
+ /** Start the DelayedEmitter once a subscriber is attached (injected path only). */
10
+ enable(): void;
11
+ }
12
+ /** The injected bundler's metadata emitter, or null when npm-fetched. */
13
+ declare const getInjectedMetadataEmitter: () => InjectedMetadataEmitter | null;
14
+ /** What `boot` needs to subscribe to metadata updates: the `event` source to hand
15
+ * `addListener` (the injected emitter, or `undefined` → listen over the transport)
16
+ * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */
17
+ interface MetadataSource {
18
+ event?: EventSource;
19
+ enable(): void;
20
+ }
21
+ /**
22
+ * Resolve the metadata-update subscription source (PURE — the dual-mode decision).
23
+ * With the injected emitter: use it and arm it, so the live path is byte-identical.
24
+ * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls
25
+ * back to the §4 transport's `onMessage`, and `enable` is a no-op.
26
+ */
27
+ declare const resolveMetadataSource: (injected: InjectedMetadataEmitter | null) => MetadataSource;
28
+
29
+ export { type EventSource, type InjectedMetadataEmitter, type MetadataSource, getInjectedMetadataEmitter, resolveMetadataSource };
@@ -0,0 +1,29 @@
1
+ /** vscode-style Event source: subscribe with a listener, get a disposable back. */
2
+ type EventSource = (listener: (msg: any) => void) => {
3
+ dispose(): void;
4
+ };
5
+ /** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`
6
+ * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */
7
+ interface InjectedMetadataEmitter {
8
+ onMetadataChange: EventSource;
9
+ /** Start the DelayedEmitter once a subscriber is attached (injected path only). */
10
+ enable(): void;
11
+ }
12
+ /** The injected bundler's metadata emitter, or null when npm-fetched. */
13
+ declare const getInjectedMetadataEmitter: () => InjectedMetadataEmitter | null;
14
+ /** What `boot` needs to subscribe to metadata updates: the `event` source to hand
15
+ * `addListener` (the injected emitter, or `undefined` → listen over the transport)
16
+ * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */
17
+ interface MetadataSource {
18
+ event?: EventSource;
19
+ enable(): void;
20
+ }
21
+ /**
22
+ * Resolve the metadata-update subscription source (PURE — the dual-mode decision).
23
+ * With the injected emitter: use it and arm it, so the live path is byte-identical.
24
+ * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls
25
+ * back to the §4 transport's `onMessage`, and `enable` is a no-op.
26
+ */
27
+ declare const resolveMetadataSource: (injected: InjectedMetadataEmitter | null) => MetadataSource;
28
+
29
+ export { type EventSource, type InjectedMetadataEmitter, type MetadataSource, getInjectedMetadataEmitter, resolveMetadataSource };