{"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 { ScrollAfterNavigation } from './components/ScrollAfterNavigation';\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';\nimport { METADATA_UPDATE, URLCHANGE, type UrlchangePayload } from './generated/protocol';\nimport { receiveNavigation } from './entryState';\n\n/** A map of MDX component overrides, or a function that receives the platform\n *  {@link DEFAULT_MDX_COMPONENTS} and returns the full map to use. */\nexport type MdxComponents = Record<string, FC> | ((defaults: Record<string, FC>) => Record<string, FC>);\n\n/**\n * Resolve the effective MDX component map from a {@link BootProps.mdxComponents}\n * value (MARKDOWN_SYNTAX_SPEC §11.3):\n * - `undefined` → the platform {@link DEFAULT_MDX_COMPONENTS} (same reference).\n * - a **function** → the full-replace escape hatch, handed the defaults.\n * - a **map** → merged *over* the defaults (`{ ...defaults, ...map }`), so\n *   overriding one component keeps the rest — the phantom-defaults invariant\n *   (§11.2) that stops the MDX missing-reference guard from firing.\n */\nexport const resolveMdxComponents = (mdxComponents?: MdxComponents): Record<string, FC> =>\n  mdxComponents === undefined\n    ? (DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n    : typeof mdxComponents === 'function'\n    ? mdxComponents(DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n    : { ...(DEFAULT_MDX_COMPONENTS as Record<string, FC>), ...mdxComponents };\n\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n  /**\n   * MDX component overrides. A **map** is *merged over* the platform defaults\n   * ({@link DEFAULT_MDX_COMPONENTS}) — so overriding `WikiLink` alone keeps the\n   * default `a` and `Admonition` (MARKDOWN_SYNTAX_SPEC §11.3). Pass a **function**\n   * `(defaults) => map` as the full-replace escape hatch when you want complete\n   * control over the set.\n   */\n  mdxComponents?: MdxComponents;\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 = ({ routingSpec, children }: { routingSpec: RoutingSpec; children?: ReactNode }) => {\n  const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n  useEffect(() => {\n    const removeListener = addListener(URLCHANGE, ({ url, entryState, back, forward }: UrlchangePayload) => {\n      // R3-627: record what the host says about this arrival BEFORE the route state\n      // updates, so a consumer reacting to the new route already sees the scratch\n      // and the direction that produced it.\n      receiveNavigation({\n        state: (entryState ?? undefined) as Record<string, unknown> | undefined,\n        direction: back ? 'back' : forward ? 'forward' : 'push',\n      });\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      {/* Capability C: lands cross-page `#fragment` deep-links on their section\n          after the destination tree mounts, uniform for every MDX app (§13.5). */}\n      <ScrollAfterNavigation />\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/** The default route table when `boot` is called with no `routingSpec`/`children`:\n *  `/` → main content, `/files/<path>` → the file router, else not-found.\n *  Re-expressed with path templates (SANDBOX_ROUTING_SPEC §7); the file path\n *  surfaces under the `*` wildcard. The catch-all stays a raw RegExp — the\n *  escape hatch — so it anchors `.+` (a non-empty path) exactly as before. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n  routes: [\n    { name: 'MainContent', pattern: '/', element: <MainContent /> },\n    { name: 'FileRouter', pattern: `${FILES_PREFIX}/*`, element: <FileRouter /> },\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 = ({ mdxComponents, routingSpec, children }: BootProps = {}) => {\n  const rootElement = document.getElementById('root');\n  if (!rootElement) {\n    throw new Error('boot requires root HTML element to exist');\n  }\n  const resolvedComponents = resolveMdxComponents(mdxComponents);\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={resolvedComponents}>\n          <BootMarkers />\n          <TinkerableApp routingSpec={spec}>{children}</TinkerableApp>\n        </MDXProvider>\n      </ModuleCacheContextProvider>\n    </StrictMode>,\n  );\n};\n"],"mappings":"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","names":["context"]}