@immediately-run/sdk 0.15.0 → 0.16.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 (116) hide show
  1. package/README.md +27 -3
  2. package/dist/MDXProvider.cjs.map +1 -1
  3. package/dist/MDXProvider.d.cts +4 -0
  4. package/dist/MDXProvider.d.ts +4 -0
  5. package/dist/MDXProvider.js.map +1 -1
  6. package/dist/RoutingSpec.cjs.map +1 -1
  7. package/dist/RoutingSpec.d.cts +20 -3
  8. package/dist/RoutingSpec.d.ts +20 -3
  9. package/dist/auth.cjs.map +1 -1
  10. package/dist/auth.d.cts +2 -0
  11. package/dist/auth.d.ts +2 -0
  12. package/dist/auth.js.map +1 -1
  13. package/dist/boot.cjs +17 -7
  14. package/dist/boot.cjs.map +1 -1
  15. package/dist/boot.d.cts +28 -4
  16. package/dist/boot.d.ts +28 -4
  17. package/dist/boot.js +16 -7
  18. package/dist/boot.js.map +1 -1
  19. package/dist/components/Include.cjs.map +1 -1
  20. package/dist/components/Include.d.cts +7 -0
  21. package/dist/components/Include.d.ts +7 -0
  22. package/dist/components/Include.js.map +1 -1
  23. package/dist/components/MDXComponents.cjs.map +1 -1
  24. package/dist/components/MDXComponents.d.cts +6 -0
  25. package/dist/components/MDXComponents.d.ts +6 -0
  26. package/dist/components/MDXComponents.js.map +1 -1
  27. package/dist/components/Routes.cjs +59 -0
  28. package/dist/components/Routes.cjs.map +1 -0
  29. package/dist/components/Routes.d.cts +34 -0
  30. package/dist/components/Routes.d.ts +34 -0
  31. package/dist/components/Routes.js +34 -0
  32. package/dist/components/Routes.js.map +1 -0
  33. package/dist/contribute.cjs.map +1 -1
  34. package/dist/contribute.d.cts +2 -0
  35. package/dist/contribute.d.ts +2 -0
  36. package/dist/contribute.js.map +1 -1
  37. package/dist/diagnostics.cjs.map +1 -1
  38. package/dist/diagnostics.d.cts +3 -0
  39. package/dist/diagnostics.d.ts +3 -0
  40. package/dist/diagnostics.js.map +1 -1
  41. package/dist/formFactor.cjs.map +1 -1
  42. package/dist/formFactor.d.cts +2 -0
  43. package/dist/formFactor.d.ts +2 -0
  44. package/dist/formFactor.js.map +1 -1
  45. package/dist/hooks.cjs +27 -28
  46. package/dist/hooks.cjs.map +1 -1
  47. package/dist/hooks.d.cts +39 -4
  48. package/dist/hooks.d.ts +39 -4
  49. package/dist/hooks.js +27 -29
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.cjs +4 -0
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.d.cts +6 -4
  54. package/dist/index.d.ts +6 -4
  55. package/dist/index.js +2 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/irMarkers.cjs.map +1 -1
  58. package/dist/irMarkers.d.cts +1 -0
  59. package/dist/irMarkers.d.ts +1 -0
  60. package/dist/irMarkers.js.map +1 -1
  61. package/dist/llm.cjs.map +1 -1
  62. package/dist/llm.d.cts +5 -0
  63. package/dist/llm.d.ts +5 -0
  64. package/dist/llm.js.map +1 -1
  65. package/dist/loading.cjs +186 -0
  66. package/dist/loading.cjs.map +1 -0
  67. package/dist/loading.d.cts +48 -0
  68. package/dist/loading.d.ts +48 -0
  69. package/dist/loading.js +162 -0
  70. package/dist/loading.js.map +1 -0
  71. package/dist/mounts.cjs.map +1 -1
  72. package/dist/mounts.d.cts +3 -1
  73. package/dist/mounts.d.ts +3 -1
  74. package/dist/mounts.js.map +1 -1
  75. package/dist/netFetch.cjs.map +1 -1
  76. package/dist/netFetch.d.cts +2 -0
  77. package/dist/netFetch.d.ts +2 -0
  78. package/dist/netFetch.js.map +1 -1
  79. package/dist/onFsChange.cjs.map +1 -1
  80. package/dist/onFsChange.d.cts +1 -0
  81. package/dist/onFsChange.d.ts +1 -0
  82. package/dist/onFsChange.js.map +1 -1
  83. package/dist/protocolStream.cjs.map +1 -1
  84. package/dist/protocolStream.d.cts +3 -0
  85. package/dist/protocolStream.d.ts +3 -0
  86. package/dist/protocolStream.js.map +1 -1
  87. package/dist/ready.cjs.map +1 -1
  88. package/dist/ready.d.cts +7 -0
  89. package/dist/ready.d.ts +7 -0
  90. package/dist/ready.js.map +1 -1
  91. package/dist/routeMatch.cjs +72 -0
  92. package/dist/routeMatch.cjs.map +1 -0
  93. package/dist/routeMatch.d.cts +19 -0
  94. package/dist/routeMatch.d.ts +19 -0
  95. package/dist/routeMatch.js +46 -0
  96. package/dist/routeMatch.js.map +1 -0
  97. package/dist/routing.cjs +35 -14
  98. package/dist/routing.cjs.map +1 -1
  99. package/dist/routing.d.cts +33 -4
  100. package/dist/routing.d.ts +33 -4
  101. package/dist/routing.js +32 -14
  102. package/dist/routing.js.map +1 -1
  103. package/dist/runtime.cjs.map +1 -1
  104. package/dist/runtime.d.cts +1 -0
  105. package/dist/runtime.d.ts +1 -0
  106. package/dist/runtime.js.map +1 -1
  107. package/dist/sandboxTypes.cjs.map +1 -1
  108. package/dist/sandboxTypes.d.cts +30 -7
  109. package/dist/sandboxTypes.d.ts +30 -7
  110. package/dist/version.cjs +1 -1
  111. package/dist/version.cjs.map +1 -1
  112. package/dist/version.d.cts +1 -1
  113. package/dist/version.d.ts +1 -1
  114. package/dist/version.js +1 -1
  115. package/dist/version.js.map +1 -1
  116. package/package.json +6 -2
package/README.md CHANGED
@@ -21,7 +21,11 @@ also reachable via subpaths (`@immediately-run/sdk/boot`, `@immediately-run/sdk/
21
21
  - `boot` — entry point that mounts an immediately.run app into the sandbox.
22
22
  - `Include` (`components/Include`) — render another file's exported component inline.
23
23
  - `MDXComponents` (`Link`, …) — MDX component overrides.
24
- - `useMetadataQuery`, `useFileMetadata` (`hooks`) — query files by frontmatter metadata.
24
+ - `useMetadataQuery`, `useFileMetadata`, `useAllMetadata` (`hooks`) — query files by
25
+ MDX frontmatter metadata. `useMetadataQuery(fn)` runs a plain JS query and returns
26
+ the matching `{ path, meta }` entries; `useFileMetadata(path)` reads one file's
27
+ frontmatter; `useAllMetadata()` returns the raw reactive map. All take an optional
28
+ type parameter for typed frontmatter access.
25
29
  - `getAuthState`, `onAuthChange`, `useAuth` (`auth`) — read or subscribe to the user's
26
30
  login / account state (`{ status, user: { login } }`). Poll with `getAuthState()`,
27
31
  subscribe with `onAuthChange(listener)` (the listener is called immediately with the
@@ -32,15 +36,35 @@ also reachable via subpaths (`@immediately-run/sdk/boot`, `@immediately-run/sdk/
32
36
  `getMounts()` / `findMount({ type })`, subscribe with `onMountsChange(listener)` or
33
37
  the `useMounts()` hook, or `await waitForMount({ type: 'firestore' })` before using a
34
38
  mount. Access the files via the `fs` module at the mount's `path`.
35
- - routing helpers (`Router`, `SandboxRouter`, …).
39
+ - routing (`routing`) — define the app-owned URL suffix. Declarative
40
+ `<Routes>`/`<Route path="/posts/:slug" element={…} />` (rendering a `<Route>`
41
+ registers it, so routes can be conditional or data-derived), or a `routingSpec`
42
+ passed to `boot`. `path` accepts a template (`:slug`, `*`) compiled to an
43
+ anchored regex, or a raw `RegExp` as an escape hatch. Read the match with
44
+ `useRouteParams()` / `useRoute()`. Also `Router`, `navigate`, `useTinkerableLink`.
36
45
  - `MDXProvider` — the MDX context provider used by transformed `.mdx` files.
37
46
  - `sandboxTypes` — shared TypeScript types for the sandbox runtime.
38
47
 
39
48
  ## API documentation
40
49
 
41
- Full API reference is published to GitHub Pages:
50
+ Full API reference (TypeDoc, human-browsable) is published to GitHub Pages:
42
51
  <https://immediately-run.github.io/immediately-run-sdk/>
43
52
 
53
+ ### For coding agents / LLMs
54
+
55
+ Two machine-readable surfaces are published next to the HTML, each fetchable in a
56
+ single request:
57
+
58
+ - **`llms.txt`** — <https://immediately-run.github.io/immediately-run-sdk/llms.txt> —
59
+ a concise, plain-Markdown map of every export grouped by module, with its kind,
60
+ import path, and a one-line description (the llmstxt.org convention). Start here.
61
+ - **`api.json`** — <https://immediately-run.github.io/immediately-run-sdk/api.json> —
62
+ the complete TypeDoc model (exact signatures, parameters, types, and JSDoc) for
63
+ when you need more than the one-liners.
64
+
65
+ The installed npm package also ships `.d.ts` carrying the same JSDoc, so your
66
+ editor/agent tooling can read the typed API inline without any network access.
67
+
44
68
  ## License
45
69
 
46
70
  [MIT](./LICENSE)
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/MDXProvider.ts"],"sourcesContent":["// Based on https://github.com/mdx-js/mdx/blob/main/packages/react/lib/index.js\n// since sandpack-bundler cannot import @mdx-js/react as it chokes on the\n// transitive dependency on VFile.\nimport {createContext, useContext, useMemo, createElement} from 'react'\nconst emptyComponents = {}\n\nconst MDXContext = createContext(emptyComponents);\n\nexport function useMDXComponents(components:any) {\n const contextComponents = useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return useMemo(\n function () {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n },\n [contextComponents, components]\n )\n}\n\nexport function MDXProvider(properties:any) {\n\n let allComponents: any\n\n if (properties.disableParentContext) {\n allComponents =\n typeof properties.components === 'function'\n ? properties.components(emptyComponents)\n : properties.components || emptyComponents\n } else {\n allComponents = useMDXComponents(properties.components)\n }\n\n return createElement(\n MDXContext.Provider,\n {value: allComponents},\n properties.children\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAgE;AAChE,MAAM,kBAAkB,CAAC;AAEzB,MAAM,iBAAa,4BAAc,eAAe;AAEzC,SAAS,iBAAiB,YAAgB;AAC/C,QAAM,wBAAoB,yBAAW,UAAU;AAG/C,aAAO;AAAA,IACL,WAAY;AAEV,UAAI,OAAO,eAAe,YAAY;AACpC,eAAO,WAAW,iBAAiB;AAAA,MACrC;AAEA,aAAO,EAAC,GAAG,mBAAmB,GAAG,WAAU;AAAA,IAC7C;AAAA,IACA,CAAC,mBAAmB,UAAU;AAAA,EAChC;AACF;AAEO,SAAS,YAAY,YAAgB;AAE1C,MAAI;AAEJ,MAAI,WAAW,sBAAsB;AACnC,oBACE,OAAO,WAAW,eAAe,aAC7B,WAAW,WAAW,eAAe,IACrC,WAAW,cAAc;AAAA,EACjC,OAAO;AACL,oBAAgB,iBAAiB,WAAW,UAAU;AAAA,EACxD;AAEA,aAAO;AAAA,IACL,WAAW;AAAA,IACX,EAAC,OAAO,cAAa;AAAA,IACrB,WAAW;AAAA,EACb;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/MDXProvider.ts"],"sourcesContent":["// Based on https://github.com/mdx-js/mdx/blob/main/packages/react/lib/index.js\n// since sandpack-bundler cannot import @mdx-js/react as it chokes on the\n// transitive dependency on VFile.\nimport {createContext, useContext, useMemo, createElement} from 'react'\nconst emptyComponents = {}\n\nconst MDXContext = createContext(emptyComponents);\n\n/** Merge the caller's MDX component overrides with those from the surrounding\n * {@link MDXProvider} context (a function arg receives the parent set to merge). */\nexport function useMDXComponents(components:any) {\n const contextComponents = useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return useMemo(\n function () {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n },\n [contextComponents, components]\n )\n}\n\n/** Provide MDX component overrides (e.g. a custom `a`/`h1`) to the rendered\n * subtree; transformed `.mdx` modules pick them up. */\nexport function MDXProvider(properties:any) {\n\n let allComponents: any\n\n if (properties.disableParentContext) {\n allComponents =\n typeof properties.components === 'function'\n ? properties.components(emptyComponents)\n : properties.components || emptyComponents\n } else {\n allComponents = useMDXComponents(properties.components)\n }\n\n return createElement(\n MDXContext.Provider,\n {value: allComponents},\n properties.children\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,mBAAgE;AAChE,MAAM,kBAAkB,CAAC;AAEzB,MAAM,iBAAa,4BAAc,eAAe;AAIzC,SAAS,iBAAiB,YAAgB;AAC/C,QAAM,wBAAoB,yBAAW,UAAU;AAG/C,aAAO;AAAA,IACL,WAAY;AAEV,UAAI,OAAO,eAAe,YAAY;AACpC,eAAO,WAAW,iBAAiB;AAAA,MACrC;AAEA,aAAO,EAAC,GAAG,mBAAmB,GAAG,WAAU;AAAA,IAC7C;AAAA,IACA,CAAC,mBAAmB,UAAU;AAAA,EAChC;AACF;AAIO,SAAS,YAAY,YAAgB;AAE1C,MAAI;AAEJ,MAAI,WAAW,sBAAsB;AACnC,oBACE,OAAO,WAAW,eAAe,aAC7B,WAAW,WAAW,eAAe,IACrC,WAAW,cAAc;AAAA,EACjC,OAAO;AACL,oBAAgB,iBAAiB,WAAW,UAAU;AAAA,EACxD;AAEA,aAAO;AAAA,IACL,WAAW;AAAA,IACX,EAAC,OAAO,cAAa;AAAA,IACrB,WAAW;AAAA,EACb;AACF;","names":[]}
@@ -1,6 +1,10 @@
1
1
  import * as react from 'react';
2
2
 
3
+ /** Merge the caller's MDX component overrides with those from the surrounding
4
+ * {@link MDXProvider} context (a function arg receives the parent set to merge). */
3
5
  declare function useMDXComponents(components: any): any;
6
+ /** Provide MDX component overrides (e.g. a custom `a`/`h1`) to the rendered
7
+ * subtree; transformed `.mdx` modules pick them up. */
4
8
  declare function MDXProvider(properties: any): react.FunctionComponentElement<react.ProviderProps<{}>>;
5
9
 
6
10
  export { MDXProvider, useMDXComponents };
@@ -1,6 +1,10 @@
1
1
  import * as react from 'react';
2
2
 
3
+ /** Merge the caller's MDX component overrides with those from the surrounding
4
+ * {@link MDXProvider} context (a function arg receives the parent set to merge). */
3
5
  declare function useMDXComponents(components: any): any;
6
+ /** Provide MDX component overrides (e.g. a custom `a`/`h1`) to the rendered
7
+ * subtree; transformed `.mdx` modules pick them up. */
4
8
  declare function MDXProvider(properties: any): react.FunctionComponentElement<react.ProviderProps<{}>>;
5
9
 
6
10
  export { MDXProvider, useMDXComponents };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/MDXProvider.ts"],"sourcesContent":["// Based on https://github.com/mdx-js/mdx/blob/main/packages/react/lib/index.js\n// since sandpack-bundler cannot import @mdx-js/react as it chokes on the\n// transitive dependency on VFile.\nimport {createContext, useContext, useMemo, createElement} from 'react'\nconst emptyComponents = {}\n\nconst MDXContext = createContext(emptyComponents);\n\nexport function useMDXComponents(components:any) {\n const contextComponents = useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return useMemo(\n function () {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n },\n [contextComponents, components]\n )\n}\n\nexport function MDXProvider(properties:any) {\n\n let allComponents: any\n\n if (properties.disableParentContext) {\n allComponents =\n typeof properties.components === 'function'\n ? properties.components(emptyComponents)\n : properties.components || emptyComponents\n } else {\n allComponents = useMDXComponents(properties.components)\n }\n\n return createElement(\n MDXContext.Provider,\n {value: allComponents},\n properties.children\n )\n}\n"],"mappings":"AAGA,SAAQ,eAAe,YAAY,SAAS,qBAAoB;AAChE,MAAM,kBAAkB,CAAC;AAEzB,MAAM,aAAa,cAAc,eAAe;AAEzC,SAAS,iBAAiB,YAAgB;AAC/C,QAAM,oBAAoB,WAAW,UAAU;AAG/C,SAAO;AAAA,IACL,WAAY;AAEV,UAAI,OAAO,eAAe,YAAY;AACpC,eAAO,WAAW,iBAAiB;AAAA,MACrC;AAEA,aAAO,EAAC,GAAG,mBAAmB,GAAG,WAAU;AAAA,IAC7C;AAAA,IACA,CAAC,mBAAmB,UAAU;AAAA,EAChC;AACF;AAEO,SAAS,YAAY,YAAgB;AAE1C,MAAI;AAEJ,MAAI,WAAW,sBAAsB;AACnC,oBACE,OAAO,WAAW,eAAe,aAC7B,WAAW,WAAW,eAAe,IACrC,WAAW,cAAc;AAAA,EACjC,OAAO;AACL,oBAAgB,iBAAiB,WAAW,UAAU;AAAA,EACxD;AAEA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,EAAC,OAAO,cAAa;AAAA,IACrB,WAAW;AAAA,EACb;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/MDXProvider.ts"],"sourcesContent":["// Based on https://github.com/mdx-js/mdx/blob/main/packages/react/lib/index.js\n// since sandpack-bundler cannot import @mdx-js/react as it chokes on the\n// transitive dependency on VFile.\nimport {createContext, useContext, useMemo, createElement} from 'react'\nconst emptyComponents = {}\n\nconst MDXContext = createContext(emptyComponents);\n\n/** Merge the caller's MDX component overrides with those from the surrounding\n * {@link MDXProvider} context (a function arg receives the parent set to merge). */\nexport function useMDXComponents(components:any) {\n const contextComponents = useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return useMemo(\n function () {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n },\n [contextComponents, components]\n )\n}\n\n/** Provide MDX component overrides (e.g. a custom `a`/`h1`) to the rendered\n * subtree; transformed `.mdx` modules pick them up. */\nexport function MDXProvider(properties:any) {\n\n let allComponents: any\n\n if (properties.disableParentContext) {\n allComponents =\n typeof properties.components === 'function'\n ? properties.components(emptyComponents)\n : properties.components || emptyComponents\n } else {\n allComponents = useMDXComponents(properties.components)\n }\n\n return createElement(\n MDXContext.Provider,\n {value: allComponents},\n properties.children\n )\n}\n"],"mappings":"AAGA,SAAQ,eAAe,YAAY,SAAS,qBAAoB;AAChE,MAAM,kBAAkB,CAAC;AAEzB,MAAM,aAAa,cAAc,eAAe;AAIzC,SAAS,iBAAiB,YAAgB;AAC/C,QAAM,oBAAoB,WAAW,UAAU;AAG/C,SAAO;AAAA,IACL,WAAY;AAEV,UAAI,OAAO,eAAe,YAAY;AACpC,eAAO,WAAW,iBAAiB;AAAA,MACrC;AAEA,aAAO,EAAC,GAAG,mBAAmB,GAAG,WAAU;AAAA,IAC7C;AAAA,IACA,CAAC,mBAAmB,UAAU;AAAA,EAChC;AACF;AAIO,SAAS,YAAY,YAAgB;AAE1C,MAAI;AAEJ,MAAI,WAAW,sBAAsB;AACnC,oBACE,OAAO,WAAW,eAAe,aAC7B,WAAW,WAAW,eAAe,IACrC,WAAW,cAAc;AAAA,EACjC,OAAO;AACL,oBAAgB,iBAAiB,WAAW,UAAU;AAAA,EACxD;AAEA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,EAAC,OAAO,cAAa;AAAA,IACrB,WAAW;AAAA,EACb;AACF;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/RoutingSpec.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\nexport type RoutingRule = {\n name?: string;\n pattern: string | RegExp;\n reactNode: ReactNode | string;\n};\n\nexport type RoutingSpec = {\n routes: RoutingRule[];\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/RoutingSpec.ts"],"sourcesContent":["import type { FC, ReactNode } from 'react';\n\n/** Named parameters extracted from a route match (`:name` segments, `*` rest). */\nexport type RouteParams = Record<string, string>;\n\n/** A route target that receives the matched params as a prop. */\nexport type RouteComponent = FC<{ params: RouteParams }>;\n\nexport type RoutingRule = {\n name?: string;\n /**\n * What to match against the app-owned `sandboxPath`. A string is a path\n * template (`/posts/:slug`, `/files/*`, or a literal like `/`) compiled to an\n * anchored regex; a `RegExp` is used as authored (the escape hatch — you own\n * anchoring).\n */\n pattern: string | RegExp;\n /** Element to render for this route. Reads params via `useRouteParams()`. */\n element?: ReactNode;\n /** Component to render for this route; receives the matched params as a prop. */\n component?: RouteComponent;\n /** @deprecated Use `element`. Accepted for backward compatibility. */\n reactNode?: ReactNode;\n};\n\nexport type RoutingSpec = {\n routes: RoutingRule[];\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -1,12 +1,29 @@
1
- import { ReactNode } from 'react';
1
+ import { FC, ReactNode } from 'react';
2
2
 
3
+ /** Named parameters extracted from a route match (`:name` segments, `*` rest). */
4
+ type RouteParams = Record<string, string>;
5
+ /** A route target that receives the matched params as a prop. */
6
+ type RouteComponent = FC<{
7
+ params: RouteParams;
8
+ }>;
3
9
  type RoutingRule = {
4
10
  name?: string;
11
+ /**
12
+ * What to match against the app-owned `sandboxPath`. A string is a path
13
+ * template (`/posts/:slug`, `/files/*`, or a literal like `/`) compiled to an
14
+ * anchored regex; a `RegExp` is used as authored (the escape hatch — you own
15
+ * anchoring).
16
+ */
5
17
  pattern: string | RegExp;
6
- reactNode: ReactNode | string;
18
+ /** Element to render for this route. Reads params via `useRouteParams()`. */
19
+ element?: ReactNode;
20
+ /** Component to render for this route; receives the matched params as a prop. */
21
+ component?: RouteComponent;
22
+ /** @deprecated Use `element`. Accepted for backward compatibility. */
23
+ reactNode?: ReactNode;
7
24
  };
8
25
  type RoutingSpec = {
9
26
  routes: RoutingRule[];
10
27
  };
11
28
 
12
- export type { RoutingRule, RoutingSpec };
29
+ export type { RouteComponent, RouteParams, RoutingRule, RoutingSpec };
@@ -1,12 +1,29 @@
1
- import { ReactNode } from 'react';
1
+ import { FC, ReactNode } from 'react';
2
2
 
3
+ /** Named parameters extracted from a route match (`:name` segments, `*` rest). */
4
+ type RouteParams = Record<string, string>;
5
+ /** A route target that receives the matched params as a prop. */
6
+ type RouteComponent = FC<{
7
+ params: RouteParams;
8
+ }>;
3
9
  type RoutingRule = {
4
10
  name?: string;
11
+ /**
12
+ * What to match against the app-owned `sandboxPath`. A string is a path
13
+ * template (`/posts/:slug`, `/files/*`, or a literal like `/`) compiled to an
14
+ * anchored regex; a `RegExp` is used as authored (the escape hatch — you own
15
+ * anchoring).
16
+ */
5
17
  pattern: string | RegExp;
6
- reactNode: ReactNode | string;
18
+ /** Element to render for this route. Reads params via `useRouteParams()`. */
19
+ element?: ReactNode;
20
+ /** Component to render for this route; receives the matched params as a prop. */
21
+ component?: RouteComponent;
22
+ /** @deprecated Use `element`. Accepted for backward compatibility. */
23
+ reactNode?: ReactNode;
7
24
  };
8
25
  type RoutingSpec = {
9
26
  routes: RoutingRule[];
10
27
  };
11
28
 
12
- export type { RoutingRule, RoutingSpec };
29
+ export type { RouteComponent, RouteParams, RoutingRule, RoutingSpec };
package/dist/auth.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: 'auth-state',\n requestType: 'request-auth-state',\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAqBlC,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,cAAU,sCAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: 'auth-state',\n requestType: 'request-auth-state',\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAuBlC,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,cAAU,sCAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
package/dist/auth.d.cts CHANGED
@@ -6,10 +6,12 @@
6
6
  * distinguish "still loading" from a confirmed signed-out session).
7
7
  */
8
8
  type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';
9
+ /** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */
9
10
  interface SandboxUser {
10
11
  /** GitHub login (handle) of the signed-in user. */
11
12
  login: string;
12
13
  }
14
+ /** The user's login / account state: a `status` plus the `user` when signed in. */
13
15
  interface AuthState {
14
16
  status: AuthStatus;
15
17
  user: SandboxUser | null;
package/dist/auth.d.ts CHANGED
@@ -6,10 +6,12 @@
6
6
  * distinguish "still loading" from a confirmed signed-out session).
7
7
  */
8
8
  type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';
9
+ /** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */
9
10
  interface SandboxUser {
10
11
  /** GitHub login (handle) of the signed-in user. */
11
12
  login: string;
12
13
  }
14
+ /** The user's login / account state: a `status` plus the `user` when signed in. */
13
15
  interface AuthState {
14
16
  status: AuthStatus;
15
17
  user: SandboxUser | null;
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: 'auth-state',\n requestType: 'request-auth-state',\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":"AAAA,SAAS,yBAAyB;AAqBlC,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,UAAU,kBAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: 'auth-state',\n requestType: 'request-auth-state',\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":"AAAA,SAAS,yBAAyB;AAuBlC,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,UAAU,kBAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
package/dist/boot.cjs CHANGED
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var boot_exports = {};
20
20
  __export(boot_exports, {
21
+ CATCH_ALL_ROUTING_SPEC: () => CATCH_ALL_ROUTING_SPEC,
21
22
  DEFAULT_ROUTING_SPEC: () => DEFAULT_ROUTING_SPEC,
22
23
  TinkerableApp: () => TinkerableApp,
23
24
  boot: () => boot
@@ -47,7 +48,10 @@ const updateAlreadyApplied = (filesMetadata, update) => {
47
48
  }
48
49
  return true;
49
50
  };
50
- const TinkerableApp = ({ routingSpec }) => {
51
+ const TinkerableApp = ({
52
+ routingSpec,
53
+ children
54
+ }) => {
51
55
  const [context, setContext] = (0, import_react.useState)((0, import_contextUtils.getInitialContext)(routingSpec));
52
56
  (0, import_react.useEffect)(() => {
53
57
  const removeListener = (0, import_sandboxUtils.addListener)("urlchange", ({ url }) => {
@@ -84,7 +88,7 @@ const TinkerableApp = ({ routingSpec }) => {
84
88
  source.enable();
85
89
  return dispose;
86
90
  }, [setContext]);
87
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TinkerableContext.TinkerableContext, { value: context, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_routing.Router, {}) });
91
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_TinkerableContext.TinkerableContext, { value: context, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_routing.Router, {}) });
88
92
  };
89
93
  const BootMarkers = () => {
90
94
  (0, import_react.useLayoutEffect)(() => {
@@ -96,34 +100,40 @@ const BootMarkers = () => {
96
100
  const escapeForRegexp = (str) => str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
97
101
  const DEFAULT_ROUTING_SPEC = {
98
102
  routes: [
99
- { name: "MainContent", pattern: /^\/$/, reactNode: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MainContent.MainContent, {}) },
103
+ { name: "MainContent", pattern: /^\/$/, element: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_MainContent.MainContent, {}) },
100
104
  {
101
105
  name: "FileRouter",
102
106
  pattern: new RegExp(`^${escapeForRegexp(import_urlUtils.FILES_PREFIX)}(?<filename>/.+)$`),
103
- reactNode: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_FileRouter.FileRouter, {})
107
+ element: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_FileRouter.FileRouter, {})
104
108
  },
105
- { name: "ErrorNotFound", pattern: /^(?<path>.+)$/, reactNode: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_errors.ErrorNotFound, {}) }
109
+ { name: "ErrorNotFound", pattern: /^(?<path>.+)$/, element: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_errors.ErrorNotFound, {}) }
106
110
  ]
107
111
  };
112
+ const CATCH_ALL_ROUTING_SPEC = {
113
+ routes: [{ name: "AppRoot", pattern: /^.*$/, element: null }]
114
+ };
108
115
  const boot = ({
109
116
  mdxComponents = import_MDXComponents.DEFAULT_MDX_COMPONENTS,
110
- routingSpec = DEFAULT_ROUTING_SPEC
117
+ routingSpec,
118
+ children
111
119
  } = {}) => {
112
120
  const rootElement = document.getElementById("root");
113
121
  if (!rootElement) {
114
122
  throw new Error("boot requires root HTML element to exist");
115
123
  }
124
+ const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);
116
125
  const moduleCache = new import_moduleCache.ModuleCache();
117
126
  const root = (0, import_client.createRoot)(rootElement);
118
127
  root.render(
119
128
  /* @__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
129
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BootMarkers, {}),
121
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TinkerableApp, { routingSpec })
130
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TinkerableApp, { routingSpec: spec, children })
122
131
  ] }) }) })
123
132
  );
124
133
  };
125
134
  // Annotate the CommonJS export names for ESM import in node:
126
135
  0 && (module.exports = {
136
+ CATCH_ALL_ROUTING_SPEC,
127
137
  DEFAULT_ROUTING_SPEC,
128
138
  TinkerableApp,
129
139
  boot
package/dist/boot.cjs.map CHANGED
@@ -1 +1 @@
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":"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","names":["context"]}
1
+ {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, 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\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\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\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\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 {children ?? <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\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, element: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n element: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\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={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"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","names":["context"]}
package/dist/boot.d.cts CHANGED
@@ -1,15 +1,39 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { FC } from 'react';
2
+ import { FC, ReactNode } from 'react';
3
3
  import { RoutingSpec } from './RoutingSpec.cjs';
4
4
 
5
+ /** Options for {@link boot}: MDX overrides, a route table, or an app root. */
5
6
  type BootProps = {
6
7
  mdxComponents?: Record<string, FC>;
7
8
  routingSpec?: RoutingSpec;
9
+ /**
10
+ * App root rendered directly inside the providers (with full navigation
11
+ * context), instead of dispatching through a `routingSpec`. Render
12
+ * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule
13
+ * boilerplate. Takes precedence over `routingSpec` for what is rendered.
14
+ */
15
+ children?: ReactNode;
8
16
  };
9
- declare const TinkerableApp: ({ routingSpec }: {
17
+ /** The app shell {@link boot} renders: holds navigation state, subscribes to host
18
+ * URL + metadata pushes, and renders `children` or the route `<Router />`. */
19
+ declare const TinkerableApp: ({ routingSpec, children, }: {
10
20
  routingSpec: RoutingSpec;
21
+ children?: ReactNode;
11
22
  }) => react_jsx_runtime.JSX.Element;
23
+ /** The default route table when `boot` is called with no `routingSpec`/`children`:
24
+ * `/` → main content, `/files/<path>` → the file router, else not-found. */
12
25
  declare const DEFAULT_ROUTING_SPEC: RoutingSpec;
13
- declare const boot: ({ mdxComponents, routingSpec, }?: BootProps) => void;
26
+ /**
27
+ * Matches any `sandboxPath` so navigation context can be built without a route
28
+ * table. Used when {@link boot} is given `children` (the app owns dispatch via
29
+ * `<Routes>`); the catch-all's `element` is never rendered (children are).
30
+ */
31
+ declare const CATCH_ALL_ROUTING_SPEC: RoutingSpec;
32
+ /**
33
+ * Mount an immediately.run app into the sandbox `#root`. The entry point every
34
+ * app calls from `index.tsx`: wires the MDX, module-cache, and navigation
35
+ * providers, then renders the route table (`routingSpec`) or your `children`.
36
+ */
37
+ declare const boot: ({ mdxComponents, routingSpec, children, }?: BootProps) => void;
14
38
 
15
- export { type BootProps, DEFAULT_ROUTING_SPEC, TinkerableApp, boot };
39
+ export { type BootProps, CATCH_ALL_ROUTING_SPEC, DEFAULT_ROUTING_SPEC, TinkerableApp, boot };
package/dist/boot.d.ts CHANGED
@@ -1,15 +1,39 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { FC } from 'react';
2
+ import { FC, ReactNode } from 'react';
3
3
  import { RoutingSpec } from './RoutingSpec.js';
4
4
 
5
+ /** Options for {@link boot}: MDX overrides, a route table, or an app root. */
5
6
  type BootProps = {
6
7
  mdxComponents?: Record<string, FC>;
7
8
  routingSpec?: RoutingSpec;
9
+ /**
10
+ * App root rendered directly inside the providers (with full navigation
11
+ * context), instead of dispatching through a `routingSpec`. Render
12
+ * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule
13
+ * boilerplate. Takes precedence over `routingSpec` for what is rendered.
14
+ */
15
+ children?: ReactNode;
8
16
  };
9
- declare const TinkerableApp: ({ routingSpec }: {
17
+ /** The app shell {@link boot} renders: holds navigation state, subscribes to host
18
+ * URL + metadata pushes, and renders `children` or the route `<Router />`. */
19
+ declare const TinkerableApp: ({ routingSpec, children, }: {
10
20
  routingSpec: RoutingSpec;
21
+ children?: ReactNode;
11
22
  }) => react_jsx_runtime.JSX.Element;
23
+ /** The default route table when `boot` is called with no `routingSpec`/`children`:
24
+ * `/` → main content, `/files/<path>` → the file router, else not-found. */
12
25
  declare const DEFAULT_ROUTING_SPEC: RoutingSpec;
13
- declare const boot: ({ mdxComponents, routingSpec, }?: BootProps) => void;
26
+ /**
27
+ * Matches any `sandboxPath` so navigation context can be built without a route
28
+ * table. Used when {@link boot} is given `children` (the app owns dispatch via
29
+ * `<Routes>`); the catch-all's `element` is never rendered (children are).
30
+ */
31
+ declare const CATCH_ALL_ROUTING_SPEC: RoutingSpec;
32
+ /**
33
+ * Mount an immediately.run app into the sandbox `#root`. The entry point every
34
+ * app calls from `index.tsx`: wires the MDX, module-cache, and navigation
35
+ * providers, then renders the route table (`routingSpec`) or your `children`.
36
+ */
37
+ declare const boot: ({ mdxComponents, routingSpec, children, }?: BootProps) => void;
14
38
 
15
- export { type BootProps, DEFAULT_ROUTING_SPEC, TinkerableApp, boot };
39
+ export { type BootProps, CATCH_ALL_ROUTING_SPEC, DEFAULT_ROUTING_SPEC, TinkerableApp, boot };
package/dist/boot.js CHANGED
@@ -22,7 +22,10 @@ const updateAlreadyApplied = (filesMetadata, update) => {
22
22
  }
23
23
  return true;
24
24
  };
25
- const TinkerableApp = ({ routingSpec }) => {
25
+ const TinkerableApp = ({
26
+ routingSpec,
27
+ children
28
+ }) => {
26
29
  const [context, setContext] = useState(getInitialContext(routingSpec));
27
30
  useEffect(() => {
28
31
  const removeListener = addListener("urlchange", ({ url }) => {
@@ -59,7 +62,7 @@ const TinkerableApp = ({ routingSpec }) => {
59
62
  source.enable();
60
63
  return dispose;
61
64
  }, [setContext]);
62
- return /* @__PURE__ */ jsx(TinkerableContext, { value: context, children: /* @__PURE__ */ jsx(Router, {}) });
65
+ return /* @__PURE__ */ jsx(TinkerableContext, { value: context, children: children ?? /* @__PURE__ */ jsx(Router, {}) });
63
66
  };
64
67
  const BootMarkers = () => {
65
68
  useLayoutEffect(() => {
@@ -71,33 +74,39 @@ const BootMarkers = () => {
71
74
  const escapeForRegexp = (str) => str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
72
75
  const DEFAULT_ROUTING_SPEC = {
73
76
  routes: [
74
- { name: "MainContent", pattern: /^\/$/, reactNode: /* @__PURE__ */ jsx(MainContent, {}) },
77
+ { name: "MainContent", pattern: /^\/$/, element: /* @__PURE__ */ jsx(MainContent, {}) },
75
78
  {
76
79
  name: "FileRouter",
77
80
  pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>/.+)$`),
78
- reactNode: /* @__PURE__ */ jsx(FileRouter, {})
81
+ element: /* @__PURE__ */ jsx(FileRouter, {})
79
82
  },
80
- { name: "ErrorNotFound", pattern: /^(?<path>.+)$/, reactNode: /* @__PURE__ */ jsx(ErrorNotFound, {}) }
83
+ { name: "ErrorNotFound", pattern: /^(?<path>.+)$/, element: /* @__PURE__ */ jsx(ErrorNotFound, {}) }
81
84
  ]
82
85
  };
86
+ const CATCH_ALL_ROUTING_SPEC = {
87
+ routes: [{ name: "AppRoot", pattern: /^.*$/, element: null }]
88
+ };
83
89
  const boot = ({
84
90
  mdxComponents = DEFAULT_MDX_COMPONENTS,
85
- routingSpec = DEFAULT_ROUTING_SPEC
91
+ routingSpec,
92
+ children
86
93
  } = {}) => {
87
94
  const rootElement = document.getElementById("root");
88
95
  if (!rootElement) {
89
96
  throw new Error("boot requires root HTML element to exist");
90
97
  }
98
+ const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);
91
99
  const moduleCache = new ModuleCache();
92
100
  const root = createRoot(rootElement);
93
101
  root.render(
94
102
  /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(ModuleCacheContextProvider, { moduleCache, children: /* @__PURE__ */ jsxs(MDXProvider, { components: mdxComponents, children: [
95
103
  /* @__PURE__ */ jsx(BootMarkers, {}),
96
- /* @__PURE__ */ jsx(TinkerableApp, { routingSpec })
104
+ /* @__PURE__ */ jsx(TinkerableApp, { routingSpec: spec, children })
97
105
  ] }) }) })
98
106
  );
99
107
  };
100
108
  export {
109
+ CATCH_ALL_ROUTING_SPEC,
101
110
  DEFAULT_ROUTING_SPEC,
102
111
  TinkerableApp,
103
112
  boot
package/dist/boot.js.map CHANGED
@@ -1 +1 @@
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":"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","names":["context"]}
1
+ {"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, 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\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n mdxComponents?: Record<string, FC>;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\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\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\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 {children ?? <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\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: /^\\/$/, element: <MainContent /> },\n {\n name: 'FileRouter',\n pattern: new RegExp(`^${escapeForRegexp(FILES_PREFIX)}(?<filename>\\/.+)$`),\n element: <FileRouter />,\n },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents = DEFAULT_MDX_COMPONENTS,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\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={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"AAgGmB,cAoEX,YApEW;AAhGnB,SAAwB,YAAY,WAAW,iBAAiB,gBAAgB;AAChF,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;AAe7B,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;AAIO,MAAM,gBAAgB,CAAC;AAAA,EAC5B;AAAA,EACA;AACF,MAGM;AACJ,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,SACvB,sBAAY,oBAAC,UAAO,GACvB;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;AAI7E,MAAM,uBAAoC;AAAA,EAC/C,QAAQ;AAAA,IACN,EAAE,MAAM,eAAe,SAAS,QAAQ,SAAS,oBAAC,eAAY,EAAG;AAAA,IACjE;AAAA,MACE,MAAM;AAAA,MACN,SAAS,IAAI,OAAO,IAAI,gBAAgB,YAAY,CAAC,mBAAoB;AAAA,MACzE,SAAS,oBAAC,cAAW;AAAA,IACvB;AAAA,IACA,EAAE,MAAM,iBAAiB,SAAS,iBAAiB,SAAS,oBAAC,iBAAc,EAAG;AAAA,EAChF;AACF;AAOO,MAAM,yBAAsC;AAAA,EACjD,QAAQ,CAAC,EAAE,MAAM,WAAW,SAAS,QAAQ,SAAS,KAAK,CAAC;AAC9D;AAOO,MAAM,OAAO,CAAC;AAAA,EACnB,gBAAgB;AAAA,EAChB;AAAA,EACA;AACF,IAAe,CAAC,MAAM;AACpB,QAAM,cAAc,SAAS,eAAe,MAAM;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAGA,QAAM,OAAO,gBAAgB,WAAW,yBAAyB;AACjE,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,aAAa,MAAO,UAAS;AAAA,OAC9C,GACF,GACF;AAAA,EACF;AACF;","names":["context"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/Include.tsx"],"sourcesContent":["import { Suspense, createContext, use } from 'react';\nimport { ErrorBoundary } from 'react-error-boundary';\n\nimport { ModuleCacheContext } from '../moduleCache';\nimport { EvaluationContext } from '../sandboxTypes';\nimport { defaultErrorComponent, defaultLoadingComponent } from './defaults';\n\nexport type RenderFileContextType = {\n evaluationContext: EvaluationContext;\n};\n\nexport const RenderExportedComponentContext = createContext<RenderFileContextType | null>(null);\n\nexport const RenderExportedComponent = ({\n evaluationContextPromise,\n exportedSymbol = 'default',\n}: {\n evaluationContextPromise: Promise<EvaluationContext>;\n exportedSymbol: string;\n}) => {\n const evaluationContext = use(evaluationContextPromise);\n // TODO: handle case where exported symbol not found.\n const Component = exportedSymbol === '*' ? evaluationContext.exports : evaluationContext.exports[exportedSymbol];\n return (\n <RenderExportedComponentContext value={{ evaluationContext }}>\n <Component />\n </RenderExportedComponentContext>\n );\n};\n\nexport const Include = ({\n filename,\n exportedSymbol = 'default',\n LoadingComponent = defaultLoadingComponent,\n ErrorComponent = defaultErrorComponent,\n baseModule,\n}: {\n filename: string;\n exportedSymbol?: string;\n LoadingComponent?: typeof defaultLoadingComponent;\n ErrorComponent?: typeof defaultErrorComponent;\n baseModule?: EvaluationContext;\n}) => {\n const moduleCache = use(ModuleCacheContext);\n // @ts-ignore\n const evaluationContextPromise = moduleCache!.getEvaluationContext(filename, baseModule ?? module);\n return (\n <ErrorBoundary fallbackRender={ErrorComponent}>\n <Suspense fallback={<LoadingComponent />}>\n <RenderExportedComponent evaluationContextPromise={evaluationContextPromise} exportedSymbol={exportedSymbol} />\n </Suspense>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBM;AAzBN,mBAA6C;AAC7C,kCAA8B;AAE9B,yBAAmC;AAEnC,sBAA+D;AAMxD,MAAM,qCAAiC,4BAA4C,IAAI;AAEvF,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA,iBAAiB;AACnB,MAGM;AACJ,QAAM,wBAAoB,kBAAI,wBAAwB;AAEtD,QAAM,YAAY,mBAAmB,MAAM,kBAAkB,UAAU,kBAAkB,QAAQ,cAAc;AAC/G,SACE,4CAAC,kCAA+B,OAAO,EAAE,kBAAkB,GACzD,sDAAC,aAAU,GACb;AAEJ;AAEO,MAAM,UAAU,CAAC;AAAA,EACtB;AAAA,EACA,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB;AACF,MAMM;AACJ,QAAM,kBAAc,kBAAI,qCAAkB;AAE1C,QAAM,2BAA2B,YAAa,qBAAqB,UAAU,cAAc,MAAM;AACjG,SACE,4CAAC,6CAAc,gBAAgB,gBAC7B,sDAAC,yBAAS,UAAU,4CAAC,oBAAiB,GACpC,sDAAC,2BAAwB,0BAAoD,gBAAgC,GAC/G,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/Include.tsx"],"sourcesContent":["import { Suspense, createContext, use } from 'react';\nimport { ErrorBoundary } from 'react-error-boundary';\n\nimport { ModuleCacheContext } from '../moduleCache';\nimport { EvaluationContext } from '../sandboxTypes';\nimport { defaultErrorComponent, defaultLoadingComponent } from './defaults';\n\n/** The value exposed on {@link RenderExportedComponentContext}: the evaluation\n * context of the module {@link Include} resolved. */\nexport type RenderFileContextType = {\n evaluationContext: EvaluationContext;\n};\n\n/** Context carrying the included module's {@link EvaluationContext} to its subtree. */\nexport const RenderExportedComponentContext = createContext<RenderFileContextType | null>(null);\n\n/** Low-level: render one export of an already-resolving module evaluation. Most\n * code should use {@link Include}, which resolves the module and adds Suspense. */\nexport const RenderExportedComponent = ({\n evaluationContextPromise,\n exportedSymbol = 'default',\n}: {\n evaluationContextPromise: Promise<EvaluationContext>;\n exportedSymbol: string;\n}) => {\n const evaluationContext = use(evaluationContextPromise);\n // TODO: handle case where exported symbol not found.\n const Component = exportedSymbol === '*' ? evaluationContext.exports : evaluationContext.exports[exportedSymbol];\n return (\n <RenderExportedComponentContext value={{ evaluationContext }}>\n <Component />\n </RenderExportedComponentContext>\n );\n};\n\n/** Render another repo file's exported component inline, resolving + evaluating it\n * through the module cache (with Suspense + an error boundary). */\nexport const Include = ({\n filename,\n exportedSymbol = 'default',\n LoadingComponent = defaultLoadingComponent,\n ErrorComponent = defaultErrorComponent,\n baseModule,\n}: {\n filename: string;\n exportedSymbol?: string;\n LoadingComponent?: typeof defaultLoadingComponent;\n ErrorComponent?: typeof defaultErrorComponent;\n baseModule?: EvaluationContext;\n}) => {\n const moduleCache = use(ModuleCacheContext);\n // @ts-ignore\n const evaluationContextPromise = moduleCache!.getEvaluationContext(filename, baseModule ?? module);\n return (\n <ErrorBoundary fallbackRender={ErrorComponent}>\n <Suspense fallback={<LoadingComponent />}>\n <RenderExportedComponent evaluationContextPromise={evaluationContextPromise} exportedSymbol={exportedSymbol} />\n </Suspense>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BM;AA9BN,mBAA6C;AAC7C,kCAA8B;AAE9B,yBAAmC;AAEnC,sBAA+D;AASxD,MAAM,qCAAiC,4BAA4C,IAAI;AAIvF,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA,iBAAiB;AACnB,MAGM;AACJ,QAAM,wBAAoB,kBAAI,wBAAwB;AAEtD,QAAM,YAAY,mBAAmB,MAAM,kBAAkB,UAAU,kBAAkB,QAAQ,cAAc;AAC/G,SACE,4CAAC,kCAA+B,OAAO,EAAE,kBAAkB,GACzD,sDAAC,aAAU,GACb;AAEJ;AAIO,MAAM,UAAU,CAAC;AAAA,EACtB;AAAA,EACA,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB;AACF,MAMM;AACJ,QAAM,kBAAc,kBAAI,qCAAkB;AAE1C,QAAM,2BAA2B,YAAa,qBAAqB,UAAU,cAAc,MAAM;AACjG,SACE,4CAAC,6CAAc,gBAAgB,gBAC7B,sDAAC,yBAAS,UAAU,4CAAC,oBAAiB,GACpC,sDAAC,2BAAwB,0BAAoD,gBAAgC,GAC/G,GACF;AAEJ;","names":[]}
@@ -4,14 +4,21 @@ import { EvaluationContext } from '../sandboxTypes.cjs';
4
4
  import { defaultLoadingComponent, defaultErrorComponent } from './defaults.cjs';
5
5
  import 'react-error-boundary';
6
6
 
7
+ /** The value exposed on {@link RenderExportedComponentContext}: the evaluation
8
+ * context of the module {@link Include} resolved. */
7
9
  type RenderFileContextType = {
8
10
  evaluationContext: EvaluationContext;
9
11
  };
12
+ /** Context carrying the included module's {@link EvaluationContext} to its subtree. */
10
13
  declare const RenderExportedComponentContext: react.Context<RenderFileContextType | null>;
14
+ /** Low-level: render one export of an already-resolving module evaluation. Most
15
+ * code should use {@link Include}, which resolves the module and adds Suspense. */
11
16
  declare const RenderExportedComponent: ({ evaluationContextPromise, exportedSymbol, }: {
12
17
  evaluationContextPromise: Promise<EvaluationContext>;
13
18
  exportedSymbol: string;
14
19
  }) => react_jsx_runtime.JSX.Element;
20
+ /** Render another repo file's exported component inline, resolving + evaluating it
21
+ * through the module cache (with Suspense + an error boundary). */
15
22
  declare const Include: ({ filename, exportedSymbol, LoadingComponent, ErrorComponent, baseModule, }: {
16
23
  filename: string;
17
24
  exportedSymbol?: string;