@noego/wood 0.7.0 → 0.7.1

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.
@@ -596,7 +596,26 @@ function environmentSurface(parts) {
596
596
  }
597
597
  },
598
598
  dispose,
599
- [import_testing.ENV_REGISTRY]: env[import_testing.ENV_REGISTRY]
599
+ // One inspection surface for both owners: a renderer-owned token's watched
600
+ // method is readable through the same environment handle as a main one.
601
+ // The main registry is consulted first; the renderer registry only once the
602
+ // renderer runtime has actually been initialized.
603
+ [import_testing.ENV_REGISTRY]: {
604
+ inspect(token, method) {
605
+ const main = env[import_testing.ENV_REGISTRY];
606
+ const renderer = runtime.renderer?.current?.[import_testing.ENV_REGISTRY];
607
+ try {
608
+ return main.inspect(token, method);
609
+ } catch (error) {
610
+ if (!renderer) throw error;
611
+ try {
612
+ return renderer.inspect(token, method);
613
+ } catch {
614
+ throw error;
615
+ }
616
+ }
617
+ }
618
+ }
600
619
  };
601
620
  }
602
621
  class TestWoodBuilder {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["import { FETCH_RUNTIME } from '@noego/runtime/fetch';\nimport { createTestFetch, defineFetchRoutes, type ConfigureFetch, type TestFetchRuntime } from '@noego/testing/fetch';\nimport type { WoodBrowserHost, WoodTestEnvironmentFor, NativeWoodTestEnvironment } from './native_browser/native_browser.types.js';\nimport { disposeControllerLifecycle, guardControllerLifecycleOnce } from '../controller/controller_lifecycle.js';\nimport type { WoodTestApi, WoodTestRawApi } from '../testing-api.js';\n/**\n * `testWood(configPath)` — the canonical Wood testing entry point.\n *\n * One persistent immutable builder over the production `wood.config.yml`:\n *\n * const app = await testWood('wood.config.yml')\n * .select({ main: { module: 'backend_service' }, renderer: { path: ['settings.backend-service'] } })\n * .class(BackendServiceLifecycleCoordinator, LifecycleStub)\n * .method(BackendServiceLifecycleCoordinator, 'getStatus', test.once(test.returns(status)))\n * .middleware('auth.session:require_session', test.original())\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * canonical singular replacements (`use(testStub())`, `class/function/method/\n * value` on exact declared tokens, `middleware` on source identities, `load`\n * on page/layout companion data-loader sources) decide what is replaced — resolved\n * by the shared `@noego/testing` overlay engine against each owner's declared\n * binding catalog. Without `select()`, every declared owner is selected whole.\n * The legacy plural vocabulary (`classes/functions/values/methods`,\n * `.main()/.renderer()`, `main: true`, `{ operation }`, `pages`) is retained\n * here for existing suites. Controllers, middleware and views are auto-bound\n * from the config; the app composes its backend through the optional\n * `main.testing.compose` hook and mounts pages through `renderer.testing.host`.\n *\n * Every build creates fresh `@noego/ioc` roots (main, and renderer when\n * pages are selected) — nothing global is touched, so environments coexist\n * in one process.\n */\nimport path from 'node:path';\nimport { LoadAs, type IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport {\n applyTestBindings,\n createTestExecutables,\n describeTestBindings,\n ENV_REGISTRY,\n resolveTestOverlays,\n testIoc,\n TestIocBuilder,\n TestStub,\n testStub,\n stubEntries,\n} from '@noego/testing';\nimport type {\n ExecutableReplacement,\n LoaderIdentity,\n LoaderReference,\n MethodDescriptor,\n ClassToken, ReplacementClass,\n ReplacementFunction,\n ReplacementOptions,\n ResolvedTestOverlay,\n TestEnvironment,\n TestExecutableOverlays,\n TestOwnerCatalog,\n} from '@noego/testing';\nimport { installWoodRuntime, WoodRouter } from '../runtime/runtime_module.js';\nimport { assertDataLoader, type DataLoader } from '../loader/loader_orchestration.js';\nimport { MiddlewareResolver } from '../middleware/middleware_resolver.js';\nimport type { WoodMiddlewareContext } from '../types/index.cjs';\nimport type { OperationDef, ViewDef } from '../types/index.cjs';\nimport type { WoodConfig } from '../types/config.cjs';\nimport type { FrontendApplicationSurface } from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { Navigation } from '../navigation/index.js';\nimport { createOperationApi, RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.cjs';\nimport {\n defaultImporter,\n importDefaultExport,\n loadWoodConfigSource,\n type ModuleImporter,\n type WoodConfigSource,\n} from './wood_config_source.cjs';\nimport {\n DECLARED_OWNERS,\n resolveSelection,\n selectOperations,\n selectRendererCatalog,\n type ResolvedWoodSelection,\n type WoodTestSelection,\n} from './wood_test_selection.cjs';\nimport {\n buildRendererTier,\n type RendererTier,\n type WoodDomDriver,\n type WoodRendererHooks,\n} from './wood_env_renderer.cjs';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.cjs';\nimport { LazyRuntimeStage, lazyMember } from './wood_lazy_runtime.cjs';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\n /** Standard per-environment backend push source; hosts subscribe automatically. */\n readonly events: RouterBridgeEvents;\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n readonly selection: ResolvedWoodSelection;\n /** The selected operations (the router will serve exactly these). */\n readonly operations: ReadonlyMap<string, OperationDef>;\n readonly importer: ModuleImporter;\n /**\n * Dispatch through the environment's router. Available from `start()`\n * onward (the router is built after compose returns); calling it during\n * compose itself fails.\n */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n}\n\n/** What the compose hook may hand back; every member is optional. */\nexport interface WoodComposeResult extends WoodRendererHooks {\n /**\n * Runs AFTER the test's substitutions are installed on the root (compose\n * itself runs before them, so production registrations never shadow a\n * seam). Seed world state and warm caches here — through the seamed graph,\n * exactly as the test will observe it.\n */\n start?(): void | Promise<void>;\n /**\n * Quiesce BEFORE the main root disposes its instances: silence push\n * delivery, drain detached work — everything that still needs the live\n * graph (database, tracer) the services' own dispose hooks run against.\n */\n drain?(): void | Promise<void>;\n /** Release resources AFTER the roots are disposed (close files, remove dirs). */\n dispose?(): void | Promise<void>;\n}\n\n/**\n * The app's backend composition on the root the builder hands it. Register\n * lazily: nothing expensive should run until a test resolves it.\n */\nexport type WoodComposeMain = (\n root: IContainer,\n context: WoodComposeContext,\n) => WoodComposeResult | void | Promise<WoodComposeResult | void>;\n\ntype Params = Record<string, string>;\n\nexport interface TestWoodEnvironment {\n /** The main-process IoC root of this environment. */\n readonly root: IContainer;\n readonly router: WoodRouter;\n /** The selected operations. */\n readonly operations: ReadonlyMap<string, OperationDef>;\n /** Typed nested operation API backed by the real selected WoodRouter. */\n readonly api: WoodTestApi;\n /** Object-shaped untyped payload surface for deliberate runtime-validation probes. */\n readonly rawApi: WoodTestRawApi;\n /** Execute a selected operation through the real pipeline. */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n /** Resolve a controller class through a fresh main scope (root seams apply). */\n controller<T>(controller: new (...args: never[]) => T): Promise<T>;\n /** Resolve any dependency from the main root; class tokens retain their instance type. */\n get<T>(token: { readonly prototype: T }): Promise<T>;\n get<T = unknown>(token: unknown): Promise<T>;\n\n /** Renderer surface — present only when `pages` was selected. */\n readonly frontend: FrontendApplicationSurface;\n /** Backend push emitter feeding renderer subscriptions (pages only). */\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n navigate(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n\n /** DOM driver — present only when `dom` was selected. */\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n\n /** Finalize `test.*` expectations; fails on undrained renderer work. */\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: unknown;\n}\n\ntype ControllerClass = new (...args: never[]) => unknown;\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly browserHost: WoodBrowserHost | null;\n readonly fetchConfigurations: readonly ConfigureFetch[];\n readonly importer: ModuleImporter;\n readonly overlays: TestStub;\n}\n\nconst CONTAINER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.container.registry');\n\nfunction globalRegistrySize(): number {\n const registry = (globalThis as Record<symbol, unknown>)[CONTAINER_REGISTRY_SYMBOL];\n return registry instanceof Map ? registry.size : 0;\n}\n\nfunction tierMissing(tier: 'renderer' | 'pages' | 'dom', member: string): never {\n throw new TestWoodError(\n `the ${member} is not available: this environment was built without { ${tier} } — add it to select()`,\n );\n}\n\nasync function loadController(\n source: WoodConfigSource,\n importer: ModuleImporter,\n reference: string,\n owner: 'main' | 'renderer',\n): Promise<ControllerClass> {\n const file = owner === 'main'\n ? source.mainControllerFile(reference)\n : source.rendererControllerFile(reference);\n const controller = await importDefaultExport<ControllerClass>(\n importer,\n file,\n `${owner} controller \"${reference}\"`,\n );\n if (typeof controller !== 'function') {\n throw new TestWoodError(`${owner} controller \"${reference}\" must default-export a class (${file})`);\n }\n return controller;\n}\n\nasync function loadMainControllers(\n source: WoodConfigSource,\n operations: ReadonlyMap<string, OperationDef>,\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n for (const operation of operations.values()) {\n if (!controllers.has(operation.controller)) {\n controllers.set(operation.controller, await loadController(source, importer, operation.controller, 'main'));\n }\n }\n return controllers;\n}\n\nasync function loadRendererControllers(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n const references = new Set<string>();\n for (const view of views) {\n if (view.controller) references.add(view.controller);\n for (const controller of view.layoutControllers ?? []) if (controller) references.add(controller);\n }\n for (const reference of references) {\n controllers.set(reference, await loadController(source, importer, reference, 'renderer'));\n }\n return controllers;\n}\n\nasync function runCompose(\n root: IContainer,\n source: WoodConfigSource,\n context: WoodComposeContext,\n): Promise<WoodComposeResult | null> {\n const composePath = source.hookModule(source.config.main.testing?.compose);\n if (!composePath) return null;\n const compose = await importDefaultExport<WoodComposeMain>(context.importer, composePath, 'main.testing.compose');\n if (typeof compose !== 'function') {\n throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);\n }\n return (await ExecutionContext.run(root as never, () => compose(root, context))) ?? null;\n}\n\n/**\n * Dispose the main root under its own execution context (service teardown\n * hooks resolve collaborators such as tracers exactly like any other call),\n * naming every instance whose dispose hook failed.\n */\nasync function disposeRoot(env: TestEnvironment): Promise<void> {\n try {\n await ExecutionContext.run(env.root as never, () => env.dispose());\n } catch (error) {\n const failures = (error as { failures?: Array<{ token: string; error: unknown }> }).failures;\n if (!Array.isArray(failures)) throw error;\n const detail = failures\n .map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`)\n .join('\\n ');\n throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):\\n ${detail}`);\n }\n}\n\nfunction assertNoGlobalContainerLeak(before: number, source: WoodConfigSource): void {\n if (!process.env.VITEST) return;\n const after = globalRegistrySize();\n if (after > before) {\n throw new TestWoodError(\n `${source.config.main.testing?.compose ?? 'the build'} registered ${after - before} process-global ` +\n 'Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.',\n );\n }\n}\n\ntype SelectedPageCatalog = NonNullable<ReturnType<typeof selectRendererCatalog>>;\ntype ControllerScope = { dispose(): Promise<void> };\n\n/** Per-build executable seams (`.middleware` on main, `.load` on renderer). */\ninterface AssemblyExecutables {\n readonly main: TestExecutableOverlays;\n readonly renderer: TestExecutableOverlays;\n /** Middleware identities the test replaced (only these get a registry executable). */\n readonly middleware: ReadonlySet<string>;\n}\n\ninterface AssemblyLiveState {\n composed: WoodComposeResult | null;\n env: TestEnvironment | null;\n renderer: RendererTier | null;\n router: WoodRouter | null;\n /** Set once overlays are resolved (before the router is constructed on first use). */\n executables: AssemblyExecutables | null;\n /** Set once the environment's runtime stages exist (after build). */\n runtime: AssemblyRuntime | null;\n /** dispose() was requested; a runtime that has not started must not start. */\n disposed: boolean;\n /** A runtime stage already rolled the environment back; never roll back twice. */\n rolledBack: boolean;\n}\n\n/** The managed main runtime: constructed router + `compose.start()` done. */\ninterface MainRuntime {\n readonly router: WoodRouter;\n}\n\n/**\n * Lazily initialized runtime stages. `main` constructs the managed router\n * and runs `compose.start()`; `renderer` (pages only) builds the renderer\n * tier over the main router. Each initializes exactly once on first use.\n */\ninterface AssemblyRuntime {\n readonly main: LazyRuntimeStage<MainRuntime>;\n readonly renderer: LazyRuntimeStage<RendererTier> | null;\n}\n\ninterface AssemblyInputs {\n readonly network: TestFetchRuntime;\n readonly state: BuilderState;\n readonly selection: ResolvedWoodSelection;\n readonly source: WoodConfigSource;\n readonly operations: Map<string, OperationDef>;\n readonly catalog: SelectedPageCatalog | null;\n readonly registryBefore: number;\n readonly mainRegistrations: RegistrationOnlyRoot;\n readonly rendererRegistrations: RegistrationOnlyRoot | null;\n readonly controllerScopes: ControllerScope[];\n readonly live: AssemblyLiveState;\n readonly context: WoodComposeContext;\n readonly dispose: () => Promise<void>;\n}\n\nfunction createComposeContext(\n source: WoodConfigSource,\n state: BuilderState,\n selection: ResolvedWoodSelection,\n operations: Map<string, OperationDef>,\n live: AssemblyLiveState,\n): WoodComposeContext {\n return {\n events: new RouterBridgeEvents(),\n configPath: source.configPath,\n configDir: source.configDir,\n config: source.config,\n selection,\n operations,\n importer: state.importer,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!live.env || (!live.router && !live.runtime)) {\n throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n }\n // Inside start() the router already exists (it is constructed before\n // start runs); after build, the first call initializes the runtime.\n const router = live.router ?? (await live.runtime!.main.acquire()).router;\n return (await ExecutionContext.run(\n live.env.root as never,\n () => router.dispatch(operation, input),\n )) as T;\n },\n };\n}\n\nfunction createAssemblyDisposer(\n live: AssemblyLiveState,\n mainRegistrations: RegistrationOnlyRoot,\n rendererRegistrations: RegistrationOnlyRoot | null,\n controllerScopes: ControllerScope[],\n network: TestFetchRuntime,\n): () => Promise<void> {\n let closing: Promise<void> | null = null;\n return (): Promise<void> => {\n live.disposed = true;\n return closing ??= cleanupAll('testWood cleanup failed', [\n // An initialization racing this dispose finishes first (it refuses to\n // complete once `disposed` is set) so every resource it created late is\n // released below. A rollback-triggered dispose skips this: its own\n // stage has already finished and waiting would be a self-await.\n ['runtime settle', async () => {\n if (live.rolledBack || !live.runtime) return;\n await live.runtime.main.settled();\n await live.runtime.renderer?.settled();\n }],\n ['renderer', async () => { if (live.renderer) await live.renderer.dispose(); }],\n ...[...controllerScopes].reverse().map(\n (scope, index) => [`controller scope ${controllerScopes.length - index}`, () => scope.dispose()] as const,\n ),\n ['compose drain', async () => {\n const root = live.env?.root ?? mainRegistrations.actualRoot;\n if (!root.isDisposed()) await ExecutionContext.run(root as never, () => live.composed?.drain?.());\n }],\n ['main root', async () => {\n if (live.env) await disposeRoot(live.env);\n else await mainRegistrations.actualRoot.dispose();\n }],\n ['unbuilt renderer root', async () => {\n if (!live.renderer && rendererRegistrations) await rendererRegistrations.actualRoot.dispose();\n }],\n ['compose external dispose', async () => { await live.composed?.dispose?.(); }],\n ['external fetch', () => network.dispose()],\n ]);\n };\n}\n\nasync function loadAssemblyControllers(\n inputs: AssemblyInputs,\n): Promise<{\n main: Map<string, ControllerClass>;\n renderer: Map<string, ControllerClass>;\n loaders: Map<string, DataLoader>;\n}> {\n const main = await loadMainControllers(inputs.source, inputs.operations, inputs.state.importer);\n const renderer = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererControllers(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, ControllerClass>();\n const loaders = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererLoaders(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, DataLoader>();\n return { main, renderer, loaders };\n}\n\nfunction installAssemblyRuntime(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n installWoodRuntime(inputs.mainRegistrations.root, {\n controllersDir: inputs.source.mainControllersDir,\n middlewareDir: inputs.source.mainMiddlewareDir,\n mode: 'dev',\n registerRuntime: (configuredRouter) => {\n for (const [identity, controller] of mainControllers) {\n configuredRouter.registerControllerLoader(identity, () => Promise.resolve({ default: controller }));\n }\n configuredRouter.loadOperations(inputs.operations);\n registerMiddlewareSeams(inputs, configuredRouter);\n },\n });\n}\n\n/**\n * `.middleware(identity, …)`: the production identity keeps its membership,\n * order and inheritance (they come from the operations source); only the\n * executable satisfying it is replaced, through the router's registry seam.\n * The source executable is resolved lazily from `main.middleware`, exactly\n * as production would, and handed to the shared test.* runtime once.\n */\nfunction registerMiddlewareSeams(inputs: AssemblyInputs, configuredRouter: WoodRouter): void {\n const executables = inputs.live.executables;\n if (!executables || !executables.middleware.size) return;\n const sourceResolver = new MiddlewareResolver(inputs.source.mainMiddlewareDir);\n for (const spec of executables.middleware) {\n configuredRouter.registerMiddleware(spec, async (context: WoodMiddlewareContext) => {\n const original = await sourceResolver.resolve(spec);\n return executables.main.replace('middleware', spec, original as ReplacementFunction)(context);\n });\n }\n}\n\n/** Production middleware identities the selected operations run, in source order. */\nfunction selectedMiddleware(operations: ReadonlyMap<string, OperationDef>): string[] {\n const identities = new Set<string>();\n for (const operation of operations.values()) for (const spec of operation.middleware) identities.add(spec);\n return [...identities];\n}\n\n/**\n * Discover and import every companion data loader (`pages/chat.svelte` →\n * `pages/chat.load.ts`) of the selected pages/layouts. Discovery and import\n * happen at build (a missing default loader function fails the build); no\n * loader RUNS until a page opens. The identity is the config-relative\n * source path with export `default` — the `.load` seam identity.\n */\nasync function loadRendererLoaders(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, DataLoader>> {\n const loaders = new Map<string, DataLoader>();\n for (const view of views) {\n for (const viewPath of [...view.layouts, view.viewPath]) {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference || loaders.has(reference.identity)) continue;\n const mod = (await importer(reference.file)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new TestWoodError(`data loader ${reference.identity} (${reference.file}) has no default export`);\n }\n try {\n loaders.set(reference.identity, assertDataLoader(mod.default, `data loader ${reference.identity}`));\n } catch (error) {\n throw new TestWoodError(error instanceof Error ? error.message : String(error));\n }\n }\n }\n return loaders;\n}\n\n/** The native renderer loader identities: every discovered companion loader source. */\nfunction rendererLoaderReferences(loaders: ReadonlyMap<string, DataLoader>): LoaderReference[] {\n return [...loaders.keys()].map((identity) => Object.freeze({ path: identity, export: 'default' }));\n}\n\nasync function configureRendererRegistrations(inputs: AssemblyInputs): Promise<void> {\n const registrations = inputs.rendererRegistrations;\n if (!registrations) return;\n // Bound by the renderer tier on first use (see buildRendererTier); declared\n // here so the shared overlay catalog can resolve replacements for them.\n registrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n registrations.declareBinding(Navigation, 'singleton');\n registrations.declareBinding(WOOD_FRONTEND_BRIDGE, 'singleton');\n const composePath = inputs.source.hookModule(inputs.source.config.renderer.compose);\n if (composePath) {\n const composeRenderer = await importDefaultExport<(root: IContainer) => void | Promise<void>>(\n inputs.state.importer,\n composePath,\n 'renderer.compose',\n );\n if (typeof composeRenderer !== 'function') {\n throw new TestWoodError(`renderer.compose default export must be a function (${composePath})`);\n }\n await ExecutionContext.run(registrations.root as never, () => composeRenderer(registrations.root));\n }\n await ExecutionContext.run(\n registrations.root as never,\n () => inputs.live.composed?.configureRendererRoot?.(registrations.root),\n );\n}\n\nfunction registerControllersBeforeOverlays(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n rendererControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n for (const controller of mainControllers.values()) {\n inputs.mainRegistrations.declare(controller);\n if (!inputs.mainRegistrations.actualRoot.isRegistered(controller)) {\n inputs.mainRegistrations.root.registerClass(controller);\n }\n }\n for (const controller of rendererControllers.values()) {\n inputs.rendererRegistrations?.declare(controller);\n }\n}\n\ninterface AssemblyIoc {\n readonly main: TestIocBuilder;\n readonly mainBorrowed: readonly unknown[];\n readonly renderer: TestIocBuilder;\n readonly rendererBorrowed: readonly unknown[];\n readonly overlays: readonly ResolvedTestOverlay[];\n readonly executables: AssemblyExecutables;\n}\n\n/**\n * Legacy plural calls are routed by owner token sets (unchanged); the\n * canonical singular replacements are resolved by the shared overlay engine\n * against each owner's DECLARED binding catalog — production registrations\n * recorded on the root, legacy explicit composition, auto-bound controllers,\n * the selected middleware identities and the page/layout loader identities.\n */\nfunction buildAssemblyIoc(\n inputs: AssemblyInputs,\n controllers: {\n main: ReadonlyMap<string, ControllerClass>;\n renderer: ReadonlyMap<string, ControllerClass>;\n loaders: ReadonlyMap<string, DataLoader>;\n },\n): AssemblyIoc {\n const catalogs: TestOwnerCatalog[] = [{\n owner: 'main',\n bindings: describeTestBindings(testIoc().use(inputs.mainRegistrations.module()), [...controllers.main.values()]),\n middleware: selectedMiddleware(inputs.operations),\n loaders: [],\n }];\n if (inputs.rendererRegistrations && inputs.catalog) {\n catalogs.push({\n owner: 'renderer',\n bindings: describeTestBindings(testIoc().use(inputs.rendererRegistrations.module()), [...controllers.renderer.values()]),\n middleware: [],\n loaders: rendererLoaderReferences(controllers.loaders),\n });\n }\n const overlays = resolveTestOverlays(inputs.state.overlays, catalogs);\n const mainPlan = applyTestBindings(testIoc(), overlays, 'main');\n const rendererPlan = applyTestBindings(testIoc(), overlays, 'renderer');\n return {\n main: mainPlan.builder,\n mainBorrowed: mainPlan.buildOptions.borrowedTokens ?? [],\n renderer: rendererPlan.builder,\n rendererBorrowed: rendererPlan.buildOptions.borrowedTokens ?? [],\n overlays,\n executables: {\n main: createTestExecutables(overlays, 'main'),\n renderer: createTestExecutables(overlays, 'renderer'),\n middleware: new Set(overlays\n .filter((overlay) => overlay.owner === 'main' && overlay.entry.kind === 'middleware')\n .map((overlay) => overlay.entry.target as string)),\n },\n };\n}\n\n/**\n * Roll the whole environment back after a failed first use: every resource a\n * partially initialized runtime created is released, and both the primary\n * error and any cleanup failures are preserved. Runs at most once.\n */\nfunction createRuntimeRollback(inputs: AssemblyInputs): (error: unknown) => Promise<never> {\n return (error) => {\n // Already rolled back, or dispose() is in flight / done: that disposer\n // owns the cleanup (it waits for this attempt), so never re-enter it here.\n if (inputs.live.rolledBack || inputs.live.disposed) return Promise.reject(error);\n inputs.live.rolledBack = true;\n return rollbackEnvironment(error, inputs.dispose, 'testWood runtime initialization failed and rollback failed');\n };\n}\n\nfunction assertRuntimeMayStart(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed before its ${stage} runtime was used`);\n }\n}\n\n/**\n * Re-checked after every await of a first initialization: dispose() may have\n * been requested meanwhile. The disposer waits for this attempt, so failing\n * here hands every late resource to it instead of exposing a disposed root.\n */\nfunction assertRuntimeMayContinue(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed while its ${stage} runtime was initializing`);\n }\n}\n\n/** First use: construct the managed router, then run `compose.start()` on the seamed graph. */\nasync function initializeMainRuntime(inputs: AssemblyInputs): Promise<MainRuntime> {\n assertRuntimeMayStart(inputs, 'main');\n const root = inputs.live.env!.root;\n const router = await Promise.resolve(\n ExecutionContext.run(root as never, () => root.get(WoodRouter)),\n ) as WoodRouter;\n inputs.live.router = router;\n assertRuntimeMayContinue(inputs, 'main');\n await ExecutionContext.run(root as never, () => inputs.live.composed?.start?.());\n assertRuntimeMayContinue(inputs, 'main');\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n return { router };\n}\n\ninterface RendererRuntimeInputs {\n readonly ioc: TestIocBuilder;\n readonly borrowedTokens: readonly unknown[];\n readonly controllerClasses: ReadonlyMap<string, ControllerClass>;\n readonly events: RouterBridgeEvents;\n readonly loaders: TestExecutableOverlays;\n /** Imported companion data loaders by `.load` identity (none has run). */\n readonly loaderModules: ReadonlyMap<string, DataLoader>;\n}\n\n/** First renderer use: the main runtime, then the renderer tier over its router. */\nasync function initializeRendererRuntime(\n inputs: AssemblyInputs,\n main: LazyRuntimeStage<MainRuntime>,\n renderer: RendererRuntimeInputs,\n): Promise<RendererTier> {\n const { router } = await main.acquire();\n assertRuntimeMayStart(inputs, 'renderer');\n const composed = inputs.live.composed;\n const configurePageScope = composed?.configurePageScope?.bind(composed);\n const hooks: WoodRendererHooks = {\n events: renderer.events,\n ...(configurePageScope ? { configurePageScope } : {}),\n };\n const tier = await buildRendererTier({\n source: inputs.source,\n router,\n catalog: inputs.catalog!,\n operations: inputs.operations,\n importer: inputs.state.importer,\n hooks,\n dom: null,\n ioc: renderer.ioc,\n borrowedTokens: renderer.borrowedTokens,\n registrations: inputs.rendererRegistrations!,\n controllerClasses: renderer.controllerClasses,\n loaders: renderer.loaders,\n loaderModules: renderer.loaderModules,\n });\n // Hand the tier to the disposer before refusing: a dispose() that raced\n // this build releases it through the regular 'renderer' cleanup step.\n inputs.live.renderer = tier;\n assertRuntimeMayContinue(inputs, 'renderer');\n return tier;\n}\n\nfunction createAssemblyRuntime(\n inputs: AssemblyInputs,\n renderer: RendererRuntimeInputs | null,\n): AssemblyRuntime {\n const rollback = createRuntimeRollback(inputs);\n const main = new LazyRuntimeStage<MainRuntime>(() => initializeMainRuntime(inputs), rollback);\n const rendererStage = renderer\n ? new LazyRuntimeStage<RendererTier>(() => initializeRendererRuntime(inputs, main, renderer), rollback)\n : null;\n return { main, renderer: rendererStage };\n}\n\n/**\n * Build: read the source, record registrations (compose, runtime module,\n * renderer hooks, controllers) and install the test overlays on the main root.\n * Nothing is resolved or started here — the managed router, `compose.start()`\n * and the renderer tier initialize on first explicit use.\n */\nasync function materializeEnvironment(inputs: AssemblyInputs): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n inputs.mainRegistrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n const controllers = await loadAssemblyControllers(inputs);\n inputs.live.composed = await runCompose(inputs.mainRegistrations.root, inputs.source, inputs.context);\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n installAssemblyRuntime(inputs, controllers.main);\n await configureRendererRegistrations(inputs);\n registerControllersBeforeOverlays(inputs, controllers.main, controllers.renderer);\n const ioc = buildAssemblyIoc(inputs, controllers);\n inputs.live.executables = ioc.executables;\n\n inputs.live.env = await buildRecordedEnvironment(ioc.main, inputs.mainRegistrations, 'main', ioc.mainBorrowed);\n const root = inputs.live.env.root;\n\n const renderer: RendererRuntimeInputs | null = inputs.catalog && inputs.rendererRegistrations\n ? {\n ioc: ioc.renderer,\n borrowedTokens: ioc.rendererBorrowed,\n controllerClasses: controllers.renderer,\n events: inputs.live.composed?.events ?? inputs.context.events,\n loaders: ioc.executables.renderer,\n loaderModules: controllers.loaders,\n }\n : null;\n const runtime = createAssemblyRuntime(inputs, renderer);\n inputs.live.runtime = runtime;\n\n const environment = environmentSurface({\n root,\n network: inputs.network,\n env: inputs.live.env,\n operations: inputs.operations,\n runtime,\n allOperations: inputs.source.operations,\n events: renderer?.events ?? inputs.live.composed?.events ?? inputs.context.events,\n dom: false,\n controllerScopes: inputs.controllerScopes,\n executables: ioc.executables,\n dispose: inputs.dispose,\n });\n if (inputs.state.browserHost) {\n const { createNativeBrowserEnvironment } = await import('./native_browser/native_browser_environment.js');\n return createNativeBrowserEnvironment({\n source: inputs.source,\n views: inputs.catalog!.all(),\n environment,\n network: inputs.network,\n events: () => inputs.live.composed?.events ?? inputs.context.events,\n }, inputs.state.browserHost);\n }\n return environment;\n}\n\nasync function rollbackEnvironment(\n error: unknown,\n dispose: () => Promise<void>,\n message = 'testWood build failed and rollback failed',\n): Promise<never> {\n try {\n await dispose();\n } catch (cleanupError) {\n const failures = [\n ...(error instanceof TestWoodCleanupError ? error.failures : []),\n ...(cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'build rollback', error: cleanupError }]),\n ];\n throw new TestWoodCleanupError(message, failures, error);\n }\n throw error;\n}\n\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selected: ResolvedWoodSelection | null,\n): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n const source = loadWoodConfigSource(state.configPath);\n // No selection: every owner the config declares, whole ({}). A Wood\n // config declares both `main` and `renderer` sections.\n let selection = selected ?? resolveSelection(\n Object.fromEntries(DECLARED_OWNERS.filter((owner) => source.config[owner]).map((owner) => [owner, {}])),\n );\n // An omitted main owner exposes no operations; a selected renderer still gets\n // a main root for dependencies but does not widen the main operation aperture.\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selectRendererCatalog(source.views, selection);\n if (state.browserHost && !catalog) {\n throw new TestWoodError('browser() requires a selected renderer owner; add renderer to select()');\n }\n if (state.browserHost && stubEntries(state.overlays).some(entry => entry.kind === 'load' || entry.owner === 'renderer')) {\n throw new TestWoodError('Native browser renderer replacements are not supported yet: renderer code executes in its own realm. Keep main-process seams, or use the existing in-process renderer test environment for renderer .class/.method/.value/.load seams.');\n }\n const network = createTestFetch(state.fetchConfigurations.flatMap(defineFetchRoutes));\n const registryBefore = globalRegistrySize();\n const mainRegistrations = new RegistrationOnlyRoot('main.testing.compose', source.configPath);\n const rendererRegistrations = catalog && !state.browserHost\n ? new RegistrationOnlyRoot('configureRendererRoot', source.configPath)\n : null;\n const controllerScopes: ControllerScope[] = [];\n const live: AssemblyLiveState = {\n composed: null,\n env: null,\n renderer: null,\n router: null,\n executables: null,\n runtime: null,\n disposed: false,\n rolledBack: false,\n };\n const context = createComposeContext(source, state, selection, operations, live);\n const dispose = createAssemblyDisposer(\n live,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n network,\n );\n const inputs: AssemblyInputs = {\n network,\n state,\n selection,\n source,\n operations,\n catalog,\n registryBefore,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n live,\n context,\n dispose,\n };\n\n try {\n return await materializeEnvironment(inputs);\n } catch (error) {\n return rollbackEnvironment(error, dispose);\n }\n}\n\ninterface SurfaceParts {\n network: TestFetchRuntime;\n root: IContainer;\n env: TestEnvironment;\n operations: Map<string, OperationDef>;\n runtime: AssemblyRuntime;\n /** Every production operation, used to keep `api` object shape stable across selections. */\n allOperations: ReadonlyMap<string, OperationDef>;\n /** Backend push emitter (pages only); available from build, before any renderer use. */\n events: RouterBridgeEvents | null;\n dom: boolean;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n executables: AssemblyExecutables;\n dispose(): Promise<void>;\n}\n\nconst OPEN_FIRST = 'await open(page) (or another renderer call) first';\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, operations, allOperations, runtime, events, dom: domSelected, controllerScopes, executables, dispose } = parts;\n const pages = (): LazyRuntimeStage<RendererTier> => runtime.renderer ?? tierMissing('pages', 'renderer');\n const dom = async (): Promise<WoodDomDriver> => {\n if (!domSelected) tierMissing('dom', 'dom driver');\n return (await pages().acquire()).dom!;\n };\n\n // Stable lazy members: async use initializes the runtime; synchronous\n // members are served once it is live and fail with guidance before that.\n const router = lazyMember<WoodRouter>(\n 'router',\n runtime.main.map((main) => main.router),\n ['dispatch'],\n 'await call()/get()/controller() first',\n );\n const frontend = runtime.renderer\n ? lazyMember<FrontendApplicationSurface>(\n 'frontend',\n runtime.renderer.map((tier) => tier.frontend),\n ['open', 'act', 'settle', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n const rendererRoot = runtime.renderer\n ? lazyMember<IContainer>(\n 'rendererRoot',\n runtime.renderer.map((tier) => tier.rendererRoot),\n ['get', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n\n const dispatchOperation = async (operation: string, input?: unknown): Promise<unknown> => {\n if (!operations.has(operation)) {\n throw new TestWoodError(\n `operation \"${operation}\" is not in the selected aperture; available operations:\\n ${[...operations.keys()].join('\\n ')}`,\n );\n }\n const { router: live } = await runtime.main.acquire();\n return await ExecutionContext.run(root as never, () => live.dispatch(operation, input));\n };\n const api = createOperationApi(allOperations.keys(), dispatchOperation) as WoodTestApi;\n\n return {\n root,\n router,\n operations,\n api,\n rawApi: api as WoodTestRawApi,\n call: dispatchOperation as TestWoodEnvironment['call'],\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n await runtime.main.acquire();\n const scope = root.extend();\n const ownsController = !root.isRegistered(controller);\n let instance: T | undefined;\n controllerScopes.push({ dispose: () => ExecutionContext.run(scope as never, () => cleanupAll('controller cleanup failed', [\n ['controller', async () => { if (ownsController) await disposeControllerLifecycle(instance); }],\n ['scope', () => scope.dispose()],\n ])) as Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n instance = await ExecutionContext.run(scope as never, () => scope.get(controller)) as T;\n guardControllerLifecycleOnce(instance);\n return instance;\n },\n get: async <T>(token: unknown): Promise<T> => {\n await runtime.main.acquire();\n return (await ExecutionContext.run(root as never, () => root.get(token))) as T;\n },\n\n get frontend() { return frontend ?? tierMissing('pages', 'renderer'); },\n get events() { return events ?? tierMissing('pages', 'renderer'); },\n get rendererRoot() { return rendererRoot ?? tierMissing('pages', 'renderer'); },\n open: async (page, params, query) => (await pages().acquire()).open(page, params, query),\n navigate: async (page, params, query) => (await pages().acquire()).navigate(page, params, query),\n current: () => pages().current?.current() ?? null,\n settle: async () => {\n await parts.network.settle();\n // Finalization applies to main-only roots too. Waiting must never start\n // an unused product or renderer merely to discover that it is idle.\n if (runtime.main.requested) await (await runtime.main.acquire()).router.drain();\n if (runtime.renderer?.requested) await (await runtime.renderer.acquire()).settle();\n },\n\n get target() {\n if (!domSelected) tierMissing('dom', 'dom driver');\n const tier = pages().current;\n if (!tier) throw new TestWoodError('no page is mounted; open(page) first');\n return tier.dom!.target;\n },\n click: async (selector) => (await dom()).click(selector),\n type: async (selector, text) => (await dom()).type(selector, text),\n submit: async (selector) => (await dom()).submit(selector),\n text: async (selector) => (await dom()).text(selector),\n exists: async (selector) => (await dom()).exists(selector),\n\n // Method expectations only: `settle()` is the completion contract for\n // owned renderer work, and a test may legitimately verify while a\n // deliberately deferred stream is still in flight.\n verify: async () => {\n const failures: Array<{ owner: string; error: unknown }> = [];\n await env.verify().catch((error) => failures.push({ owner: 'main', error }));\n await executables.main.verify().catch((error) => failures.push({ owner: 'main middleware', error }));\n const renderer = runtime.renderer?.current ?? null;\n if (renderer) {\n await renderer.verify().catch((error) => failures.push({ owner: 'renderer', error }));\n }\n await executables.renderer.verify().catch((error) => failures.push({ owner: 'renderer loaders', error }));\n if (failures.length === 1) throw failures[0]!.error;\n if (failures.length) {\n throw new TestWoodError(\n 'testWood verification failed in several owners:\\n ' +\n failures.map(({ owner, error }) =>\n `${owner}: ${error instanceof Error ? error.message : String(error)}`).join('\\n '),\n );\n }\n },\n dispose,\n [ENV_REGISTRY]: (env as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n\nexport class TestWoodBuilder<H extends WoodBrowserHost | null = null> {\n private constructor(private readonly state: BuilderState) {}\n\n static create(configPath: string): TestWoodBuilder {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n return new TestWoodBuilder({\n configPath: path.resolve(configPath),\n selection: null,\n browserHost: null,\n fetchConfigurations: [],\n importer: defaultImporter,\n overlays: testStub(),\n });\n }\n\n fetch(configure: ConfigureFetch): TestWoodBuilder<H> {\n if (typeof configure !== 'function') throw new TypeError('fetch() requires a route configuration callback');\n return this.derive({fetchConfigurations: Object.freeze([...this.state.fetchConfigurations, configure])});\n }\n\n browser(): TestWoodBuilder<'playwright'>;\n browser(host: 'playwright'): TestWoodBuilder<'playwright'>;\n browser(host: 'happy_dom'): TestWoodBuilder<'happy_dom'>;\n browser(host: WoodBrowserHost = 'playwright'): TestWoodBuilder<WoodBrowserHost> {\n if (host !== 'playwright' && host !== 'happy_dom') throw new TestWoodError('browser() accepts playwright or happy_dom');\n return new TestWoodBuilder<WoodBrowserHost>({...this.state, browserHost: host});\n }\n\n /** Declare selected tiers, snapshotting the source selector at call time. */\n select(selection: WoodTestSelection): TestWoodBuilder<H> {\n const resolved = resolveSelection(selection);\n return this.derive({ selection: Object.freeze({\n ...(resolved.main !== null ? { main: resolved.main } : {}),\n ...(resolved.renderer !== null ? { renderer: resolved.renderer } : {}),\n }) });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder<H> {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /** Compose a reusable canonical testStub() replacement description. */\n use(preset: TestStub): TestWoodBuilder<H> {\n if (!(preset instanceof TestStub)) throw new TestWoodError('use() requires testStub()');\n return this.derive({ overlays: this.state.overlays.use(preset) });\n }\n\n /* -- canonical singular replacements (shared @noego/testing engine) ------ */\n\n /** Replace the class bound to an exact declared token; the declared lifetime is kept. */\n class<T>(target: ClassToken<T>, replacement: ReplacementClass<T>, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.class(target, replacement, options) });\n }\n\n /** Replace a declared factory/provider (dependencies and lifetime kept) or a callable value. */\n function(target: unknown, replacement: ReplacementFunction, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.function(target, replacement, options) });\n }\n\n /** A test.* method seam on the FINAL binding of an exact declared token. */\n method(target: unknown, method: string, replacement: MethodDescriptor, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.method(target, method, replacement, options) });\n }\n\n /** Provide a caller-owned value for an exact declared token (never disposed by the app). */\n value(target: unknown, replacement: unknown, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.value(target, replacement, options) });\n }\n\n /**\n * Replace the executable satisfying a production middleware identity\n * (`file.path:selector` as written in the operations source). Membership,\n * order and inheritance always come from the source.\n */\n middleware(identity: string, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.middleware(identity, replacement, options) });\n }\n\n /**\n * Replace the DATA LOADER behind a selected page/layout: the companion\n * `.load` source of a view (`pages/chat.svelte` → `pages/chat.load.ts`).\n * The identity is the config-relative source path (`ui/pages/chat.load.ts`),\n * export `default`; the executable receives the loader `RequestData`\n * (`url` = page identity, `params`, `query`) and yields the data the\n * controller's `initialize({ params, query, data })` receives. A source\n * with no companion loader is not a declared identity (build error).\n * Controllers are replaced with `.class`, never here.\n */\n load(identity: LoaderIdentity, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.load(identity, replacement, options) });\n }\n\n /** Build; without `select()`, every owner the config declares is selected whole. */\n async build(): Promise<WoodTestEnvironmentFor<H>> {\n return await assembleEnvironment(this.state, this.state.selection ? resolveSelection(this.state.selection) : null) as WoodTestEnvironmentFor<H>;\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder<H> {\n return new TestWoodBuilder({ ...this.state, ...patch });\n }\n}\n\nexport function testWood(configPath: string): TestWoodBuilder {\n return TestWoodBuilder.create(configPath);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA8B;AAC9B,IAAAA,gBAA+F;AAE/F,kCAAyE;AA8BzE,uBAAiB;AACjB,iBAAwC;AACxC,IAAAC,cAAiC;AACjC,qBAWO;AAcP,4BAA+C;AAC/C,kCAAkD;AAClD,iCAAmC;AAMnC,wBAA2B;AAC3B,kCAAuD;AACvD,uBAAoD;AACpD,gCAMO;AACP,iCAOO;AACP,+BAKO;AACP,oCAKO;AACP,+BAA6C;AAwG7C,MAAM,4BAA4B,uBAAO,IAAI,gCAAgC;AAE7E,SAAS,qBAA6B;AACpC,QAAM,WAAY,WAAuC,yBAAyB;AAClF,SAAO,oBAAoB,MAAM,SAAS,OAAO;AACnD;AAEA,SAAS,YAAY,MAAoC,QAAuB;AAC9E,QAAM,IAAI;AAAA,IACR,OAAO,MAAM,2DAA2D,IAAI;AAAA,EAC9E;AACF;AAEA,eAAe,eACb,QACA,UACA,WACA,OAC0B;AAC1B,QAAM,OAAO,UAAU,SACnB,OAAO,mBAAmB,SAAS,IACnC,OAAO,uBAAuB,SAAS;AAC3C,QAAM,aAAa,UAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA,GAAG,KAAK,gBAAgB,SAAS;AAAA,EACnC;AACA,MAAI,OAAO,eAAe,YAAY;AACpC,UAAM,IAAI,+BAAc,GAAG,KAAK,gBAAgB,SAAS,kCAAkC,IAAI,GAAG;AAAA,EACpG;AACA,SAAO;AACT;AAEA,eAAe,oBACb,QACA,YACA,UACuC;AACvC,QAAM,cAAc,oBAAI,IAA6B;AACrD,aAAW,aAAa,WAAW,OAAO,GAAG;AAC3C,QAAI,CAAC,YAAY,IAAI,UAAU,UAAU,GAAG;AAC1C,kBAAY,IAAI,UAAU,YAAY,MAAM,eAAe,QAAQ,UAAU,UAAU,YAAY,MAAM,CAAC;AAAA,IAC5G;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,wBACb,QACA,OACA,UACuC;AACvC,QAAM,cAAc,oBAAI,IAA6B;AACrD,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,WAAY,YAAW,IAAI,KAAK,UAAU;AACnD,eAAW,cAAc,KAAK,qBAAqB,CAAC,EAAG,KAAI,WAAY,YAAW,IAAI,UAAU;AAAA,EAClG;AACA,aAAW,aAAa,YAAY;AAClC,gBAAY,IAAI,WAAW,MAAM,eAAe,QAAQ,UAAU,WAAW,UAAU,CAAC;AAAA,EAC1F;AACA,SAAO;AACT;AAEA,eAAe,WACb,MACA,QACA,SACmC;AACnC,QAAM,cAAc,OAAO,WAAW,OAAO,OAAO,KAAK,SAAS,OAAO;AACzE,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,UAAU,UAAM,+CAAqC,QAAQ,UAAU,aAAa,sBAAsB;AAChH,MAAI,OAAO,YAAY,YAAY;AACjC,UAAM,IAAI,+BAAc,2DAA2D,WAAW,GAAG;AAAA,EACnG;AACA,SAAQ,MAAM,6BAAiB,IAAI,MAAe,MAAM,QAAQ,MAAM,OAAO,CAAC,KAAM;AACtF;AAOA,eAAe,YAAY,KAAqC;AAC9D,MAAI;AACF,UAAM,6BAAiB,IAAI,IAAI,MAAe,MAAM,IAAI,QAAQ,CAAC;AAAA,EACnE,SAAS,OAAO;AACd,UAAM,WAAY,MAAkE;AACpF,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,OAAM;AACpC,UAAM,SAAS,SACZ,IAAI,CAAC,YAAY,GAAG,QAAQ,KAAK,KAAK,QAAQ,iBAAiB,QAAQ,QAAQ,MAAM,UAAU,OAAO,QAAQ,KAAK,CAAC,EAAE,EACtH,KAAK,MAAM;AACd,UAAM,IAAI,+BAAc,iCAAiC,SAAS,MAAM;AAAA,IAAoB,MAAM,EAAE;AAAA,EACtG;AACF;AAEA,SAAS,4BAA4B,QAAgB,QAAgC;AACnF,MAAI,CAAC,QAAQ,IAAI,OAAQ;AACzB,QAAM,QAAQ,mBAAmB;AACjC,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR,GAAG,OAAO,OAAO,KAAK,SAAS,WAAW,WAAW,eAAe,QAAQ,MAAM;AAAA,IAEpF;AAAA,EACF;AACF;AA2DA,SAAS,qBACP,QACA,OACA,WACA,YACA,MACoB;AACpB,SAAO;AAAA,IACL,QAAQ,IAAI,+CAAmB;AAAA,IAC/B,YAAY,OAAO;AAAA,IACnB,WAAW,OAAO;AAAA,IAClB,QAAQ,OAAO;AAAA,IACf;AAAA,IACA;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,MAAM,OAAU,WAAmB,UAAgC;AACjE,UAAI,CAAC,KAAK,OAAQ,CAAC,KAAK,UAAU,CAAC,KAAK,SAAU;AAChD,cAAM,IAAI,+BAAc,6EAA6E;AAAA,MACvG;AAGA,YAAM,SAAS,KAAK,WAAW,MAAM,KAAK,QAAS,KAAK,QAAQ,GAAG;AACnE,aAAQ,MAAM,6BAAiB;AAAA,QAC7B,KAAK,IAAI;AAAA,QACT,MAAM,OAAO,SAAS,WAAW,KAAK;AAAA,MACxC;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,uBACP,MACA,mBACA,uBACA,kBACA,SACqB;AACrB,MAAI,UAAgC;AACpC,SAAO,MAAqB;AAC1B,SAAK,WAAW;AAChB,WAAO,0BAAY,0CAAW,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA,MAKvD,CAAC,kBAAkB,YAAY;AAC7B,YAAI,KAAK,cAAc,CAAC,KAAK,QAAS;AACtC,cAAM,KAAK,QAAQ,KAAK,QAAQ;AAChC,cAAM,KAAK,QAAQ,UAAU,QAAQ;AAAA,MACvC,CAAC;AAAA,MACD,CAAC,YAAY,YAAY;AAAE,YAAI,KAAK,SAAU,OAAM,KAAK,SAAS,QAAQ;AAAA,MAAG,CAAC;AAAA,MAC9E,GAAG,CAAC,GAAG,gBAAgB,EAAE,QAAQ,EAAE;AAAA,QACjC,CAAC,OAAO,UAAU,CAAC,oBAAoB,iBAAiB,SAAS,KAAK,IAAI,MAAM,MAAM,QAAQ,CAAC;AAAA,MACjG;AAAA,MACA,CAAC,iBAAiB,YAAY;AAC5B,cAAM,OAAO,KAAK,KAAK,QAAQ,kBAAkB;AACjD,YAAI,CAAC,KAAK,WAAW,EAAG,OAAM,6BAAiB,IAAI,MAAe,MAAM,KAAK,UAAU,QAAQ,CAAC;AAAA,MAClG,CAAC;AAAA,MACD,CAAC,aAAa,YAAY;AACxB,YAAI,KAAK,IAAK,OAAM,YAAY,KAAK,GAAG;AAAA,YACnC,OAAM,kBAAkB,WAAW,QAAQ;AAAA,MAClD,CAAC;AAAA,MACD,CAAC,yBAAyB,YAAY;AACpC,YAAI,CAAC,KAAK,YAAY,sBAAuB,OAAM,sBAAsB,WAAW,QAAQ;AAAA,MAC9F,CAAC;AAAA,MACD,CAAC,4BAA4B,YAAY;AAAE,cAAM,KAAK,UAAU,UAAU;AAAA,MAAG,CAAC;AAAA,MAC9E,CAAC,kBAAkB,MAAM,QAAQ,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH;AACF;AAEA,eAAe,wBACb,QAKC;AACD,QAAM,OAAO,MAAM,oBAAoB,OAAO,QAAQ,OAAO,YAAY,OAAO,MAAM,QAAQ;AAC9F,QAAM,WAAW,OAAO,WAAW,CAAC,OAAO,MAAM,cAC7C,MAAM,wBAAwB,OAAO,QAAQ,OAAO,QAAQ,IAAI,GAAG,OAAO,MAAM,QAAQ,IACxF,oBAAI,IAA6B;AACrC,QAAM,UAAU,OAAO,WAAW,CAAC,OAAO,MAAM,cAC5C,MAAM,oBAAoB,OAAO,QAAQ,OAAO,QAAQ,IAAI,GAAG,OAAO,MAAM,QAAQ,IACpF,oBAAI,IAAwB;AAChC,SAAO,EAAE,MAAM,UAAU,QAAQ;AACnC;AAEA,SAAS,uBACP,QACA,iBACM;AACN,gDAAmB,OAAO,kBAAkB,MAAM;AAAA,IAChD,gBAAgB,OAAO,OAAO;AAAA,IAC9B,eAAe,OAAO,OAAO;AAAA,IAC7B,MAAM;AAAA,IACN,iBAAiB,CAAC,qBAAqB;AACrC,iBAAW,CAAC,UAAU,UAAU,KAAK,iBAAiB;AACpD,yBAAiB,yBAAyB,UAAU,MAAM,QAAQ,QAAQ,EAAE,SAAS,WAAW,CAAC,CAAC;AAAA,MACpG;AACA,uBAAiB,eAAe,OAAO,UAAU;AACjD,8BAAwB,QAAQ,gBAAgB;AAAA,IAClD;AAAA,EACF,CAAC;AACH;AASA,SAAS,wBAAwB,QAAwB,kBAAoC;AAC3F,QAAM,cAAc,OAAO,KAAK;AAChC,MAAI,CAAC,eAAe,CAAC,YAAY,WAAW,KAAM;AAClD,QAAM,iBAAiB,IAAI,8CAAmB,OAAO,OAAO,iBAAiB;AAC7E,aAAW,QAAQ,YAAY,YAAY;AACzC,qBAAiB,mBAAmB,MAAM,OAAO,YAAmC;AAClF,YAAM,WAAW,MAAM,eAAe,QAAQ,IAAI;AAClD,aAAO,YAAY,KAAK,QAAQ,cAAc,MAAM,QAA+B,EAAE,OAAO;AAAA,IAC9F,CAAC;AAAA,EACH;AACF;AAGA,SAAS,mBAAmB,YAAyD;AACnF,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,aAAa,WAAW,OAAO,EAAG,YAAW,QAAQ,UAAU,WAAY,YAAW,IAAI,IAAI;AACzG,SAAO,CAAC,GAAG,UAAU;AACvB;AASA,eAAe,oBACb,QACA,OACA,UACkC;AAClC,QAAM,UAAU,oBAAI,IAAwB;AAC5C,aAAW,QAAQ,OAAO;AACxB,eAAW,YAAY,CAAC,GAAG,KAAK,SAAS,KAAK,QAAQ,GAAG;AACvD,YAAM,YAAY,OAAO,wBAAwB,QAAQ;AACzD,UAAI,CAAC,aAAa,QAAQ,IAAI,UAAU,QAAQ,EAAG;AACnD,YAAM,MAAO,MAAM,SAAS,UAAU,IAAI;AAC1C,UAAI,IAAI,YAAY,QAAW;AAC7B,cAAM,IAAI,+BAAc,eAAe,UAAU,QAAQ,KAAK,UAAU,IAAI,yBAAyB;AAAA,MACvG;AACA,UAAI;AACF,gBAAQ,IAAI,UAAU,cAAU,8CAAiB,IAAI,SAAS,eAAe,UAAU,QAAQ,EAAE,CAAC;AAAA,MACpG,SAAS,OAAO;AACd,cAAM,IAAI,+BAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGA,SAAS,yBAAyB,SAA6D;AAC7F,SAAO,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE,IAAI,CAAC,aAAa,OAAO,OAAO,EAAE,MAAM,UAAU,QAAQ,UAAU,CAAC,CAAC;AACnG;AAEA,eAAe,+BAA+B,QAAuC;AACnF,QAAM,gBAAgB,OAAO;AAC7B,MAAI,CAAC,cAAe;AAGpB,gBAAc,KAAK,iBAAiB,4BAAe,MAAM,OAAO,SAAS,EAAC,OAAO,OAAO,QAAQ,kBAAO,UAAS,CAAC;AACjH,gBAAc,eAAe,8BAAY,WAAW;AACpD,gBAAc,eAAe,uCAAsB,WAAW;AAC9D,QAAM,cAAc,OAAO,OAAO,WAAW,OAAO,OAAO,OAAO,SAAS,OAAO;AAClF,MAAI,aAAa;AACf,UAAM,kBAAkB,UAAM;AAAA,MAC5B,OAAO,MAAM;AAAA,MACb;AAAA,MACA;AAAA,IACF;AACA,QAAI,OAAO,oBAAoB,YAAY;AACzC,YAAM,IAAI,+BAAc,uDAAuD,WAAW,GAAG;AAAA,IAC/F;AACA,UAAM,6BAAiB,IAAI,cAAc,MAAe,MAAM,gBAAgB,cAAc,IAAI,CAAC;AAAA,EACnG;AACA,QAAM,6BAAiB;AAAA,IACrB,cAAc;AAAA,IACd,MAAM,OAAO,KAAK,UAAU,wBAAwB,cAAc,IAAI;AAAA,EACxE;AACF;AAEA,SAAS,kCACP,QACA,iBACA,qBACM;AACN,aAAW,cAAc,gBAAgB,OAAO,GAAG;AACjD,WAAO,kBAAkB,QAAQ,UAAU;AAC3C,QAAI,CAAC,OAAO,kBAAkB,WAAW,aAAa,UAAU,GAAG;AACjE,aAAO,kBAAkB,KAAK,cAAc,UAAU;AAAA,IACxD;AAAA,EACF;AACA,aAAW,cAAc,oBAAoB,OAAO,GAAG;AACrD,WAAO,uBAAuB,QAAQ,UAAU;AAAA,EAClD;AACF;AAkBA,SAAS,iBACP,QACA,aAKa;AACb,QAAM,WAA+B,CAAC;AAAA,IACpC,OAAO;AAAA,IACP,cAAU,yCAAqB,wBAAQ,EAAE,IAAI,OAAO,kBAAkB,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,KAAK,OAAO,CAAC,CAAC;AAAA,IAC/G,YAAY,mBAAmB,OAAO,UAAU;AAAA,IAChD,SAAS,CAAC;AAAA,EACZ,CAAC;AACD,MAAI,OAAO,yBAAyB,OAAO,SAAS;AAClD,aAAS,KAAK;AAAA,MACZ,OAAO;AAAA,MACP,cAAU,yCAAqB,wBAAQ,EAAE,IAAI,OAAO,sBAAsB,OAAO,CAAC,GAAG,CAAC,GAAG,YAAY,SAAS,OAAO,CAAC,CAAC;AAAA,MACvH,YAAY,CAAC;AAAA,MACb,SAAS,yBAAyB,YAAY,OAAO;AAAA,IACvD,CAAC;AAAA,EACH;AACA,QAAM,eAAW,oCAAoB,OAAO,MAAM,UAAU,QAAQ;AACpE,QAAM,eAAW,sCAAkB,wBAAQ,GAAG,UAAU,MAAM;AAC9D,QAAM,mBAAe,sCAAkB,wBAAQ,GAAG,UAAU,UAAU;AACtE,SAAO;AAAA,IACL,MAAM,SAAS;AAAA,IACf,cAAc,SAAS,aAAa,kBAAkB,CAAC;AAAA,IACvD,UAAU,aAAa;AAAA,IACvB,kBAAkB,aAAa,aAAa,kBAAkB,CAAC;AAAA,IAC/D;AAAA,IACA,aAAa;AAAA,MACX,UAAM,sCAAsB,UAAU,MAAM;AAAA,MAC5C,cAAU,sCAAsB,UAAU,UAAU;AAAA,MACpD,YAAY,IAAI,IAAI,SACjB,OAAO,CAAC,YAAY,QAAQ,UAAU,UAAU,QAAQ,MAAM,SAAS,YAAY,EACnF,IAAI,CAAC,YAAY,QAAQ,MAAM,MAAgB,CAAC;AAAA,IACrD;AAAA,EACF;AACF;AAOA,SAAS,sBAAsB,QAA4D;AACzF,SAAO,CAAC,UAAU;AAGhB,QAAI,OAAO,KAAK,cAAc,OAAO,KAAK,SAAU,QAAO,QAAQ,OAAO,KAAK;AAC/E,WAAO,KAAK,aAAa;AACzB,WAAO,oBAAoB,OAAO,OAAO,SAAS,4DAA4D;AAAA,EAChH;AACF;AAEA,SAAS,sBAAsB,QAAwB,OAAqB;AAC1E,MAAI,OAAO,KAAK,UAAU;AACxB,UAAM,IAAI,+BAAc,qDAAqD,KAAK,mBAAmB;AAAA,EACvG;AACF;AAOA,SAAS,yBAAyB,QAAwB,OAAqB;AAC7E,MAAI,OAAO,KAAK,UAAU;AACxB,UAAM,IAAI,+BAAc,oDAAoD,KAAK,2BAA2B;AAAA,EAC9G;AACF;AAGA,eAAe,sBAAsB,QAA8C;AACjF,wBAAsB,QAAQ,MAAM;AACpC,QAAM,OAAO,OAAO,KAAK,IAAK;AAC9B,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,6BAAiB,IAAI,MAAe,MAAM,KAAK,IAAI,gCAAU,CAAC;AAAA,EAChE;AACA,SAAO,KAAK,SAAS;AACrB,2BAAyB,QAAQ,MAAM;AACvC,QAAM,6BAAiB,IAAI,MAAe,MAAM,OAAO,KAAK,UAAU,QAAQ,CAAC;AAC/E,2BAAyB,QAAQ,MAAM;AACvC,8BAA4B,OAAO,gBAAgB,OAAO,MAAM;AAChE,SAAO,EAAE,OAAO;AAClB;AAaA,eAAe,0BACb,QACA,MACA,UACuB;AACvB,QAAM,EAAE,OAAO,IAAI,MAAM,KAAK,QAAQ;AACtC,wBAAsB,QAAQ,UAAU;AACxC,QAAM,WAAW,OAAO,KAAK;AAC7B,QAAM,qBAAqB,UAAU,oBAAoB,KAAK,QAAQ;AACtE,QAAM,QAA2B;AAAA,IAC/B,QAAQ,SAAS;AAAA,IACjB,GAAI,qBAAqB,EAAE,mBAAmB,IAAI,CAAC;AAAA,EACrD;AACA,QAAM,OAAO,UAAM,4CAAkB;AAAA,IACnC,QAAQ,OAAO;AAAA,IACf;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,IACnB,UAAU,OAAO,MAAM;AAAA,IACvB;AAAA,IACA,KAAK;AAAA,IACL,KAAK,SAAS;AAAA,IACd,gBAAgB,SAAS;AAAA,IACzB,eAAe,OAAO;AAAA,IACtB,mBAAmB,SAAS;AAAA,IAC5B,SAAS,SAAS;AAAA,IAClB,eAAe,SAAS;AAAA,EAC1B,CAAC;AAGD,SAAO,KAAK,WAAW;AACvB,2BAAyB,QAAQ,UAAU;AAC3C,SAAO;AACT;AAEA,SAAS,sBACP,QACA,UACiB;AACjB,QAAM,WAAW,sBAAsB,MAAM;AAC7C,QAAM,OAAO,IAAI,0CAA8B,MAAM,sBAAsB,MAAM,GAAG,QAAQ;AAC5F,QAAM,gBAAgB,WAClB,IAAI,0CAA+B,MAAM,0BAA0B,QAAQ,MAAM,QAAQ,GAAG,QAAQ,IACpG;AACJ,SAAO,EAAE,MAAM,UAAU,cAAc;AACzC;AAQA,eAAe,uBAAuB,QAAmG;AACvI,SAAO,kBAAkB,KAAK,iBAAiB,4BAAe,MAAM,OAAO,SAAS,EAAC,OAAO,OAAO,QAAQ,kBAAO,UAAS,CAAC;AAC5H,QAAM,cAAc,MAAM,wBAAwB,MAAM;AACxD,SAAO,KAAK,WAAW,MAAM,WAAW,OAAO,kBAAkB,MAAM,OAAO,QAAQ,OAAO,OAAO;AACpG,8BAA4B,OAAO,gBAAgB,OAAO,MAAM;AAChE,yBAAuB,QAAQ,YAAY,IAAI;AAC/C,QAAM,+BAA+B,MAAM;AAC3C,oCAAkC,QAAQ,YAAY,MAAM,YAAY,QAAQ;AAChF,QAAM,MAAM,iBAAiB,QAAQ,WAAW;AAChD,SAAO,KAAK,cAAc,IAAI;AAE9B,SAAO,KAAK,MAAM,UAAM,wDAAyB,IAAI,MAAM,OAAO,mBAAmB,QAAQ,IAAI,YAAY;AAC7G,QAAM,OAAO,OAAO,KAAK,IAAI;AAE7B,QAAM,WAAyC,OAAO,WAAW,OAAO,wBACpE;AAAA,IACA,KAAK,IAAI;AAAA,IACT,gBAAgB,IAAI;AAAA,IACpB,mBAAmB,YAAY;AAAA,IAC/B,QAAQ,OAAO,KAAK,UAAU,UAAU,OAAO,QAAQ;AAAA,IACvD,SAAS,IAAI,YAAY;AAAA,IACzB,eAAe,YAAY;AAAA,EAC7B,IACE;AACJ,QAAM,UAAU,sBAAsB,QAAQ,QAAQ;AACtD,SAAO,KAAK,UAAU;AAEtB,QAAM,cAAc,mBAAmB;AAAA,IACrC;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,KAAK,OAAO,KAAK;AAAA,IACjB,YAAY,OAAO;AAAA,IACnB;AAAA,IACA,eAAe,OAAO,OAAO;AAAA,IAC7B,QAAQ,UAAU,UAAU,OAAO,KAAK,UAAU,UAAU,OAAO,QAAQ;AAAA,IAC3E,KAAK;AAAA,IACL,kBAAkB,OAAO;AAAA,IACzB,aAAa,IAAI;AAAA,IACjB,SAAS,OAAO;AAAA,EAClB,CAAC;AACD,MAAI,OAAO,MAAM,aAAa;AAC5B,UAAM,EAAE,+BAA+B,IAAI,MAAM,OAAO,gDAAgD;AACxG,WAAO,+BAA+B;AAAA,MACpC,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO,QAAS,IAAI;AAAA,MAC3B;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,QAAQ,MAAM,OAAO,KAAK,UAAU,UAAU,OAAO,QAAQ;AAAA,IAC/D,GAAG,OAAO,MAAM,WAAW;AAAA,EAC7B;AACA,SAAO;AACT;AAEA,eAAe,oBACb,OACA,SACA,UAAU,6CACM;AAChB,MAAI;AACF,UAAM,QAAQ;AAAA,EAChB,SAAS,cAAc;AACrB,UAAM,WAAW;AAAA,MACf,GAAI,iBAAiB,qDAAuB,MAAM,WAAW,CAAC;AAAA,MAC9D,GAAI,wBAAwB,qDACxB,aAAa,WACb,CAAC,EAAE,OAAO,kBAAkB,OAAO,aAAa,CAAC;AAAA,IACvD;AACA,UAAM,IAAI,mDAAqB,SAAS,UAAU,KAAK;AAAA,EACzD;AACA,QAAM;AACR;AAGA,eAAe,oBACb,OACA,UAC2E;AAC3E,QAAM,aAAS,gDAAqB,MAAM,UAAU;AAGpD,MAAI,YAAY,gBAAY;AAAA,IAC1B,OAAO,YAAY,2CAAgB,OAAO,CAAC,UAAU,OAAO,OAAO,KAAK,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAAA,EACxG;AAGA,QAAM,iBAAa,6CAAiB,OAAO,YAAY,UAAU,IAAI;AACrE,QAAM,cAAU,kDAAsB,OAAO,OAAO,SAAS;AAC7D,MAAI,MAAM,eAAe,CAAC,SAAS;AACjC,UAAM,IAAI,+BAAc,wEAAwE;AAAA,EAClG;AACA,MAAI,MAAM,mBAAe,4BAAY,MAAM,QAAQ,EAAE,KAAK,WAAS,MAAM,SAAS,UAAU,MAAM,UAAU,UAAU,GAAG;AACvH,UAAM,IAAI,+BAAc,wOAAwO;AAAA,EAClQ;AACA,QAAM,cAAU,+BAAgB,MAAM,oBAAoB,QAAQ,+BAAiB,CAAC;AACpF,QAAM,iBAAiB,mBAAmB;AAC1C,QAAM,oBAAoB,IAAI,mDAAqB,wBAAwB,OAAO,UAAU;AAC5F,QAAM,wBAAwB,WAAW,CAAC,MAAM,cAC5C,IAAI,mDAAqB,yBAAyB,OAAO,UAAU,IACnE;AACJ,QAAM,mBAAsC,CAAC;AAC7C,QAAM,OAA0B;AAAA,IAC9B,UAAU;AAAA,IACV,KAAK;AAAA,IACL,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,EACd;AACA,QAAM,UAAU,qBAAqB,QAAQ,OAAO,WAAW,YAAY,IAAI;AAC/E,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,SAAyB;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI;AACF,WAAO,MAAM,uBAAuB,MAAM;AAAA,EAC5C,SAAS,OAAO;AACd,WAAO,oBAAoB,OAAO,OAAO;AAAA,EAC3C;AACF;AAkBA,MAAM,aAAa;AAEnB,SAAS,mBAAmB,OAA0C;AACpE,QAAM,EAAE,MAAM,KAAK,YAAY,eAAe,SAAS,QAAQ,KAAK,aAAa,kBAAkB,aAAa,QAAQ,IAAI;AAC5H,QAAM,QAAQ,MAAsC,QAAQ,YAAY,YAAY,SAAS,UAAU;AACvG,QAAM,MAAM,YAAoC;AAC9C,QAAI,CAAC,YAAa,aAAY,OAAO,YAAY;AACjD,YAAQ,MAAM,MAAM,EAAE,QAAQ,GAAG;AAAA,EACnC;AAIA,QAAM,aAAS;AAAA,IACb;AAAA,IACA,QAAQ,KAAK,IAAI,CAAC,SAAS,KAAK,MAAM;AAAA,IACtC,CAAC,UAAU;AAAA,IACX;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,eACrB;AAAA,IACA;AAAA,IACA,QAAQ,SAAS,IAAI,CAAC,SAAS,KAAK,QAAQ;AAAA,IAC5C,CAAC,QAAQ,OAAO,UAAU,SAAS;AAAA,IACnC;AAAA,EACF,IACE;AACJ,QAAM,eAAe,QAAQ,eACzB;AAAA,IACA;AAAA,IACA,QAAQ,SAAS,IAAI,CAAC,SAAS,KAAK,YAAY;AAAA,IAChD,CAAC,OAAO,SAAS;AAAA,IACjB;AAAA,EACF,IACE;AAEJ,QAAM,oBAAoB,OAAO,WAAmB,UAAsC;AACxF,QAAI,CAAC,WAAW,IAAI,SAAS,GAAG;AAC9B,YAAM,IAAI;AAAA,QACR,cAAc,SAAS;AAAA,IAA+D,CAAC,GAAG,WAAW,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC;AAAA,MAC3H;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,QAAQ,KAAK,QAAQ;AACpD,WAAO,MAAM,6BAAiB,IAAI,MAAe,MAAM,KAAK,SAAS,WAAW,KAAK,CAAC;AAAA,EACxF;AACA,QAAM,UAAM,gDAAmB,cAAc,KAAK,GAAG,iBAAiB;AAEtE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY,OAAU,eAAwD;AAC5E,YAAM,QAAQ,KAAK,QAAQ;AAC3B,YAAM,QAAQ,KAAK,OAAO;AAC1B,YAAM,iBAAiB,CAAC,KAAK,aAAa,UAAU;AACpD,UAAI;AACJ,uBAAiB,KAAK,EAAE,SAAS,MAAM,6BAAiB,IAAI,OAAgB,UAAM,0CAAW,6BAA6B;AAAA,QACxH,CAAC,cAAc,YAAY;AAAE,cAAI,eAAgB,WAAM,wDAA2B,QAAQ;AAAA,QAAG,CAAC;AAAA,QAC9F,CAAC,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,MACjC,CAAC,CAAC,EAAmB,CAAC;AACtB,UAAI,CAAC,MAAM,aAAa,UAAU,EAAG,OAAM,cAAc,UAAU;AACnE,iBAAW,MAAM,6BAAiB,IAAI,OAAgB,MAAM,MAAM,IAAI,UAAU,CAAC;AACjF,oEAA6B,QAAQ;AACrC,aAAO;AAAA,IACT;AAAA,IACA,KAAK,OAAU,UAA+B;AAC5C,YAAM,QAAQ,KAAK,QAAQ;AAC3B,aAAQ,MAAM,6BAAiB,IAAI,MAAe,MAAM,KAAK,IAAI,KAAK,CAAC;AAAA,IACzE;AAAA,IAEA,IAAI,WAAW;AAAE,aAAO,YAAY,YAAY,SAAS,UAAU;AAAA,IAAG;AAAA,IACtE,IAAI,SAAS;AAAE,aAAO,UAAU,YAAY,SAAS,UAAU;AAAA,IAAG;AAAA,IAClE,IAAI,eAAe;AAAE,aAAO,gBAAgB,YAAY,SAAS,UAAU;AAAA,IAAG;AAAA,IAC9E,MAAM,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,EAAE,QAAQ,GAAG,KAAK,MAAM,QAAQ,KAAK;AAAA,IACvF,UAAU,OAAO,MAAM,QAAQ,WAAW,MAAM,MAAM,EAAE,QAAQ,GAAG,SAAS,MAAM,QAAQ,KAAK;AAAA,IAC/F,SAAS,MAAM,MAAM,EAAE,SAAS,QAAQ,KAAK;AAAA,IAC7C,QAAQ,YAAY;AAClB,YAAM,MAAM,QAAQ,OAAO;AAG3B,UAAI,QAAQ,KAAK,UAAW,QAAO,MAAM,QAAQ,KAAK,QAAQ,GAAG,OAAO,MAAM;AAC9E,UAAI,QAAQ,UAAU,UAAW,QAAO,MAAM,QAAQ,SAAS,QAAQ,GAAG,OAAO;AAAA,IACnF;AAAA,IAEA,IAAI,SAAS;AACX,UAAI,CAAC,YAAa,aAAY,OAAO,YAAY;AACjD,YAAM,OAAO,MAAM,EAAE;AACrB,UAAI,CAAC,KAAM,OAAM,IAAI,+BAAc,sCAAsC;AACzE,aAAO,KAAK,IAAK;AAAA,IACnB;AAAA,IACA,OAAO,OAAO,cAAc,MAAM,IAAI,GAAG,MAAM,QAAQ;AAAA,IACvD,MAAM,OAAO,UAAU,UAAU,MAAM,IAAI,GAAG,KAAK,UAAU,IAAI;AAAA,IACjE,QAAQ,OAAO,cAAc,MAAM,IAAI,GAAG,OAAO,QAAQ;AAAA,IACzD,MAAM,OAAO,cAAc,MAAM,IAAI,GAAG,KAAK,QAAQ;AAAA,IACrD,QAAQ,OAAO,cAAc,MAAM,IAAI,GAAG,OAAO,QAAQ;AAAA;AAAA;AAAA;AAAA,IAKzD,QAAQ,YAAY;AAClB,YAAM,WAAqD,CAAC;AAC5D,YAAM,IAAI,OAAO,EAAE,MAAM,CAAC,UAAU,SAAS,KAAK,EAAE,OAAO,QAAQ,MAAM,CAAC,CAAC;AAC3E,YAAM,YAAY,KAAK,OAAO,EAAE,MAAM,CAAC,UAAU,SAAS,KAAK,EAAE,OAAO,mBAAmB,MAAM,CAAC,CAAC;AACnG,YAAM,WAAW,QAAQ,UAAU,WAAW;AAC9C,UAAI,UAAU;AACZ,cAAM,SAAS,OAAO,EAAE,MAAM,CAAC,UAAU,SAAS,KAAK,EAAE,OAAO,YAAY,MAAM,CAAC,CAAC;AAAA,MACtF;AACA,YAAM,YAAY,SAAS,OAAO,EAAE,MAAM,CAAC,UAAU,SAAS,KAAK,EAAE,OAAO,oBAAoB,MAAM,CAAC,CAAC;AACxG,UAAI,SAAS,WAAW,EAAG,OAAM,SAAS,CAAC,EAAG;AAC9C,UAAI,SAAS,QAAQ;AACnB,cAAM,IAAI;AAAA,UACR,wDACA,SAAS,IAAI,CAAC,EAAE,OAAO,MAAM,MAC3B,GAAG,KAAK,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,EAAE,KAAK,MAAM;AAAA,QACtF;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA,CAAC,2BAAY,GAAI,IAA2C,2BAAY;AAAA,EAC1E;AACF;AAEO,MAAM,gBAAyD;AAAA,EAC5D,YAA6B,OAAqB;AAArB;AAAA,EAAsB;AAAA,EAE3D,OAAO,OAAO,YAAqC;AACjD,QAAI,OAAO,eAAe,YAAY,CAAC,WAAW,QAAQ;AACxD,YAAM,IAAI,+BAAc,6DAA6D;AAAA,IACvF;AACA,WAAO,IAAI,gBAAgB;AAAA,MACzB,YAAY,iBAAAC,QAAK,QAAQ,UAAU;AAAA,MACnC,WAAW;AAAA,MACX,aAAa;AAAA,MACb,qBAAqB,CAAC;AAAA,MACtB,UAAU;AAAA,MACV,cAAU,yBAAS;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,WAA+C;AACnD,QAAI,OAAO,cAAc,WAAY,OAAM,IAAI,UAAU,iDAAiD;AAC1G,WAAO,KAAK,OAAO,EAAC,qBAAqB,OAAO,OAAO,CAAC,GAAG,KAAK,MAAM,qBAAqB,SAAS,CAAC,EAAC,CAAC;AAAA,EACzG;AAAA,EAKA,QAAQ,OAAwB,cAAgD;AAC9E,QAAI,SAAS,gBAAgB,SAAS,YAAa,OAAM,IAAI,+BAAc,2CAA2C;AACtH,WAAO,IAAI,gBAAiC,EAAC,GAAG,KAAK,OAAO,aAAa,KAAI,CAAC;AAAA,EAChF;AAAA;AAAA,EAGA,OAAO,WAAkD;AACvD,UAAM,eAAW,6CAAiB,SAAS;AAC3C,WAAO,KAAK,OAAO,EAAE,WAAW,OAAO,OAAO;AAAA,MAC5C,GAAI,SAAS,SAAS,OAAO,EAAE,MAAM,SAAS,KAAK,IAAI,CAAC;AAAA,MACxD,GAAI,SAAS,aAAa,OAAO,EAAE,UAAU,SAAS,SAAS,IAAI,CAAC;AAAA,IACtE,CAAC,EAAE,CAAC;AAAA,EACN;AAAA;AAAA,EAGA,SAAS,UAA8C;AACrD,QAAI,OAAO,aAAa,WAAY,OAAM,IAAI,+BAAc,+BAA+B;AAC3F,WAAO,KAAK,OAAO,EAAE,SAAS,CAAC;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,QAAsC;AACxC,QAAI,EAAE,kBAAkB,yBAAW,OAAM,IAAI,+BAAc,2BAA2B;AACtF,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,IAAI,MAAM,EAAE,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA,EAKA,MAAS,QAAuB,aAAkC,SAAkD;AAClH,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,MAAM,QAAQ,aAAa,OAAO,EAAE,CAAC;AAAA,EAC1F;AAAA;AAAA,EAGA,SAAS,QAAiB,aAAkC,SAAkD;AAC5G,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,SAAS,QAAQ,aAAa,OAAO,EAAE,CAAC;AAAA,EAC7F;AAAA;AAAA,EAGA,OAAO,QAAiB,QAAgB,aAA+B,SAAkD;AACvH,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,OAAO,QAAQ,QAAQ,aAAa,OAAO,EAAE,CAAC;AAAA,EACnG;AAAA;AAAA,EAGA,MAAM,QAAiB,aAAsB,SAAkD;AAC7F,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,MAAM,QAAQ,aAAa,OAAO,EAAE,CAAC;AAAA,EAC1F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,UAAkB,aAAoC,SAAkD;AACjH,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,WAAW,UAAU,aAAa,OAAO,EAAE,CAAC;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,KAAK,UAA0B,aAAoC,SAAkD;AACnH,WAAO,KAAK,OAAO,EAAE,UAAU,KAAK,MAAM,SAAS,KAAK,UAAU,aAAa,OAAO,EAAE,CAAC;AAAA,EAC3F;AAAA;AAAA,EAGA,MAAM,QAA4C;AAChD,WAAO,MAAM,oBAAoB,KAAK,OAAO,KAAK,MAAM,gBAAY,6CAAiB,KAAK,MAAM,SAAS,IAAI,IAAI;AAAA,EACnH;AAAA,EAEQ,OAAO,OAAkD;AAC/D,WAAO,IAAI,gBAAgB,EAAE,GAAG,KAAK,OAAO,GAAG,MAAM,CAAC;AAAA,EACxD;AACF;AAEO,SAAS,SAAS,YAAqC;AAC5D,SAAO,gBAAgB,OAAO,UAAU;AAC1C;","names":["import_fetch","import_ioc","path"]}
1
+ {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["import { FETCH_RUNTIME } from '@noego/runtime/fetch';\nimport { createTestFetch, defineFetchRoutes, type ConfigureFetch, type TestFetchRuntime } from '@noego/testing/fetch';\nimport type { WoodBrowserHost, WoodTestEnvironmentFor, NativeWoodTestEnvironment } from './native_browser/native_browser.types.js';\nimport { disposeControllerLifecycle, guardControllerLifecycleOnce } from '../controller/controller_lifecycle.js';\nimport type { WoodTestApi, WoodTestRawApi } from '../testing-api.js';\n/**\n * `testWood(configPath)` — the canonical Wood testing entry point.\n *\n * One persistent immutable builder over the production `wood.config.yml`:\n *\n * const app = await testWood('wood.config.yml')\n * .select({ main: { module: 'backend_service' }, renderer: { path: ['settings.backend-service'] } })\n * .class(BackendServiceLifecycleCoordinator, LifecycleStub)\n * .method(BackendServiceLifecycleCoordinator, 'getStatus', test.once(test.returns(status)))\n * .middleware('auth.session:require_session', test.original())\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * canonical singular replacements (`use(testStub())`, `class/function/method/\n * value` on exact declared tokens, `middleware` on source identities, `load`\n * on page/layout companion data-loader sources) decide what is replaced — resolved\n * by the shared `@noego/testing` overlay engine against each owner's declared\n * binding catalog. Without `select()`, every declared owner is selected whole.\n * The legacy plural vocabulary (`classes/functions/values/methods`,\n * `.main()/.renderer()`, `main: true`, `{ operation }`, `pages`) is retained\n * here for existing suites. Controllers, middleware and views are auto-bound\n * from the config; the app composes its backend through the optional\n * `main.testing.compose` hook and mounts pages through `renderer.testing.host`.\n *\n * Every build creates fresh `@noego/ioc` roots (main, and renderer when\n * pages are selected) — nothing global is touched, so environments coexist\n * in one process.\n */\nimport path from 'node:path';\nimport { LoadAs, type IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport {\n applyTestBindings,\n createTestExecutables,\n describeTestBindings,\n ENV_REGISTRY,\n resolveTestOverlays,\n testIoc,\n TestIocBuilder,\n TestStub,\n testStub,\n stubEntries,\n} from '@noego/testing';\nimport type {\n ExecutableReplacement,\n LoaderIdentity,\n LoaderReference,\n MethodDescriptor,\n ClassToken, ReplacementClass,\n ReplacementFunction,\n ReplacementOptions,\n ResolvedTestOverlay,\n TestEnvironment,\n TestExecutableOverlays,\n TestOwnerCatalog,\n} from '@noego/testing';\nimport { installWoodRuntime, WoodRouter } from '../runtime/runtime_module.js';\nimport { assertDataLoader, type DataLoader } from '../loader/loader_orchestration.js';\nimport { MiddlewareResolver } from '../middleware/middleware_resolver.js';\nimport type { WoodMiddlewareContext } from '../types/index.cjs';\nimport type { OperationDef, ViewDef } from '../types/index.cjs';\nimport type { WoodConfig } from '../types/config.cjs';\nimport type { FrontendApplicationSurface } from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { Navigation } from '../navigation/index.js';\nimport { createOperationApi, RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.cjs';\nimport {\n defaultImporter,\n importDefaultExport,\n loadWoodConfigSource,\n type ModuleImporter,\n type WoodConfigSource,\n} from './wood_config_source.cjs';\nimport {\n DECLARED_OWNERS,\n resolveSelection,\n selectOperations,\n selectRendererCatalog,\n type ResolvedWoodSelection,\n type WoodTestSelection,\n} from './wood_test_selection.cjs';\nimport {\n buildRendererTier,\n type RendererTier,\n type WoodDomDriver,\n type WoodRendererHooks,\n} from './wood_env_renderer.cjs';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.cjs';\nimport { LazyRuntimeStage, lazyMember } from './wood_lazy_runtime.cjs';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\n /** Standard per-environment backend push source; hosts subscribe automatically. */\n readonly events: RouterBridgeEvents;\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n readonly selection: ResolvedWoodSelection;\n /** The selected operations (the router will serve exactly these). */\n readonly operations: ReadonlyMap<string, OperationDef>;\n readonly importer: ModuleImporter;\n /**\n * Dispatch through the environment's router. Available from `start()`\n * onward (the router is built after compose returns); calling it during\n * compose itself fails.\n */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n}\n\n/** What the compose hook may hand back; every member is optional. */\nexport interface WoodComposeResult extends WoodRendererHooks {\n /**\n * Runs AFTER the test's substitutions are installed on the root (compose\n * itself runs before them, so production registrations never shadow a\n * seam). Seed world state and warm caches here — through the seamed graph,\n * exactly as the test will observe it.\n */\n start?(): void | Promise<void>;\n /**\n * Quiesce BEFORE the main root disposes its instances: silence push\n * delivery, drain detached work — everything that still needs the live\n * graph (database, tracer) the services' own dispose hooks run against.\n */\n drain?(): void | Promise<void>;\n /** Release resources AFTER the roots are disposed (close files, remove dirs). */\n dispose?(): void | Promise<void>;\n}\n\n/**\n * The app's backend composition on the root the builder hands it. Register\n * lazily: nothing expensive should run until a test resolves it.\n */\nexport type WoodComposeMain = (\n root: IContainer,\n context: WoodComposeContext,\n) => WoodComposeResult | void | Promise<WoodComposeResult | void>;\n\ntype Params = Record<string, string>;\n\nexport interface TestWoodEnvironment {\n /** The main-process IoC root of this environment. */\n readonly root: IContainer;\n readonly router: WoodRouter;\n /** The selected operations. */\n readonly operations: ReadonlyMap<string, OperationDef>;\n /** Typed nested operation API backed by the real selected WoodRouter. */\n readonly api: WoodTestApi;\n /** Object-shaped untyped payload surface for deliberate runtime-validation probes. */\n readonly rawApi: WoodTestRawApi;\n /** Execute a selected operation through the real pipeline. */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n /** Resolve a controller class through a fresh main scope (root seams apply). */\n controller<T>(controller: new (...args: never[]) => T): Promise<T>;\n /** Resolve any dependency from the main root; class tokens retain their instance type. */\n get<T>(token: { readonly prototype: T }): Promise<T>;\n get<T = unknown>(token: unknown): Promise<T>;\n\n /** Renderer surface — present only when `pages` was selected. */\n readonly frontend: FrontendApplicationSurface;\n /** Backend push emitter feeding renderer subscriptions (pages only). */\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n navigate(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n\n /** DOM driver — present only when `dom` was selected. */\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n\n /** Finalize `test.*` expectations; fails on undrained renderer work. */\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: unknown;\n}\n\ntype ControllerClass = new (...args: never[]) => unknown;\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly browserHost: WoodBrowserHost | null;\n readonly fetchConfigurations: readonly ConfigureFetch[];\n readonly importer: ModuleImporter;\n readonly overlays: TestStub;\n}\n\nconst CONTAINER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.container.registry');\n\nfunction globalRegistrySize(): number {\n const registry = (globalThis as Record<symbol, unknown>)[CONTAINER_REGISTRY_SYMBOL];\n return registry instanceof Map ? registry.size : 0;\n}\n\nfunction tierMissing(tier: 'renderer' | 'pages' | 'dom', member: string): never {\n throw new TestWoodError(\n `the ${member} is not available: this environment was built without { ${tier} } — add it to select()`,\n );\n}\n\nasync function loadController(\n source: WoodConfigSource,\n importer: ModuleImporter,\n reference: string,\n owner: 'main' | 'renderer',\n): Promise<ControllerClass> {\n const file = owner === 'main'\n ? source.mainControllerFile(reference)\n : source.rendererControllerFile(reference);\n const controller = await importDefaultExport<ControllerClass>(\n importer,\n file,\n `${owner} controller \"${reference}\"`,\n );\n if (typeof controller !== 'function') {\n throw new TestWoodError(`${owner} controller \"${reference}\" must default-export a class (${file})`);\n }\n return controller;\n}\n\nasync function loadMainControllers(\n source: WoodConfigSource,\n operations: ReadonlyMap<string, OperationDef>,\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n for (const operation of operations.values()) {\n if (!controllers.has(operation.controller)) {\n controllers.set(operation.controller, await loadController(source, importer, operation.controller, 'main'));\n }\n }\n return controllers;\n}\n\nasync function loadRendererControllers(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n const references = new Set<string>();\n for (const view of views) {\n if (view.controller) references.add(view.controller);\n for (const controller of view.layoutControllers ?? []) if (controller) references.add(controller);\n }\n for (const reference of references) {\n controllers.set(reference, await loadController(source, importer, reference, 'renderer'));\n }\n return controllers;\n}\n\nasync function runCompose(\n root: IContainer,\n source: WoodConfigSource,\n context: WoodComposeContext,\n): Promise<WoodComposeResult | null> {\n const composePath = source.hookModule(source.config.main.testing?.compose);\n if (!composePath) return null;\n const compose = await importDefaultExport<WoodComposeMain>(context.importer, composePath, 'main.testing.compose');\n if (typeof compose !== 'function') {\n throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);\n }\n return (await ExecutionContext.run(root as never, () => compose(root, context))) ?? null;\n}\n\n/**\n * Dispose the main root under its own execution context (service teardown\n * hooks resolve collaborators such as tracers exactly like any other call),\n * naming every instance whose dispose hook failed.\n */\nasync function disposeRoot(env: TestEnvironment): Promise<void> {\n try {\n await ExecutionContext.run(env.root as never, () => env.dispose());\n } catch (error) {\n const failures = (error as { failures?: Array<{ token: string; error: unknown }> }).failures;\n if (!Array.isArray(failures)) throw error;\n const detail = failures\n .map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`)\n .join('\\n ');\n throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):\\n ${detail}`);\n }\n}\n\nfunction assertNoGlobalContainerLeak(before: number, source: WoodConfigSource): void {\n if (!process.env.VITEST) return;\n const after = globalRegistrySize();\n if (after > before) {\n throw new TestWoodError(\n `${source.config.main.testing?.compose ?? 'the build'} registered ${after - before} process-global ` +\n 'Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.',\n );\n }\n}\n\ntype SelectedPageCatalog = NonNullable<ReturnType<typeof selectRendererCatalog>>;\ntype ControllerScope = { dispose(): Promise<void> };\n\n/** Per-build executable seams (`.middleware` on main, `.load` on renderer). */\ninterface AssemblyExecutables {\n readonly main: TestExecutableOverlays;\n readonly renderer: TestExecutableOverlays;\n /** Middleware identities the test replaced (only these get a registry executable). */\n readonly middleware: ReadonlySet<string>;\n}\n\ninterface AssemblyLiveState {\n composed: WoodComposeResult | null;\n env: TestEnvironment | null;\n renderer: RendererTier | null;\n router: WoodRouter | null;\n /** Set once overlays are resolved (before the router is constructed on first use). */\n executables: AssemblyExecutables | null;\n /** Set once the environment's runtime stages exist (after build). */\n runtime: AssemblyRuntime | null;\n /** dispose() was requested; a runtime that has not started must not start. */\n disposed: boolean;\n /** A runtime stage already rolled the environment back; never roll back twice. */\n rolledBack: boolean;\n}\n\n/** The managed main runtime: constructed router + `compose.start()` done. */\ninterface MainRuntime {\n readonly router: WoodRouter;\n}\n\n/**\n * Lazily initialized runtime stages. `main` constructs the managed router\n * and runs `compose.start()`; `renderer` (pages only) builds the renderer\n * tier over the main router. Each initializes exactly once on first use.\n */\ninterface AssemblyRuntime {\n readonly main: LazyRuntimeStage<MainRuntime>;\n readonly renderer: LazyRuntimeStage<RendererTier> | null;\n}\n\ninterface AssemblyInputs {\n readonly network: TestFetchRuntime;\n readonly state: BuilderState;\n readonly selection: ResolvedWoodSelection;\n readonly source: WoodConfigSource;\n readonly operations: Map<string, OperationDef>;\n readonly catalog: SelectedPageCatalog | null;\n readonly registryBefore: number;\n readonly mainRegistrations: RegistrationOnlyRoot;\n readonly rendererRegistrations: RegistrationOnlyRoot | null;\n readonly controllerScopes: ControllerScope[];\n readonly live: AssemblyLiveState;\n readonly context: WoodComposeContext;\n readonly dispose: () => Promise<void>;\n}\n\nfunction createComposeContext(\n source: WoodConfigSource,\n state: BuilderState,\n selection: ResolvedWoodSelection,\n operations: Map<string, OperationDef>,\n live: AssemblyLiveState,\n): WoodComposeContext {\n return {\n events: new RouterBridgeEvents(),\n configPath: source.configPath,\n configDir: source.configDir,\n config: source.config,\n selection,\n operations,\n importer: state.importer,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!live.env || (!live.router && !live.runtime)) {\n throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n }\n // Inside start() the router already exists (it is constructed before\n // start runs); after build, the first call initializes the runtime.\n const router = live.router ?? (await live.runtime!.main.acquire()).router;\n return (await ExecutionContext.run(\n live.env.root as never,\n () => router.dispatch(operation, input),\n )) as T;\n },\n };\n}\n\nfunction createAssemblyDisposer(\n live: AssemblyLiveState,\n mainRegistrations: RegistrationOnlyRoot,\n rendererRegistrations: RegistrationOnlyRoot | null,\n controllerScopes: ControllerScope[],\n network: TestFetchRuntime,\n): () => Promise<void> {\n let closing: Promise<void> | null = null;\n return (): Promise<void> => {\n live.disposed = true;\n return closing ??= cleanupAll('testWood cleanup failed', [\n // An initialization racing this dispose finishes first (it refuses to\n // complete once `disposed` is set) so every resource it created late is\n // released below. A rollback-triggered dispose skips this: its own\n // stage has already finished and waiting would be a self-await.\n ['runtime settle', async () => {\n if (live.rolledBack || !live.runtime) return;\n await live.runtime.main.settled();\n await live.runtime.renderer?.settled();\n }],\n ['renderer', async () => { if (live.renderer) await live.renderer.dispose(); }],\n ...[...controllerScopes].reverse().map(\n (scope, index) => [`controller scope ${controllerScopes.length - index}`, () => scope.dispose()] as const,\n ),\n ['compose drain', async () => {\n const root = live.env?.root ?? mainRegistrations.actualRoot;\n if (!root.isDisposed()) await ExecutionContext.run(root as never, () => live.composed?.drain?.());\n }],\n ['main root', async () => {\n if (live.env) await disposeRoot(live.env);\n else await mainRegistrations.actualRoot.dispose();\n }],\n ['unbuilt renderer root', async () => {\n if (!live.renderer && rendererRegistrations) await rendererRegistrations.actualRoot.dispose();\n }],\n ['compose external dispose', async () => { await live.composed?.dispose?.(); }],\n ['external fetch', () => network.dispose()],\n ]);\n };\n}\n\nasync function loadAssemblyControllers(\n inputs: AssemblyInputs,\n): Promise<{\n main: Map<string, ControllerClass>;\n renderer: Map<string, ControllerClass>;\n loaders: Map<string, DataLoader>;\n}> {\n const main = await loadMainControllers(inputs.source, inputs.operations, inputs.state.importer);\n const renderer = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererControllers(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, ControllerClass>();\n const loaders = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererLoaders(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, DataLoader>();\n return { main, renderer, loaders };\n}\n\nfunction installAssemblyRuntime(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n installWoodRuntime(inputs.mainRegistrations.root, {\n controllersDir: inputs.source.mainControllersDir,\n middlewareDir: inputs.source.mainMiddlewareDir,\n mode: 'dev',\n registerRuntime: (configuredRouter) => {\n for (const [identity, controller] of mainControllers) {\n configuredRouter.registerControllerLoader(identity, () => Promise.resolve({ default: controller }));\n }\n configuredRouter.loadOperations(inputs.operations);\n registerMiddlewareSeams(inputs, configuredRouter);\n },\n });\n}\n\n/**\n * `.middleware(identity, …)`: the production identity keeps its membership,\n * order and inheritance (they come from the operations source); only the\n * executable satisfying it is replaced, through the router's registry seam.\n * The source executable is resolved lazily from `main.middleware`, exactly\n * as production would, and handed to the shared test.* runtime once.\n */\nfunction registerMiddlewareSeams(inputs: AssemblyInputs, configuredRouter: WoodRouter): void {\n const executables = inputs.live.executables;\n if (!executables || !executables.middleware.size) return;\n const sourceResolver = new MiddlewareResolver(inputs.source.mainMiddlewareDir);\n for (const spec of executables.middleware) {\n configuredRouter.registerMiddleware(spec, async (context: WoodMiddlewareContext) => {\n const original = await sourceResolver.resolve(spec);\n return executables.main.replace('middleware', spec, original as ReplacementFunction)(context);\n });\n }\n}\n\n/** Production middleware identities the selected operations run, in source order. */\nfunction selectedMiddleware(operations: ReadonlyMap<string, OperationDef>): string[] {\n const identities = new Set<string>();\n for (const operation of operations.values()) for (const spec of operation.middleware) identities.add(spec);\n return [...identities];\n}\n\n/**\n * Discover and import every companion data loader (`pages/chat.svelte` →\n * `pages/chat.load.ts`) of the selected pages/layouts. Discovery and import\n * happen at build (a missing default loader function fails the build); no\n * loader RUNS until a page opens. The identity is the config-relative\n * source path with export `default` — the `.load` seam identity.\n */\nasync function loadRendererLoaders(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, DataLoader>> {\n const loaders = new Map<string, DataLoader>();\n for (const view of views) {\n for (const viewPath of [...view.layouts, view.viewPath]) {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference || loaders.has(reference.identity)) continue;\n const mod = (await importer(reference.file)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new TestWoodError(`data loader ${reference.identity} (${reference.file}) has no default export`);\n }\n try {\n loaders.set(reference.identity, assertDataLoader(mod.default, `data loader ${reference.identity}`));\n } catch (error) {\n throw new TestWoodError(error instanceof Error ? error.message : String(error));\n }\n }\n }\n return loaders;\n}\n\n/** The native renderer loader identities: every discovered companion loader source. */\nfunction rendererLoaderReferences(loaders: ReadonlyMap<string, DataLoader>): LoaderReference[] {\n return [...loaders.keys()].map((identity) => Object.freeze({ path: identity, export: 'default' }));\n}\n\nasync function configureRendererRegistrations(inputs: AssemblyInputs): Promise<void> {\n const registrations = inputs.rendererRegistrations;\n if (!registrations) return;\n // Bound by the renderer tier on first use (see buildRendererTier); declared\n // here so the shared overlay catalog can resolve replacements for them.\n registrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n registrations.declareBinding(Navigation, 'singleton');\n registrations.declareBinding(WOOD_FRONTEND_BRIDGE, 'singleton');\n const composePath = inputs.source.hookModule(inputs.source.config.renderer.compose);\n if (composePath) {\n const composeRenderer = await importDefaultExport<(root: IContainer) => void | Promise<void>>(\n inputs.state.importer,\n composePath,\n 'renderer.compose',\n );\n if (typeof composeRenderer !== 'function') {\n throw new TestWoodError(`renderer.compose default export must be a function (${composePath})`);\n }\n await ExecutionContext.run(registrations.root as never, () => composeRenderer(registrations.root));\n }\n await ExecutionContext.run(\n registrations.root as never,\n () => inputs.live.composed?.configureRendererRoot?.(registrations.root),\n );\n}\n\nfunction registerControllersBeforeOverlays(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n rendererControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n for (const controller of mainControllers.values()) {\n inputs.mainRegistrations.declare(controller);\n if (!inputs.mainRegistrations.actualRoot.isRegistered(controller)) {\n inputs.mainRegistrations.root.registerClass(controller);\n }\n }\n for (const controller of rendererControllers.values()) {\n inputs.rendererRegistrations?.declare(controller);\n }\n}\n\ninterface AssemblyIoc {\n readonly main: TestIocBuilder;\n readonly mainBorrowed: readonly unknown[];\n readonly renderer: TestIocBuilder;\n readonly rendererBorrowed: readonly unknown[];\n readonly overlays: readonly ResolvedTestOverlay[];\n readonly executables: AssemblyExecutables;\n}\n\n/**\n * Legacy plural calls are routed by owner token sets (unchanged); the\n * canonical singular replacements are resolved by the shared overlay engine\n * against each owner's DECLARED binding catalog — production registrations\n * recorded on the root, legacy explicit composition, auto-bound controllers,\n * the selected middleware identities and the page/layout loader identities.\n */\nfunction buildAssemblyIoc(\n inputs: AssemblyInputs,\n controllers: {\n main: ReadonlyMap<string, ControllerClass>;\n renderer: ReadonlyMap<string, ControllerClass>;\n loaders: ReadonlyMap<string, DataLoader>;\n },\n): AssemblyIoc {\n const catalogs: TestOwnerCatalog[] = [{\n owner: 'main',\n bindings: describeTestBindings(testIoc().use(inputs.mainRegistrations.module()), [...controllers.main.values()]),\n middleware: selectedMiddleware(inputs.operations),\n loaders: [],\n }];\n if (inputs.rendererRegistrations && inputs.catalog) {\n catalogs.push({\n owner: 'renderer',\n bindings: describeTestBindings(testIoc().use(inputs.rendererRegistrations.module()), [...controllers.renderer.values()]),\n middleware: [],\n loaders: rendererLoaderReferences(controllers.loaders),\n });\n }\n const overlays = resolveTestOverlays(inputs.state.overlays, catalogs);\n const mainPlan = applyTestBindings(testIoc(), overlays, 'main');\n const rendererPlan = applyTestBindings(testIoc(), overlays, 'renderer');\n return {\n main: mainPlan.builder,\n mainBorrowed: mainPlan.buildOptions.borrowedTokens ?? [],\n renderer: rendererPlan.builder,\n rendererBorrowed: rendererPlan.buildOptions.borrowedTokens ?? [],\n overlays,\n executables: {\n main: createTestExecutables(overlays, 'main'),\n renderer: createTestExecutables(overlays, 'renderer'),\n middleware: new Set(overlays\n .filter((overlay) => overlay.owner === 'main' && overlay.entry.kind === 'middleware')\n .map((overlay) => overlay.entry.target as string)),\n },\n };\n}\n\n/**\n * Roll the whole environment back after a failed first use: every resource a\n * partially initialized runtime created is released, and both the primary\n * error and any cleanup failures are preserved. Runs at most once.\n */\nfunction createRuntimeRollback(inputs: AssemblyInputs): (error: unknown) => Promise<never> {\n return (error) => {\n // Already rolled back, or dispose() is in flight / done: that disposer\n // owns the cleanup (it waits for this attempt), so never re-enter it here.\n if (inputs.live.rolledBack || inputs.live.disposed) return Promise.reject(error);\n inputs.live.rolledBack = true;\n return rollbackEnvironment(error, inputs.dispose, 'testWood runtime initialization failed and rollback failed');\n };\n}\n\nfunction assertRuntimeMayStart(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed before its ${stage} runtime was used`);\n }\n}\n\n/**\n * Re-checked after every await of a first initialization: dispose() may have\n * been requested meanwhile. The disposer waits for this attempt, so failing\n * here hands every late resource to it instead of exposing a disposed root.\n */\nfunction assertRuntimeMayContinue(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed while its ${stage} runtime was initializing`);\n }\n}\n\n/** First use: construct the managed router, then run `compose.start()` on the seamed graph. */\nasync function initializeMainRuntime(inputs: AssemblyInputs): Promise<MainRuntime> {\n assertRuntimeMayStart(inputs, 'main');\n const root = inputs.live.env!.root;\n const router = await Promise.resolve(\n ExecutionContext.run(root as never, () => root.get(WoodRouter)),\n ) as WoodRouter;\n inputs.live.router = router;\n assertRuntimeMayContinue(inputs, 'main');\n await ExecutionContext.run(root as never, () => inputs.live.composed?.start?.());\n assertRuntimeMayContinue(inputs, 'main');\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n return { router };\n}\n\ninterface RendererRuntimeInputs {\n readonly ioc: TestIocBuilder;\n readonly borrowedTokens: readonly unknown[];\n readonly controllerClasses: ReadonlyMap<string, ControllerClass>;\n readonly events: RouterBridgeEvents;\n readonly loaders: TestExecutableOverlays;\n /** Imported companion data loaders by `.load` identity (none has run). */\n readonly loaderModules: ReadonlyMap<string, DataLoader>;\n}\n\n/** First renderer use: the main runtime, then the renderer tier over its router. */\nasync function initializeRendererRuntime(\n inputs: AssemblyInputs,\n main: LazyRuntimeStage<MainRuntime>,\n renderer: RendererRuntimeInputs,\n): Promise<RendererTier> {\n const { router } = await main.acquire();\n assertRuntimeMayStart(inputs, 'renderer');\n const composed = inputs.live.composed;\n const configurePageScope = composed?.configurePageScope?.bind(composed);\n const hooks: WoodRendererHooks = {\n events: renderer.events,\n ...(configurePageScope ? { configurePageScope } : {}),\n };\n const tier = await buildRendererTier({\n source: inputs.source,\n router,\n catalog: inputs.catalog!,\n operations: inputs.operations,\n importer: inputs.state.importer,\n hooks,\n dom: null,\n ioc: renderer.ioc,\n borrowedTokens: renderer.borrowedTokens,\n registrations: inputs.rendererRegistrations!,\n controllerClasses: renderer.controllerClasses,\n loaders: renderer.loaders,\n loaderModules: renderer.loaderModules,\n });\n // Hand the tier to the disposer before refusing: a dispose() that raced\n // this build releases it through the regular 'renderer' cleanup step.\n inputs.live.renderer = tier;\n assertRuntimeMayContinue(inputs, 'renderer');\n return tier;\n}\n\nfunction createAssemblyRuntime(\n inputs: AssemblyInputs,\n renderer: RendererRuntimeInputs | null,\n): AssemblyRuntime {\n const rollback = createRuntimeRollback(inputs);\n const main = new LazyRuntimeStage<MainRuntime>(() => initializeMainRuntime(inputs), rollback);\n const rendererStage = renderer\n ? new LazyRuntimeStage<RendererTier>(() => initializeRendererRuntime(inputs, main, renderer), rollback)\n : null;\n return { main, renderer: rendererStage };\n}\n\n/**\n * Build: read the source, record registrations (compose, runtime module,\n * renderer hooks, controllers) and install the test overlays on the main root.\n * Nothing is resolved or started here — the managed router, `compose.start()`\n * and the renderer tier initialize on first explicit use.\n */\nasync function materializeEnvironment(inputs: AssemblyInputs): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n inputs.mainRegistrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n const controllers = await loadAssemblyControllers(inputs);\n inputs.live.composed = await runCompose(inputs.mainRegistrations.root, inputs.source, inputs.context);\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n installAssemblyRuntime(inputs, controllers.main);\n await configureRendererRegistrations(inputs);\n registerControllersBeforeOverlays(inputs, controllers.main, controllers.renderer);\n const ioc = buildAssemblyIoc(inputs, controllers);\n inputs.live.executables = ioc.executables;\n\n inputs.live.env = await buildRecordedEnvironment(ioc.main, inputs.mainRegistrations, 'main', ioc.mainBorrowed);\n const root = inputs.live.env.root;\n\n const renderer: RendererRuntimeInputs | null = inputs.catalog && inputs.rendererRegistrations\n ? {\n ioc: ioc.renderer,\n borrowedTokens: ioc.rendererBorrowed,\n controllerClasses: controllers.renderer,\n events: inputs.live.composed?.events ?? inputs.context.events,\n loaders: ioc.executables.renderer,\n loaderModules: controllers.loaders,\n }\n : null;\n const runtime = createAssemblyRuntime(inputs, renderer);\n inputs.live.runtime = runtime;\n\n const environment = environmentSurface({\n root,\n network: inputs.network,\n env: inputs.live.env,\n operations: inputs.operations,\n runtime,\n allOperations: inputs.source.operations,\n events: renderer?.events ?? inputs.live.composed?.events ?? inputs.context.events,\n dom: false,\n controllerScopes: inputs.controllerScopes,\n executables: ioc.executables,\n dispose: inputs.dispose,\n });\n if (inputs.state.browserHost) {\n const { createNativeBrowserEnvironment } = await import('./native_browser/native_browser_environment.js');\n return createNativeBrowserEnvironment({\n source: inputs.source,\n views: inputs.catalog!.all(),\n environment,\n network: inputs.network,\n events: () => inputs.live.composed?.events ?? inputs.context.events,\n }, inputs.state.browserHost);\n }\n return environment;\n}\n\nasync function rollbackEnvironment(\n error: unknown,\n dispose: () => Promise<void>,\n message = 'testWood build failed and rollback failed',\n): Promise<never> {\n try {\n await dispose();\n } catch (cleanupError) {\n const failures = [\n ...(error instanceof TestWoodCleanupError ? error.failures : []),\n ...(cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'build rollback', error: cleanupError }]),\n ];\n throw new TestWoodCleanupError(message, failures, error);\n }\n throw error;\n}\n\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selected: ResolvedWoodSelection | null,\n): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n const source = loadWoodConfigSource(state.configPath);\n // No selection: every owner the config declares, whole ({}). A Wood\n // config declares both `main` and `renderer` sections.\n let selection = selected ?? resolveSelection(\n Object.fromEntries(DECLARED_OWNERS.filter((owner) => source.config[owner]).map((owner) => [owner, {}])),\n );\n // An omitted main owner exposes no operations; a selected renderer still gets\n // a main root for dependencies but does not widen the main operation aperture.\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selectRendererCatalog(source.views, selection);\n if (state.browserHost && !catalog) {\n throw new TestWoodError('browser() requires a selected renderer owner; add renderer to select()');\n }\n if (state.browserHost && stubEntries(state.overlays).some(entry => entry.kind === 'load' || entry.owner === 'renderer')) {\n throw new TestWoodError('Native browser renderer replacements are not supported yet: renderer code executes in its own realm. Keep main-process seams, or use the existing in-process renderer test environment for renderer .class/.method/.value/.load seams.');\n }\n const network = createTestFetch(state.fetchConfigurations.flatMap(defineFetchRoutes));\n const registryBefore = globalRegistrySize();\n const mainRegistrations = new RegistrationOnlyRoot('main.testing.compose', source.configPath);\n const rendererRegistrations = catalog && !state.browserHost\n ? new RegistrationOnlyRoot('configureRendererRoot', source.configPath)\n : null;\n const controllerScopes: ControllerScope[] = [];\n const live: AssemblyLiveState = {\n composed: null,\n env: null,\n renderer: null,\n router: null,\n executables: null,\n runtime: null,\n disposed: false,\n rolledBack: false,\n };\n const context = createComposeContext(source, state, selection, operations, live);\n const dispose = createAssemblyDisposer(\n live,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n network,\n );\n const inputs: AssemblyInputs = {\n network,\n state,\n selection,\n source,\n operations,\n catalog,\n registryBefore,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n live,\n context,\n dispose,\n };\n\n try {\n return await materializeEnvironment(inputs);\n } catch (error) {\n return rollbackEnvironment(error, dispose);\n }\n}\n\ninterface SurfaceParts {\n network: TestFetchRuntime;\n root: IContainer;\n env: TestEnvironment;\n operations: Map<string, OperationDef>;\n runtime: AssemblyRuntime;\n /** Every production operation, used to keep `api` object shape stable across selections. */\n allOperations: ReadonlyMap<string, OperationDef>;\n /** Backend push emitter (pages only); available from build, before any renderer use. */\n events: RouterBridgeEvents | null;\n dom: boolean;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n executables: AssemblyExecutables;\n dispose(): Promise<void>;\n}\n\nconst OPEN_FIRST = 'await open(page) (or another renderer call) first';\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, operations, allOperations, runtime, events, dom: domSelected, controllerScopes, executables, dispose } = parts;\n const pages = (): LazyRuntimeStage<RendererTier> => runtime.renderer ?? tierMissing('pages', 'renderer');\n const dom = async (): Promise<WoodDomDriver> => {\n if (!domSelected) tierMissing('dom', 'dom driver');\n return (await pages().acquire()).dom!;\n };\n\n // Stable lazy members: async use initializes the runtime; synchronous\n // members are served once it is live and fail with guidance before that.\n const router = lazyMember<WoodRouter>(\n 'router',\n runtime.main.map((main) => main.router),\n ['dispatch'],\n 'await call()/get()/controller() first',\n );\n const frontend = runtime.renderer\n ? lazyMember<FrontendApplicationSurface>(\n 'frontend',\n runtime.renderer.map((tier) => tier.frontend),\n ['open', 'act', 'settle', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n const rendererRoot = runtime.renderer\n ? lazyMember<IContainer>(\n 'rendererRoot',\n runtime.renderer.map((tier) => tier.rendererRoot),\n ['get', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n\n const dispatchOperation = async (operation: string, input?: unknown): Promise<unknown> => {\n if (!operations.has(operation)) {\n throw new TestWoodError(\n `operation \"${operation}\" is not in the selected aperture; available operations:\\n ${[...operations.keys()].join('\\n ')}`,\n );\n }\n const { router: live } = await runtime.main.acquire();\n return await ExecutionContext.run(root as never, () => live.dispatch(operation, input));\n };\n const api = createOperationApi(allOperations.keys(), dispatchOperation) as WoodTestApi;\n\n return {\n root,\n router,\n operations,\n api,\n rawApi: api as WoodTestRawApi,\n call: dispatchOperation as TestWoodEnvironment['call'],\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n await runtime.main.acquire();\n const scope = root.extend();\n const ownsController = !root.isRegistered(controller);\n let instance: T | undefined;\n controllerScopes.push({ dispose: () => ExecutionContext.run(scope as never, () => cleanupAll('controller cleanup failed', [\n ['controller', async () => { if (ownsController) await disposeControllerLifecycle(instance); }],\n ['scope', () => scope.dispose()],\n ])) as Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n instance = await ExecutionContext.run(scope as never, () => scope.get(controller)) as T;\n guardControllerLifecycleOnce(instance);\n return instance;\n },\n get: async <T>(token: unknown): Promise<T> => {\n await runtime.main.acquire();\n return (await ExecutionContext.run(root as never, () => root.get(token))) as T;\n },\n\n get frontend() { return frontend ?? tierMissing('pages', 'renderer'); },\n get events() { return events ?? tierMissing('pages', 'renderer'); },\n get rendererRoot() { return rendererRoot ?? tierMissing('pages', 'renderer'); },\n open: async (page, params, query) => (await pages().acquire()).open(page, params, query),\n navigate: async (page, params, query) => (await pages().acquire()).navigate(page, params, query),\n current: () => pages().current?.current() ?? null,\n settle: async () => {\n await parts.network.settle();\n // Finalization applies to main-only roots too. Waiting must never start\n // an unused product or renderer merely to discover that it is idle.\n if (runtime.main.requested) await (await runtime.main.acquire()).router.drain();\n if (runtime.renderer?.requested) await (await runtime.renderer.acquire()).settle();\n },\n\n get target() {\n if (!domSelected) tierMissing('dom', 'dom driver');\n const tier = pages().current;\n if (!tier) throw new TestWoodError('no page is mounted; open(page) first');\n return tier.dom!.target;\n },\n click: async (selector) => (await dom()).click(selector),\n type: async (selector, text) => (await dom()).type(selector, text),\n submit: async (selector) => (await dom()).submit(selector),\n text: async (selector) => (await dom()).text(selector),\n exists: async (selector) => (await dom()).exists(selector),\n\n // Method expectations only: `settle()` is the completion contract for\n // owned renderer work, and a test may legitimately verify while a\n // deliberately deferred stream is still in flight.\n verify: async () => {\n const failures: Array<{ owner: string; error: unknown }> = [];\n await env.verify().catch((error) => failures.push({ owner: 'main', error }));\n await executables.main.verify().catch((error) => failures.push({ owner: 'main middleware', error }));\n const renderer = runtime.renderer?.current ?? null;\n if (renderer) {\n await renderer.verify().catch((error) => failures.push({ owner: 'renderer', error }));\n }\n await executables.renderer.verify().catch((error) => failures.push({ owner: 'renderer loaders', error }));\n if (failures.length === 1) throw failures[0]!.error;\n if (failures.length) {\n throw new TestWoodError(\n 'testWood verification failed in several owners:\\n ' +\n failures.map(({ owner, error }) =>\n `${owner}: ${error instanceof Error ? error.message : String(error)}`).join('\\n '),\n );\n }\n },\n dispose,\n // One inspection surface for both owners: a renderer-owned token's watched\n // method is readable through the same environment handle as a main one.\n // The main registry is consulted first; the renderer registry only once the\n // renderer runtime has actually been initialized.\n [ENV_REGISTRY]: {\n inspect(token: unknown, method: string) {\n const main = (env as unknown as Record<symbol, { inspect(token: unknown, method: string): unknown }>)[ENV_REGISTRY];\n const renderer = (runtime.renderer?.current as unknown as Record<symbol, { inspect(token: unknown, method: string): unknown }> | null | undefined)?.[ENV_REGISTRY];\n try {\n return main.inspect(token, method);\n } catch (error) {\n if (!renderer) throw error;\n try {\n return renderer.inspect(token, method);\n } catch {\n throw error;\n }\n }\n },\n },\n };\n}\n\nexport class TestWoodBuilder<H extends WoodBrowserHost | null = null> {\n private constructor(private readonly state: BuilderState) {}\n\n static create(configPath: string): TestWoodBuilder {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n return new TestWoodBuilder({\n configPath: path.resolve(configPath),\n selection: null,\n browserHost: null,\n fetchConfigurations: [],\n importer: defaultImporter,\n overlays: testStub(),\n });\n }\n\n fetch(configure: ConfigureFetch): TestWoodBuilder<H> {\n if (typeof configure !== 'function') throw new TypeError('fetch() requires a route configuration callback');\n return this.derive({fetchConfigurations: Object.freeze([...this.state.fetchConfigurations, configure])});\n }\n\n browser(): TestWoodBuilder<'playwright'>;\n browser(host: 'playwright'): TestWoodBuilder<'playwright'>;\n browser(host: 'happy_dom'): TestWoodBuilder<'happy_dom'>;\n browser(host: WoodBrowserHost = 'playwright'): TestWoodBuilder<WoodBrowserHost> {\n if (host !== 'playwright' && host !== 'happy_dom') throw new TestWoodError('browser() accepts playwright or happy_dom');\n return new TestWoodBuilder<WoodBrowserHost>({...this.state, browserHost: host});\n }\n\n /** Declare selected tiers, snapshotting the source selector at call time. */\n select(selection: WoodTestSelection): TestWoodBuilder<H> {\n const resolved = resolveSelection(selection);\n return this.derive({ selection: Object.freeze({\n ...(resolved.main !== null ? { main: resolved.main } : {}),\n ...(resolved.renderer !== null ? { renderer: resolved.renderer } : {}),\n }) });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder<H> {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /** Compose a reusable canonical testStub() replacement description. */\n use(preset: TestStub): TestWoodBuilder<H> {\n if (!(preset instanceof TestStub)) throw new TestWoodError('use() requires testStub()');\n return this.derive({ overlays: this.state.overlays.use(preset) });\n }\n\n /* -- canonical singular replacements (shared @noego/testing engine) ------ */\n\n /** Replace the class bound to an exact declared token; the declared lifetime is kept. */\n class<T>(target: ClassToken<T>, replacement: ReplacementClass<T>, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.class(target, replacement, options) });\n }\n\n /** Replace a declared factory/provider (dependencies and lifetime kept) or a callable value. */\n function(target: unknown, replacement: ReplacementFunction, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.function(target, replacement, options) });\n }\n\n /** A test.* method seam on the FINAL binding of an exact declared token. */\n method(target: unknown, method: string, replacement: MethodDescriptor, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.method(target, method, replacement, options) });\n }\n\n /** Provide a caller-owned value for an exact declared token (never disposed by the app). */\n value(target: unknown, replacement: unknown, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.value(target, replacement, options) });\n }\n\n /**\n * Replace the executable satisfying a production middleware identity\n * (`file.path:selector` as written in the operations source). Membership,\n * order and inheritance always come from the source.\n */\n middleware(identity: string, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.middleware(identity, replacement, options) });\n }\n\n /**\n * Replace the DATA LOADER behind a selected page/layout: the companion\n * `.load` source of a view (`pages/chat.svelte` → `pages/chat.load.ts`).\n * The identity is the config-relative source path (`ui/pages/chat.load.ts`),\n * export `default`; the executable receives the loader `RequestData`\n * (`url` = page identity, `params`, `query`) and yields the data the\n * controller's `initialize({ params, query, data })` receives. A source\n * with no companion loader is not a declared identity (build error).\n * Controllers are replaced with `.class`, never here.\n */\n load(identity: LoaderIdentity, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.load(identity, replacement, options) });\n }\n\n /** Build; without `select()`, every owner the config declares is selected whole. */\n async build(): Promise<WoodTestEnvironmentFor<H>> {\n return await assembleEnvironment(this.state, this.state.selection ? resolveSelection(this.state.selection) : null) as WoodTestEnvironmentFor<H>;\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder<H> {\n return new TestWoodBuilder({ ...this.state, ...patch });\n }\n}\n\nexport function testWood(configPath: string): TestWoodBuilder {\n return TestWoodBuilder.create(configPath);\n}\n"],"mappings":"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","names":["import_fetch","import_ioc","path"]}
@@ -588,7 +588,26 @@ function environmentSurface(parts) {
588
588
  }
589
589
  },
590
590
  dispose,
591
- [ENV_REGISTRY]: env[ENV_REGISTRY]
591
+ // One inspection surface for both owners: a renderer-owned token's watched
592
+ // method is readable through the same environment handle as a main one.
593
+ // The main registry is consulted first; the renderer registry only once the
594
+ // renderer runtime has actually been initialized.
595
+ [ENV_REGISTRY]: {
596
+ inspect(token, method) {
597
+ const main = env[ENV_REGISTRY];
598
+ const renderer = runtime.renderer?.current?.[ENV_REGISTRY];
599
+ try {
600
+ return main.inspect(token, method);
601
+ } catch (error) {
602
+ if (!renderer) throw error;
603
+ try {
604
+ return renderer.inspect(token, method);
605
+ } catch {
606
+ throw error;
607
+ }
608
+ }
609
+ }
610
+ }
592
611
  };
593
612
  }
594
613
  class TestWoodBuilder {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["import { FETCH_RUNTIME } from '@noego/runtime/fetch';\nimport { createTestFetch, defineFetchRoutes, type ConfigureFetch, type TestFetchRuntime } from '@noego/testing/fetch';\nimport type { WoodBrowserHost, WoodTestEnvironmentFor, NativeWoodTestEnvironment } from './native_browser/native_browser.types.js';\nimport { disposeControllerLifecycle, guardControllerLifecycleOnce } from '../controller/controller_lifecycle.js';\nimport type { WoodTestApi, WoodTestRawApi } from '../testing-api.js';\n/**\n * `testWood(configPath)` — the canonical Wood testing entry point.\n *\n * One persistent immutable builder over the production `wood.config.yml`:\n *\n * const app = await testWood('wood.config.yml')\n * .select({ main: { module: 'backend_service' }, renderer: { path: ['settings.backend-service'] } })\n * .class(BackendServiceLifecycleCoordinator, LifecycleStub)\n * .method(BackendServiceLifecycleCoordinator, 'getStatus', test.once(test.returns(status)))\n * .middleware('auth.session:require_session', test.original())\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * canonical singular replacements (`use(testStub())`, `class/function/method/\n * value` on exact declared tokens, `middleware` on source identities, `load`\n * on page/layout companion data-loader sources) decide what is replaced — resolved\n * by the shared `@noego/testing` overlay engine against each owner's declared\n * binding catalog. Without `select()`, every declared owner is selected whole.\n * The legacy plural vocabulary (`classes/functions/values/methods`,\n * `.main()/.renderer()`, `main: true`, `{ operation }`, `pages`) is retained\n * here for existing suites. Controllers, middleware and views are auto-bound\n * from the config; the app composes its backend through the optional\n * `main.testing.compose` hook and mounts pages through `renderer.testing.host`.\n *\n * Every build creates fresh `@noego/ioc` roots (main, and renderer when\n * pages are selected) — nothing global is touched, so environments coexist\n * in one process.\n */\nimport path from 'node:path';\nimport { LoadAs, type IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport {\n applyTestBindings,\n createTestExecutables,\n describeTestBindings,\n ENV_REGISTRY,\n resolveTestOverlays,\n testIoc,\n TestIocBuilder,\n TestStub,\n testStub,\n stubEntries,\n} from '@noego/testing';\nimport type {\n ExecutableReplacement,\n LoaderIdentity,\n LoaderReference,\n MethodDescriptor,\n ClassToken, ReplacementClass,\n ReplacementFunction,\n ReplacementOptions,\n ResolvedTestOverlay,\n TestEnvironment,\n TestExecutableOverlays,\n TestOwnerCatalog,\n} from '@noego/testing';\nimport { installWoodRuntime, WoodRouter } from '../runtime/runtime_module.js';\nimport { assertDataLoader, type DataLoader } from '../loader/loader_orchestration.js';\nimport { MiddlewareResolver } from '../middleware/middleware_resolver.js';\nimport type { WoodMiddlewareContext } from '../types/index.js';\nimport type { OperationDef, ViewDef } from '../types/index.js';\nimport type { WoodConfig } from '../types/config.js';\nimport type { FrontendApplicationSurface } from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { Navigation } from '../navigation/index.js';\nimport { createOperationApi, RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.js';\nimport {\n defaultImporter,\n importDefaultExport,\n loadWoodConfigSource,\n type ModuleImporter,\n type WoodConfigSource,\n} from './wood_config_source.js';\nimport {\n DECLARED_OWNERS,\n resolveSelection,\n selectOperations,\n selectRendererCatalog,\n type ResolvedWoodSelection,\n type WoodTestSelection,\n} from './wood_test_selection.js';\nimport {\n buildRendererTier,\n type RendererTier,\n type WoodDomDriver,\n type WoodRendererHooks,\n} from './wood_env_renderer.js';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.js';\nimport { LazyRuntimeStage, lazyMember } from './wood_lazy_runtime.js';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\n /** Standard per-environment backend push source; hosts subscribe automatically. */\n readonly events: RouterBridgeEvents;\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n readonly selection: ResolvedWoodSelection;\n /** The selected operations (the router will serve exactly these). */\n readonly operations: ReadonlyMap<string, OperationDef>;\n readonly importer: ModuleImporter;\n /**\n * Dispatch through the environment's router. Available from `start()`\n * onward (the router is built after compose returns); calling it during\n * compose itself fails.\n */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n}\n\n/** What the compose hook may hand back; every member is optional. */\nexport interface WoodComposeResult extends WoodRendererHooks {\n /**\n * Runs AFTER the test's substitutions are installed on the root (compose\n * itself runs before them, so production registrations never shadow a\n * seam). Seed world state and warm caches here — through the seamed graph,\n * exactly as the test will observe it.\n */\n start?(): void | Promise<void>;\n /**\n * Quiesce BEFORE the main root disposes its instances: silence push\n * delivery, drain detached work — everything that still needs the live\n * graph (database, tracer) the services' own dispose hooks run against.\n */\n drain?(): void | Promise<void>;\n /** Release resources AFTER the roots are disposed (close files, remove dirs). */\n dispose?(): void | Promise<void>;\n}\n\n/**\n * The app's backend composition on the root the builder hands it. Register\n * lazily: nothing expensive should run until a test resolves it.\n */\nexport type WoodComposeMain = (\n root: IContainer,\n context: WoodComposeContext,\n) => WoodComposeResult | void | Promise<WoodComposeResult | void>;\n\ntype Params = Record<string, string>;\n\nexport interface TestWoodEnvironment {\n /** The main-process IoC root of this environment. */\n readonly root: IContainer;\n readonly router: WoodRouter;\n /** The selected operations. */\n readonly operations: ReadonlyMap<string, OperationDef>;\n /** Typed nested operation API backed by the real selected WoodRouter. */\n readonly api: WoodTestApi;\n /** Object-shaped untyped payload surface for deliberate runtime-validation probes. */\n readonly rawApi: WoodTestRawApi;\n /** Execute a selected operation through the real pipeline. */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n /** Resolve a controller class through a fresh main scope (root seams apply). */\n controller<T>(controller: new (...args: never[]) => T): Promise<T>;\n /** Resolve any dependency from the main root; class tokens retain their instance type. */\n get<T>(token: { readonly prototype: T }): Promise<T>;\n get<T = unknown>(token: unknown): Promise<T>;\n\n /** Renderer surface — present only when `pages` was selected. */\n readonly frontend: FrontendApplicationSurface;\n /** Backend push emitter feeding renderer subscriptions (pages only). */\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n navigate(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n\n /** DOM driver — present only when `dom` was selected. */\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n\n /** Finalize `test.*` expectations; fails on undrained renderer work. */\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: unknown;\n}\n\ntype ControllerClass = new (...args: never[]) => unknown;\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly browserHost: WoodBrowserHost | null;\n readonly fetchConfigurations: readonly ConfigureFetch[];\n readonly importer: ModuleImporter;\n readonly overlays: TestStub;\n}\n\nconst CONTAINER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.container.registry');\n\nfunction globalRegistrySize(): number {\n const registry = (globalThis as Record<symbol, unknown>)[CONTAINER_REGISTRY_SYMBOL];\n return registry instanceof Map ? registry.size : 0;\n}\n\nfunction tierMissing(tier: 'renderer' | 'pages' | 'dom', member: string): never {\n throw new TestWoodError(\n `the ${member} is not available: this environment was built without { ${tier} } — add it to select()`,\n );\n}\n\nasync function loadController(\n source: WoodConfigSource,\n importer: ModuleImporter,\n reference: string,\n owner: 'main' | 'renderer',\n): Promise<ControllerClass> {\n const file = owner === 'main'\n ? source.mainControllerFile(reference)\n : source.rendererControllerFile(reference);\n const controller = await importDefaultExport<ControllerClass>(\n importer,\n file,\n `${owner} controller \"${reference}\"`,\n );\n if (typeof controller !== 'function') {\n throw new TestWoodError(`${owner} controller \"${reference}\" must default-export a class (${file})`);\n }\n return controller;\n}\n\nasync function loadMainControllers(\n source: WoodConfigSource,\n operations: ReadonlyMap<string, OperationDef>,\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n for (const operation of operations.values()) {\n if (!controllers.has(operation.controller)) {\n controllers.set(operation.controller, await loadController(source, importer, operation.controller, 'main'));\n }\n }\n return controllers;\n}\n\nasync function loadRendererControllers(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n const references = new Set<string>();\n for (const view of views) {\n if (view.controller) references.add(view.controller);\n for (const controller of view.layoutControllers ?? []) if (controller) references.add(controller);\n }\n for (const reference of references) {\n controllers.set(reference, await loadController(source, importer, reference, 'renderer'));\n }\n return controllers;\n}\n\nasync function runCompose(\n root: IContainer,\n source: WoodConfigSource,\n context: WoodComposeContext,\n): Promise<WoodComposeResult | null> {\n const composePath = source.hookModule(source.config.main.testing?.compose);\n if (!composePath) return null;\n const compose = await importDefaultExport<WoodComposeMain>(context.importer, composePath, 'main.testing.compose');\n if (typeof compose !== 'function') {\n throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);\n }\n return (await ExecutionContext.run(root as never, () => compose(root, context))) ?? null;\n}\n\n/**\n * Dispose the main root under its own execution context (service teardown\n * hooks resolve collaborators such as tracers exactly like any other call),\n * naming every instance whose dispose hook failed.\n */\nasync function disposeRoot(env: TestEnvironment): Promise<void> {\n try {\n await ExecutionContext.run(env.root as never, () => env.dispose());\n } catch (error) {\n const failures = (error as { failures?: Array<{ token: string; error: unknown }> }).failures;\n if (!Array.isArray(failures)) throw error;\n const detail = failures\n .map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`)\n .join('\\n ');\n throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):\\n ${detail}`);\n }\n}\n\nfunction assertNoGlobalContainerLeak(before: number, source: WoodConfigSource): void {\n if (!process.env.VITEST) return;\n const after = globalRegistrySize();\n if (after > before) {\n throw new TestWoodError(\n `${source.config.main.testing?.compose ?? 'the build'} registered ${after - before} process-global ` +\n 'Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.',\n );\n }\n}\n\ntype SelectedPageCatalog = NonNullable<ReturnType<typeof selectRendererCatalog>>;\ntype ControllerScope = { dispose(): Promise<void> };\n\n/** Per-build executable seams (`.middleware` on main, `.load` on renderer). */\ninterface AssemblyExecutables {\n readonly main: TestExecutableOverlays;\n readonly renderer: TestExecutableOverlays;\n /** Middleware identities the test replaced (only these get a registry executable). */\n readonly middleware: ReadonlySet<string>;\n}\n\ninterface AssemblyLiveState {\n composed: WoodComposeResult | null;\n env: TestEnvironment | null;\n renderer: RendererTier | null;\n router: WoodRouter | null;\n /** Set once overlays are resolved (before the router is constructed on first use). */\n executables: AssemblyExecutables | null;\n /** Set once the environment's runtime stages exist (after build). */\n runtime: AssemblyRuntime | null;\n /** dispose() was requested; a runtime that has not started must not start. */\n disposed: boolean;\n /** A runtime stage already rolled the environment back; never roll back twice. */\n rolledBack: boolean;\n}\n\n/** The managed main runtime: constructed router + `compose.start()` done. */\ninterface MainRuntime {\n readonly router: WoodRouter;\n}\n\n/**\n * Lazily initialized runtime stages. `main` constructs the managed router\n * and runs `compose.start()`; `renderer` (pages only) builds the renderer\n * tier over the main router. Each initializes exactly once on first use.\n */\ninterface AssemblyRuntime {\n readonly main: LazyRuntimeStage<MainRuntime>;\n readonly renderer: LazyRuntimeStage<RendererTier> | null;\n}\n\ninterface AssemblyInputs {\n readonly network: TestFetchRuntime;\n readonly state: BuilderState;\n readonly selection: ResolvedWoodSelection;\n readonly source: WoodConfigSource;\n readonly operations: Map<string, OperationDef>;\n readonly catalog: SelectedPageCatalog | null;\n readonly registryBefore: number;\n readonly mainRegistrations: RegistrationOnlyRoot;\n readonly rendererRegistrations: RegistrationOnlyRoot | null;\n readonly controllerScopes: ControllerScope[];\n readonly live: AssemblyLiveState;\n readonly context: WoodComposeContext;\n readonly dispose: () => Promise<void>;\n}\n\nfunction createComposeContext(\n source: WoodConfigSource,\n state: BuilderState,\n selection: ResolvedWoodSelection,\n operations: Map<string, OperationDef>,\n live: AssemblyLiveState,\n): WoodComposeContext {\n return {\n events: new RouterBridgeEvents(),\n configPath: source.configPath,\n configDir: source.configDir,\n config: source.config,\n selection,\n operations,\n importer: state.importer,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!live.env || (!live.router && !live.runtime)) {\n throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n }\n // Inside start() the router already exists (it is constructed before\n // start runs); after build, the first call initializes the runtime.\n const router = live.router ?? (await live.runtime!.main.acquire()).router;\n return (await ExecutionContext.run(\n live.env.root as never,\n () => router.dispatch(operation, input),\n )) as T;\n },\n };\n}\n\nfunction createAssemblyDisposer(\n live: AssemblyLiveState,\n mainRegistrations: RegistrationOnlyRoot,\n rendererRegistrations: RegistrationOnlyRoot | null,\n controllerScopes: ControllerScope[],\n network: TestFetchRuntime,\n): () => Promise<void> {\n let closing: Promise<void> | null = null;\n return (): Promise<void> => {\n live.disposed = true;\n return closing ??= cleanupAll('testWood cleanup failed', [\n // An initialization racing this dispose finishes first (it refuses to\n // complete once `disposed` is set) so every resource it created late is\n // released below. A rollback-triggered dispose skips this: its own\n // stage has already finished and waiting would be a self-await.\n ['runtime settle', async () => {\n if (live.rolledBack || !live.runtime) return;\n await live.runtime.main.settled();\n await live.runtime.renderer?.settled();\n }],\n ['renderer', async () => { if (live.renderer) await live.renderer.dispose(); }],\n ...[...controllerScopes].reverse().map(\n (scope, index) => [`controller scope ${controllerScopes.length - index}`, () => scope.dispose()] as const,\n ),\n ['compose drain', async () => {\n const root = live.env?.root ?? mainRegistrations.actualRoot;\n if (!root.isDisposed()) await ExecutionContext.run(root as never, () => live.composed?.drain?.());\n }],\n ['main root', async () => {\n if (live.env) await disposeRoot(live.env);\n else await mainRegistrations.actualRoot.dispose();\n }],\n ['unbuilt renderer root', async () => {\n if (!live.renderer && rendererRegistrations) await rendererRegistrations.actualRoot.dispose();\n }],\n ['compose external dispose', async () => { await live.composed?.dispose?.(); }],\n ['external fetch', () => network.dispose()],\n ]);\n };\n}\n\nasync function loadAssemblyControllers(\n inputs: AssemblyInputs,\n): Promise<{\n main: Map<string, ControllerClass>;\n renderer: Map<string, ControllerClass>;\n loaders: Map<string, DataLoader>;\n}> {\n const main = await loadMainControllers(inputs.source, inputs.operations, inputs.state.importer);\n const renderer = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererControllers(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, ControllerClass>();\n const loaders = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererLoaders(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, DataLoader>();\n return { main, renderer, loaders };\n}\n\nfunction installAssemblyRuntime(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n installWoodRuntime(inputs.mainRegistrations.root, {\n controllersDir: inputs.source.mainControllersDir,\n middlewareDir: inputs.source.mainMiddlewareDir,\n mode: 'dev',\n registerRuntime: (configuredRouter) => {\n for (const [identity, controller] of mainControllers) {\n configuredRouter.registerControllerLoader(identity, () => Promise.resolve({ default: controller }));\n }\n configuredRouter.loadOperations(inputs.operations);\n registerMiddlewareSeams(inputs, configuredRouter);\n },\n });\n}\n\n/**\n * `.middleware(identity, …)`: the production identity keeps its membership,\n * order and inheritance (they come from the operations source); only the\n * executable satisfying it is replaced, through the router's registry seam.\n * The source executable is resolved lazily from `main.middleware`, exactly\n * as production would, and handed to the shared test.* runtime once.\n */\nfunction registerMiddlewareSeams(inputs: AssemblyInputs, configuredRouter: WoodRouter): void {\n const executables = inputs.live.executables;\n if (!executables || !executables.middleware.size) return;\n const sourceResolver = new MiddlewareResolver(inputs.source.mainMiddlewareDir);\n for (const spec of executables.middleware) {\n configuredRouter.registerMiddleware(spec, async (context: WoodMiddlewareContext) => {\n const original = await sourceResolver.resolve(spec);\n return executables.main.replace('middleware', spec, original as ReplacementFunction)(context);\n });\n }\n}\n\n/** Production middleware identities the selected operations run, in source order. */\nfunction selectedMiddleware(operations: ReadonlyMap<string, OperationDef>): string[] {\n const identities = new Set<string>();\n for (const operation of operations.values()) for (const spec of operation.middleware) identities.add(spec);\n return [...identities];\n}\n\n/**\n * Discover and import every companion data loader (`pages/chat.svelte` →\n * `pages/chat.load.ts`) of the selected pages/layouts. Discovery and import\n * happen at build (a missing default loader function fails the build); no\n * loader RUNS until a page opens. The identity is the config-relative\n * source path with export `default` — the `.load` seam identity.\n */\nasync function loadRendererLoaders(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, DataLoader>> {\n const loaders = new Map<string, DataLoader>();\n for (const view of views) {\n for (const viewPath of [...view.layouts, view.viewPath]) {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference || loaders.has(reference.identity)) continue;\n const mod = (await importer(reference.file)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new TestWoodError(`data loader ${reference.identity} (${reference.file}) has no default export`);\n }\n try {\n loaders.set(reference.identity, assertDataLoader(mod.default, `data loader ${reference.identity}`));\n } catch (error) {\n throw new TestWoodError(error instanceof Error ? error.message : String(error));\n }\n }\n }\n return loaders;\n}\n\n/** The native renderer loader identities: every discovered companion loader source. */\nfunction rendererLoaderReferences(loaders: ReadonlyMap<string, DataLoader>): LoaderReference[] {\n return [...loaders.keys()].map((identity) => Object.freeze({ path: identity, export: 'default' }));\n}\n\nasync function configureRendererRegistrations(inputs: AssemblyInputs): Promise<void> {\n const registrations = inputs.rendererRegistrations;\n if (!registrations) return;\n // Bound by the renderer tier on first use (see buildRendererTier); declared\n // here so the shared overlay catalog can resolve replacements for them.\n registrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n registrations.declareBinding(Navigation, 'singleton');\n registrations.declareBinding(WOOD_FRONTEND_BRIDGE, 'singleton');\n const composePath = inputs.source.hookModule(inputs.source.config.renderer.compose);\n if (composePath) {\n const composeRenderer = await importDefaultExport<(root: IContainer) => void | Promise<void>>(\n inputs.state.importer,\n composePath,\n 'renderer.compose',\n );\n if (typeof composeRenderer !== 'function') {\n throw new TestWoodError(`renderer.compose default export must be a function (${composePath})`);\n }\n await ExecutionContext.run(registrations.root as never, () => composeRenderer(registrations.root));\n }\n await ExecutionContext.run(\n registrations.root as never,\n () => inputs.live.composed?.configureRendererRoot?.(registrations.root),\n );\n}\n\nfunction registerControllersBeforeOverlays(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n rendererControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n for (const controller of mainControllers.values()) {\n inputs.mainRegistrations.declare(controller);\n if (!inputs.mainRegistrations.actualRoot.isRegistered(controller)) {\n inputs.mainRegistrations.root.registerClass(controller);\n }\n }\n for (const controller of rendererControllers.values()) {\n inputs.rendererRegistrations?.declare(controller);\n }\n}\n\ninterface AssemblyIoc {\n readonly main: TestIocBuilder;\n readonly mainBorrowed: readonly unknown[];\n readonly renderer: TestIocBuilder;\n readonly rendererBorrowed: readonly unknown[];\n readonly overlays: readonly ResolvedTestOverlay[];\n readonly executables: AssemblyExecutables;\n}\n\n/**\n * Legacy plural calls are routed by owner token sets (unchanged); the\n * canonical singular replacements are resolved by the shared overlay engine\n * against each owner's DECLARED binding catalog — production registrations\n * recorded on the root, legacy explicit composition, auto-bound controllers,\n * the selected middleware identities and the page/layout loader identities.\n */\nfunction buildAssemblyIoc(\n inputs: AssemblyInputs,\n controllers: {\n main: ReadonlyMap<string, ControllerClass>;\n renderer: ReadonlyMap<string, ControllerClass>;\n loaders: ReadonlyMap<string, DataLoader>;\n },\n): AssemblyIoc {\n const catalogs: TestOwnerCatalog[] = [{\n owner: 'main',\n bindings: describeTestBindings(testIoc().use(inputs.mainRegistrations.module()), [...controllers.main.values()]),\n middleware: selectedMiddleware(inputs.operations),\n loaders: [],\n }];\n if (inputs.rendererRegistrations && inputs.catalog) {\n catalogs.push({\n owner: 'renderer',\n bindings: describeTestBindings(testIoc().use(inputs.rendererRegistrations.module()), [...controllers.renderer.values()]),\n middleware: [],\n loaders: rendererLoaderReferences(controllers.loaders),\n });\n }\n const overlays = resolveTestOverlays(inputs.state.overlays, catalogs);\n const mainPlan = applyTestBindings(testIoc(), overlays, 'main');\n const rendererPlan = applyTestBindings(testIoc(), overlays, 'renderer');\n return {\n main: mainPlan.builder,\n mainBorrowed: mainPlan.buildOptions.borrowedTokens ?? [],\n renderer: rendererPlan.builder,\n rendererBorrowed: rendererPlan.buildOptions.borrowedTokens ?? [],\n overlays,\n executables: {\n main: createTestExecutables(overlays, 'main'),\n renderer: createTestExecutables(overlays, 'renderer'),\n middleware: new Set(overlays\n .filter((overlay) => overlay.owner === 'main' && overlay.entry.kind === 'middleware')\n .map((overlay) => overlay.entry.target as string)),\n },\n };\n}\n\n/**\n * Roll the whole environment back after a failed first use: every resource a\n * partially initialized runtime created is released, and both the primary\n * error and any cleanup failures are preserved. Runs at most once.\n */\nfunction createRuntimeRollback(inputs: AssemblyInputs): (error: unknown) => Promise<never> {\n return (error) => {\n // Already rolled back, or dispose() is in flight / done: that disposer\n // owns the cleanup (it waits for this attempt), so never re-enter it here.\n if (inputs.live.rolledBack || inputs.live.disposed) return Promise.reject(error);\n inputs.live.rolledBack = true;\n return rollbackEnvironment(error, inputs.dispose, 'testWood runtime initialization failed and rollback failed');\n };\n}\n\nfunction assertRuntimeMayStart(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed before its ${stage} runtime was used`);\n }\n}\n\n/**\n * Re-checked after every await of a first initialization: dispose() may have\n * been requested meanwhile. The disposer waits for this attempt, so failing\n * here hands every late resource to it instead of exposing a disposed root.\n */\nfunction assertRuntimeMayContinue(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed while its ${stage} runtime was initializing`);\n }\n}\n\n/** First use: construct the managed router, then run `compose.start()` on the seamed graph. */\nasync function initializeMainRuntime(inputs: AssemblyInputs): Promise<MainRuntime> {\n assertRuntimeMayStart(inputs, 'main');\n const root = inputs.live.env!.root;\n const router = await Promise.resolve(\n ExecutionContext.run(root as never, () => root.get(WoodRouter)),\n ) as WoodRouter;\n inputs.live.router = router;\n assertRuntimeMayContinue(inputs, 'main');\n await ExecutionContext.run(root as never, () => inputs.live.composed?.start?.());\n assertRuntimeMayContinue(inputs, 'main');\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n return { router };\n}\n\ninterface RendererRuntimeInputs {\n readonly ioc: TestIocBuilder;\n readonly borrowedTokens: readonly unknown[];\n readonly controllerClasses: ReadonlyMap<string, ControllerClass>;\n readonly events: RouterBridgeEvents;\n readonly loaders: TestExecutableOverlays;\n /** Imported companion data loaders by `.load` identity (none has run). */\n readonly loaderModules: ReadonlyMap<string, DataLoader>;\n}\n\n/** First renderer use: the main runtime, then the renderer tier over its router. */\nasync function initializeRendererRuntime(\n inputs: AssemblyInputs,\n main: LazyRuntimeStage<MainRuntime>,\n renderer: RendererRuntimeInputs,\n): Promise<RendererTier> {\n const { router } = await main.acquire();\n assertRuntimeMayStart(inputs, 'renderer');\n const composed = inputs.live.composed;\n const configurePageScope = composed?.configurePageScope?.bind(composed);\n const hooks: WoodRendererHooks = {\n events: renderer.events,\n ...(configurePageScope ? { configurePageScope } : {}),\n };\n const tier = await buildRendererTier({\n source: inputs.source,\n router,\n catalog: inputs.catalog!,\n operations: inputs.operations,\n importer: inputs.state.importer,\n hooks,\n dom: null,\n ioc: renderer.ioc,\n borrowedTokens: renderer.borrowedTokens,\n registrations: inputs.rendererRegistrations!,\n controllerClasses: renderer.controllerClasses,\n loaders: renderer.loaders,\n loaderModules: renderer.loaderModules,\n });\n // Hand the tier to the disposer before refusing: a dispose() that raced\n // this build releases it through the regular 'renderer' cleanup step.\n inputs.live.renderer = tier;\n assertRuntimeMayContinue(inputs, 'renderer');\n return tier;\n}\n\nfunction createAssemblyRuntime(\n inputs: AssemblyInputs,\n renderer: RendererRuntimeInputs | null,\n): AssemblyRuntime {\n const rollback = createRuntimeRollback(inputs);\n const main = new LazyRuntimeStage<MainRuntime>(() => initializeMainRuntime(inputs), rollback);\n const rendererStage = renderer\n ? new LazyRuntimeStage<RendererTier>(() => initializeRendererRuntime(inputs, main, renderer), rollback)\n : null;\n return { main, renderer: rendererStage };\n}\n\n/**\n * Build: read the source, record registrations (compose, runtime module,\n * renderer hooks, controllers) and install the test overlays on the main root.\n * Nothing is resolved or started here — the managed router, `compose.start()`\n * and the renderer tier initialize on first explicit use.\n */\nasync function materializeEnvironment(inputs: AssemblyInputs): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n inputs.mainRegistrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n const controllers = await loadAssemblyControllers(inputs);\n inputs.live.composed = await runCompose(inputs.mainRegistrations.root, inputs.source, inputs.context);\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n installAssemblyRuntime(inputs, controllers.main);\n await configureRendererRegistrations(inputs);\n registerControllersBeforeOverlays(inputs, controllers.main, controllers.renderer);\n const ioc = buildAssemblyIoc(inputs, controllers);\n inputs.live.executables = ioc.executables;\n\n inputs.live.env = await buildRecordedEnvironment(ioc.main, inputs.mainRegistrations, 'main', ioc.mainBorrowed);\n const root = inputs.live.env.root;\n\n const renderer: RendererRuntimeInputs | null = inputs.catalog && inputs.rendererRegistrations\n ? {\n ioc: ioc.renderer,\n borrowedTokens: ioc.rendererBorrowed,\n controllerClasses: controllers.renderer,\n events: inputs.live.composed?.events ?? inputs.context.events,\n loaders: ioc.executables.renderer,\n loaderModules: controllers.loaders,\n }\n : null;\n const runtime = createAssemblyRuntime(inputs, renderer);\n inputs.live.runtime = runtime;\n\n const environment = environmentSurface({\n root,\n network: inputs.network,\n env: inputs.live.env,\n operations: inputs.operations,\n runtime,\n allOperations: inputs.source.operations,\n events: renderer?.events ?? inputs.live.composed?.events ?? inputs.context.events,\n dom: false,\n controllerScopes: inputs.controllerScopes,\n executables: ioc.executables,\n dispose: inputs.dispose,\n });\n if (inputs.state.browserHost) {\n const { createNativeBrowserEnvironment } = await import('./native_browser/native_browser_environment.js');\n return createNativeBrowserEnvironment({\n source: inputs.source,\n views: inputs.catalog!.all(),\n environment,\n network: inputs.network,\n events: () => inputs.live.composed?.events ?? inputs.context.events,\n }, inputs.state.browserHost);\n }\n return environment;\n}\n\nasync function rollbackEnvironment(\n error: unknown,\n dispose: () => Promise<void>,\n message = 'testWood build failed and rollback failed',\n): Promise<never> {\n try {\n await dispose();\n } catch (cleanupError) {\n const failures = [\n ...(error instanceof TestWoodCleanupError ? error.failures : []),\n ...(cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'build rollback', error: cleanupError }]),\n ];\n throw new TestWoodCleanupError(message, failures, error);\n }\n throw error;\n}\n\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selected: ResolvedWoodSelection | null,\n): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n const source = loadWoodConfigSource(state.configPath);\n // No selection: every owner the config declares, whole ({}). A Wood\n // config declares both `main` and `renderer` sections.\n let selection = selected ?? resolveSelection(\n Object.fromEntries(DECLARED_OWNERS.filter((owner) => source.config[owner]).map((owner) => [owner, {}])),\n );\n // An omitted main owner exposes no operations; a selected renderer still gets\n // a main root for dependencies but does not widen the main operation aperture.\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selectRendererCatalog(source.views, selection);\n if (state.browserHost && !catalog) {\n throw new TestWoodError('browser() requires a selected renderer owner; add renderer to select()');\n }\n if (state.browserHost && stubEntries(state.overlays).some(entry => entry.kind === 'load' || entry.owner === 'renderer')) {\n throw new TestWoodError('Native browser renderer replacements are not supported yet: renderer code executes in its own realm. Keep main-process seams, or use the existing in-process renderer test environment for renderer .class/.method/.value/.load seams.');\n }\n const network = createTestFetch(state.fetchConfigurations.flatMap(defineFetchRoutes));\n const registryBefore = globalRegistrySize();\n const mainRegistrations = new RegistrationOnlyRoot('main.testing.compose', source.configPath);\n const rendererRegistrations = catalog && !state.browserHost\n ? new RegistrationOnlyRoot('configureRendererRoot', source.configPath)\n : null;\n const controllerScopes: ControllerScope[] = [];\n const live: AssemblyLiveState = {\n composed: null,\n env: null,\n renderer: null,\n router: null,\n executables: null,\n runtime: null,\n disposed: false,\n rolledBack: false,\n };\n const context = createComposeContext(source, state, selection, operations, live);\n const dispose = createAssemblyDisposer(\n live,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n network,\n );\n const inputs: AssemblyInputs = {\n network,\n state,\n selection,\n source,\n operations,\n catalog,\n registryBefore,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n live,\n context,\n dispose,\n };\n\n try {\n return await materializeEnvironment(inputs);\n } catch (error) {\n return rollbackEnvironment(error, dispose);\n }\n}\n\ninterface SurfaceParts {\n network: TestFetchRuntime;\n root: IContainer;\n env: TestEnvironment;\n operations: Map<string, OperationDef>;\n runtime: AssemblyRuntime;\n /** Every production operation, used to keep `api` object shape stable across selections. */\n allOperations: ReadonlyMap<string, OperationDef>;\n /** Backend push emitter (pages only); available from build, before any renderer use. */\n events: RouterBridgeEvents | null;\n dom: boolean;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n executables: AssemblyExecutables;\n dispose(): Promise<void>;\n}\n\nconst OPEN_FIRST = 'await open(page) (or another renderer call) first';\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, operations, allOperations, runtime, events, dom: domSelected, controllerScopes, executables, dispose } = parts;\n const pages = (): LazyRuntimeStage<RendererTier> => runtime.renderer ?? tierMissing('pages', 'renderer');\n const dom = async (): Promise<WoodDomDriver> => {\n if (!domSelected) tierMissing('dom', 'dom driver');\n return (await pages().acquire()).dom!;\n };\n\n // Stable lazy members: async use initializes the runtime; synchronous\n // members are served once it is live and fail with guidance before that.\n const router = lazyMember<WoodRouter>(\n 'router',\n runtime.main.map((main) => main.router),\n ['dispatch'],\n 'await call()/get()/controller() first',\n );\n const frontend = runtime.renderer\n ? lazyMember<FrontendApplicationSurface>(\n 'frontend',\n runtime.renderer.map((tier) => tier.frontend),\n ['open', 'act', 'settle', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n const rendererRoot = runtime.renderer\n ? lazyMember<IContainer>(\n 'rendererRoot',\n runtime.renderer.map((tier) => tier.rendererRoot),\n ['get', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n\n const dispatchOperation = async (operation: string, input?: unknown): Promise<unknown> => {\n if (!operations.has(operation)) {\n throw new TestWoodError(\n `operation \"${operation}\" is not in the selected aperture; available operations:\\n ${[...operations.keys()].join('\\n ')}`,\n );\n }\n const { router: live } = await runtime.main.acquire();\n return await ExecutionContext.run(root as never, () => live.dispatch(operation, input));\n };\n const api = createOperationApi(allOperations.keys(), dispatchOperation) as WoodTestApi;\n\n return {\n root,\n router,\n operations,\n api,\n rawApi: api as WoodTestRawApi,\n call: dispatchOperation as TestWoodEnvironment['call'],\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n await runtime.main.acquire();\n const scope = root.extend();\n const ownsController = !root.isRegistered(controller);\n let instance: T | undefined;\n controllerScopes.push({ dispose: () => ExecutionContext.run(scope as never, () => cleanupAll('controller cleanup failed', [\n ['controller', async () => { if (ownsController) await disposeControllerLifecycle(instance); }],\n ['scope', () => scope.dispose()],\n ])) as Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n instance = await ExecutionContext.run(scope as never, () => scope.get(controller)) as T;\n guardControllerLifecycleOnce(instance);\n return instance;\n },\n get: async <T>(token: unknown): Promise<T> => {\n await runtime.main.acquire();\n return (await ExecutionContext.run(root as never, () => root.get(token))) as T;\n },\n\n get frontend() { return frontend ?? tierMissing('pages', 'renderer'); },\n get events() { return events ?? tierMissing('pages', 'renderer'); },\n get rendererRoot() { return rendererRoot ?? tierMissing('pages', 'renderer'); },\n open: async (page, params, query) => (await pages().acquire()).open(page, params, query),\n navigate: async (page, params, query) => (await pages().acquire()).navigate(page, params, query),\n current: () => pages().current?.current() ?? null,\n settle: async () => {\n await parts.network.settle();\n // Finalization applies to main-only roots too. Waiting must never start\n // an unused product or renderer merely to discover that it is idle.\n if (runtime.main.requested) await (await runtime.main.acquire()).router.drain();\n if (runtime.renderer?.requested) await (await runtime.renderer.acquire()).settle();\n },\n\n get target() {\n if (!domSelected) tierMissing('dom', 'dom driver');\n const tier = pages().current;\n if (!tier) throw new TestWoodError('no page is mounted; open(page) first');\n return tier.dom!.target;\n },\n click: async (selector) => (await dom()).click(selector),\n type: async (selector, text) => (await dom()).type(selector, text),\n submit: async (selector) => (await dom()).submit(selector),\n text: async (selector) => (await dom()).text(selector),\n exists: async (selector) => (await dom()).exists(selector),\n\n // Method expectations only: `settle()` is the completion contract for\n // owned renderer work, and a test may legitimately verify while a\n // deliberately deferred stream is still in flight.\n verify: async () => {\n const failures: Array<{ owner: string; error: unknown }> = [];\n await env.verify().catch((error) => failures.push({ owner: 'main', error }));\n await executables.main.verify().catch((error) => failures.push({ owner: 'main middleware', error }));\n const renderer = runtime.renderer?.current ?? null;\n if (renderer) {\n await renderer.verify().catch((error) => failures.push({ owner: 'renderer', error }));\n }\n await executables.renderer.verify().catch((error) => failures.push({ owner: 'renderer loaders', error }));\n if (failures.length === 1) throw failures[0]!.error;\n if (failures.length) {\n throw new TestWoodError(\n 'testWood verification failed in several owners:\\n ' +\n failures.map(({ owner, error }) =>\n `${owner}: ${error instanceof Error ? error.message : String(error)}`).join('\\n '),\n );\n }\n },\n dispose,\n [ENV_REGISTRY]: (env as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n\nexport class TestWoodBuilder<H extends WoodBrowserHost | null = null> {\n private constructor(private readonly state: BuilderState) {}\n\n static create(configPath: string): TestWoodBuilder {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n return new TestWoodBuilder({\n configPath: path.resolve(configPath),\n selection: null,\n browserHost: null,\n fetchConfigurations: [],\n importer: defaultImporter,\n overlays: testStub(),\n });\n }\n\n fetch(configure: ConfigureFetch): TestWoodBuilder<H> {\n if (typeof configure !== 'function') throw new TypeError('fetch() requires a route configuration callback');\n return this.derive({fetchConfigurations: Object.freeze([...this.state.fetchConfigurations, configure])});\n }\n\n browser(): TestWoodBuilder<'playwright'>;\n browser(host: 'playwright'): TestWoodBuilder<'playwright'>;\n browser(host: 'happy_dom'): TestWoodBuilder<'happy_dom'>;\n browser(host: WoodBrowserHost = 'playwright'): TestWoodBuilder<WoodBrowserHost> {\n if (host !== 'playwright' && host !== 'happy_dom') throw new TestWoodError('browser() accepts playwright or happy_dom');\n return new TestWoodBuilder<WoodBrowserHost>({...this.state, browserHost: host});\n }\n\n /** Declare selected tiers, snapshotting the source selector at call time. */\n select(selection: WoodTestSelection): TestWoodBuilder<H> {\n const resolved = resolveSelection(selection);\n return this.derive({ selection: Object.freeze({\n ...(resolved.main !== null ? { main: resolved.main } : {}),\n ...(resolved.renderer !== null ? { renderer: resolved.renderer } : {}),\n }) });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder<H> {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /** Compose a reusable canonical testStub() replacement description. */\n use(preset: TestStub): TestWoodBuilder<H> {\n if (!(preset instanceof TestStub)) throw new TestWoodError('use() requires testStub()');\n return this.derive({ overlays: this.state.overlays.use(preset) });\n }\n\n /* -- canonical singular replacements (shared @noego/testing engine) ------ */\n\n /** Replace the class bound to an exact declared token; the declared lifetime is kept. */\n class<T>(target: ClassToken<T>, replacement: ReplacementClass<T>, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.class(target, replacement, options) });\n }\n\n /** Replace a declared factory/provider (dependencies and lifetime kept) or a callable value. */\n function(target: unknown, replacement: ReplacementFunction, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.function(target, replacement, options) });\n }\n\n /** A test.* method seam on the FINAL binding of an exact declared token. */\n method(target: unknown, method: string, replacement: MethodDescriptor, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.method(target, method, replacement, options) });\n }\n\n /** Provide a caller-owned value for an exact declared token (never disposed by the app). */\n value(target: unknown, replacement: unknown, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.value(target, replacement, options) });\n }\n\n /**\n * Replace the executable satisfying a production middleware identity\n * (`file.path:selector` as written in the operations source). Membership,\n * order and inheritance always come from the source.\n */\n middleware(identity: string, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.middleware(identity, replacement, options) });\n }\n\n /**\n * Replace the DATA LOADER behind a selected page/layout: the companion\n * `.load` source of a view (`pages/chat.svelte` → `pages/chat.load.ts`).\n * The identity is the config-relative source path (`ui/pages/chat.load.ts`),\n * export `default`; the executable receives the loader `RequestData`\n * (`url` = page identity, `params`, `query`) and yields the data the\n * controller's `initialize({ params, query, data })` receives. A source\n * with no companion loader is not a declared identity (build error).\n * Controllers are replaced with `.class`, never here.\n */\n load(identity: LoaderIdentity, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.load(identity, replacement, options) });\n }\n\n /** Build; without `select()`, every owner the config declares is selected whole. */\n async build(): Promise<WoodTestEnvironmentFor<H>> {\n return await assembleEnvironment(this.state, this.state.selection ? resolveSelection(this.state.selection) : null) as WoodTestEnvironmentFor<H>;\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder<H> {\n return new TestWoodBuilder({ ...this.state, ...patch });\n }\n}\n\nexport function testWood(configPath: string): TestWoodBuilder {\n return TestWoodBuilder.create(configPath);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["import { FETCH_RUNTIME } from '@noego/runtime/fetch';\nimport { createTestFetch, defineFetchRoutes, type ConfigureFetch, type TestFetchRuntime } from '@noego/testing/fetch';\nimport type { WoodBrowserHost, WoodTestEnvironmentFor, NativeWoodTestEnvironment } from './native_browser/native_browser.types.js';\nimport { disposeControllerLifecycle, guardControllerLifecycleOnce } from '../controller/controller_lifecycle.js';\nimport type { WoodTestApi, WoodTestRawApi } from '../testing-api.js';\n/**\n * `testWood(configPath)` — the canonical Wood testing entry point.\n *\n * One persistent immutable builder over the production `wood.config.yml`:\n *\n * const app = await testWood('wood.config.yml')\n * .select({ main: { module: 'backend_service' }, renderer: { path: ['settings.backend-service'] } })\n * .class(BackendServiceLifecycleCoordinator, LifecycleStub)\n * .method(BackendServiceLifecycleCoordinator, 'getStatus', test.once(test.returns(status)))\n * .middleware('auth.session:require_session', test.original())\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * canonical singular replacements (`use(testStub())`, `class/function/method/\n * value` on exact declared tokens, `middleware` on source identities, `load`\n * on page/layout companion data-loader sources) decide what is replaced — resolved\n * by the shared `@noego/testing` overlay engine against each owner's declared\n * binding catalog. Without `select()`, every declared owner is selected whole.\n * The legacy plural vocabulary (`classes/functions/values/methods`,\n * `.main()/.renderer()`, `main: true`, `{ operation }`, `pages`) is retained\n * here for existing suites. Controllers, middleware and views are auto-bound\n * from the config; the app composes its backend through the optional\n * `main.testing.compose` hook and mounts pages through `renderer.testing.host`.\n *\n * Every build creates fresh `@noego/ioc` roots (main, and renderer when\n * pages are selected) — nothing global is touched, so environments coexist\n * in one process.\n */\nimport path from 'node:path';\nimport { LoadAs, type IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport {\n applyTestBindings,\n createTestExecutables,\n describeTestBindings,\n ENV_REGISTRY,\n resolveTestOverlays,\n testIoc,\n TestIocBuilder,\n TestStub,\n testStub,\n stubEntries,\n} from '@noego/testing';\nimport type {\n ExecutableReplacement,\n LoaderIdentity,\n LoaderReference,\n MethodDescriptor,\n ClassToken, ReplacementClass,\n ReplacementFunction,\n ReplacementOptions,\n ResolvedTestOverlay,\n TestEnvironment,\n TestExecutableOverlays,\n TestOwnerCatalog,\n} from '@noego/testing';\nimport { installWoodRuntime, WoodRouter } from '../runtime/runtime_module.js';\nimport { assertDataLoader, type DataLoader } from '../loader/loader_orchestration.js';\nimport { MiddlewareResolver } from '../middleware/middleware_resolver.js';\nimport type { WoodMiddlewareContext } from '../types/index.js';\nimport type { OperationDef, ViewDef } from '../types/index.js';\nimport type { WoodConfig } from '../types/config.js';\nimport type { FrontendApplicationSurface } from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { Navigation } from '../navigation/index.js';\nimport { createOperationApi, RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.js';\nimport {\n defaultImporter,\n importDefaultExport,\n loadWoodConfigSource,\n type ModuleImporter,\n type WoodConfigSource,\n} from './wood_config_source.js';\nimport {\n DECLARED_OWNERS,\n resolveSelection,\n selectOperations,\n selectRendererCatalog,\n type ResolvedWoodSelection,\n type WoodTestSelection,\n} from './wood_test_selection.js';\nimport {\n buildRendererTier,\n type RendererTier,\n type WoodDomDriver,\n type WoodRendererHooks,\n} from './wood_env_renderer.js';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.js';\nimport { LazyRuntimeStage, lazyMember } from './wood_lazy_runtime.js';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\n /** Standard per-environment backend push source; hosts subscribe automatically. */\n readonly events: RouterBridgeEvents;\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n readonly selection: ResolvedWoodSelection;\n /** The selected operations (the router will serve exactly these). */\n readonly operations: ReadonlyMap<string, OperationDef>;\n readonly importer: ModuleImporter;\n /**\n * Dispatch through the environment's router. Available from `start()`\n * onward (the router is built after compose returns); calling it during\n * compose itself fails.\n */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n}\n\n/** What the compose hook may hand back; every member is optional. */\nexport interface WoodComposeResult extends WoodRendererHooks {\n /**\n * Runs AFTER the test's substitutions are installed on the root (compose\n * itself runs before them, so production registrations never shadow a\n * seam). Seed world state and warm caches here — through the seamed graph,\n * exactly as the test will observe it.\n */\n start?(): void | Promise<void>;\n /**\n * Quiesce BEFORE the main root disposes its instances: silence push\n * delivery, drain detached work — everything that still needs the live\n * graph (database, tracer) the services' own dispose hooks run against.\n */\n drain?(): void | Promise<void>;\n /** Release resources AFTER the roots are disposed (close files, remove dirs). */\n dispose?(): void | Promise<void>;\n}\n\n/**\n * The app's backend composition on the root the builder hands it. Register\n * lazily: nothing expensive should run until a test resolves it.\n */\nexport type WoodComposeMain = (\n root: IContainer,\n context: WoodComposeContext,\n) => WoodComposeResult | void | Promise<WoodComposeResult | void>;\n\ntype Params = Record<string, string>;\n\nexport interface TestWoodEnvironment {\n /** The main-process IoC root of this environment. */\n readonly root: IContainer;\n readonly router: WoodRouter;\n /** The selected operations. */\n readonly operations: ReadonlyMap<string, OperationDef>;\n /** Typed nested operation API backed by the real selected WoodRouter. */\n readonly api: WoodTestApi;\n /** Object-shaped untyped payload surface for deliberate runtime-validation probes. */\n readonly rawApi: WoodTestRawApi;\n /** Execute a selected operation through the real pipeline. */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n /** Resolve a controller class through a fresh main scope (root seams apply). */\n controller<T>(controller: new (...args: never[]) => T): Promise<T>;\n /** Resolve any dependency from the main root; class tokens retain their instance type. */\n get<T>(token: { readonly prototype: T }): Promise<T>;\n get<T = unknown>(token: unknown): Promise<T>;\n\n /** Renderer surface — present only when `pages` was selected. */\n readonly frontend: FrontendApplicationSurface;\n /** Backend push emitter feeding renderer subscriptions (pages only). */\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n navigate(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n\n /** DOM driver — present only when `dom` was selected. */\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n\n /** Finalize `test.*` expectations; fails on undrained renderer work. */\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: unknown;\n}\n\ntype ControllerClass = new (...args: never[]) => unknown;\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly browserHost: WoodBrowserHost | null;\n readonly fetchConfigurations: readonly ConfigureFetch[];\n readonly importer: ModuleImporter;\n readonly overlays: TestStub;\n}\n\nconst CONTAINER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.container.registry');\n\nfunction globalRegistrySize(): number {\n const registry = (globalThis as Record<symbol, unknown>)[CONTAINER_REGISTRY_SYMBOL];\n return registry instanceof Map ? registry.size : 0;\n}\n\nfunction tierMissing(tier: 'renderer' | 'pages' | 'dom', member: string): never {\n throw new TestWoodError(\n `the ${member} is not available: this environment was built without { ${tier} } — add it to select()`,\n );\n}\n\nasync function loadController(\n source: WoodConfigSource,\n importer: ModuleImporter,\n reference: string,\n owner: 'main' | 'renderer',\n): Promise<ControllerClass> {\n const file = owner === 'main'\n ? source.mainControllerFile(reference)\n : source.rendererControllerFile(reference);\n const controller = await importDefaultExport<ControllerClass>(\n importer,\n file,\n `${owner} controller \"${reference}\"`,\n );\n if (typeof controller !== 'function') {\n throw new TestWoodError(`${owner} controller \"${reference}\" must default-export a class (${file})`);\n }\n return controller;\n}\n\nasync function loadMainControllers(\n source: WoodConfigSource,\n operations: ReadonlyMap<string, OperationDef>,\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n for (const operation of operations.values()) {\n if (!controllers.has(operation.controller)) {\n controllers.set(operation.controller, await loadController(source, importer, operation.controller, 'main'));\n }\n }\n return controllers;\n}\n\nasync function loadRendererControllers(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, ControllerClass>> {\n const controllers = new Map<string, ControllerClass>();\n const references = new Set<string>();\n for (const view of views) {\n if (view.controller) references.add(view.controller);\n for (const controller of view.layoutControllers ?? []) if (controller) references.add(controller);\n }\n for (const reference of references) {\n controllers.set(reference, await loadController(source, importer, reference, 'renderer'));\n }\n return controllers;\n}\n\nasync function runCompose(\n root: IContainer,\n source: WoodConfigSource,\n context: WoodComposeContext,\n): Promise<WoodComposeResult | null> {\n const composePath = source.hookModule(source.config.main.testing?.compose);\n if (!composePath) return null;\n const compose = await importDefaultExport<WoodComposeMain>(context.importer, composePath, 'main.testing.compose');\n if (typeof compose !== 'function') {\n throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);\n }\n return (await ExecutionContext.run(root as never, () => compose(root, context))) ?? null;\n}\n\n/**\n * Dispose the main root under its own execution context (service teardown\n * hooks resolve collaborators such as tracers exactly like any other call),\n * naming every instance whose dispose hook failed.\n */\nasync function disposeRoot(env: TestEnvironment): Promise<void> {\n try {\n await ExecutionContext.run(env.root as never, () => env.dispose());\n } catch (error) {\n const failures = (error as { failures?: Array<{ token: string; error: unknown }> }).failures;\n if (!Array.isArray(failures)) throw error;\n const detail = failures\n .map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`)\n .join('\\n ');\n throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):\\n ${detail}`);\n }\n}\n\nfunction assertNoGlobalContainerLeak(before: number, source: WoodConfigSource): void {\n if (!process.env.VITEST) return;\n const after = globalRegistrySize();\n if (after > before) {\n throw new TestWoodError(\n `${source.config.main.testing?.compose ?? 'the build'} registered ${after - before} process-global ` +\n 'Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.',\n );\n }\n}\n\ntype SelectedPageCatalog = NonNullable<ReturnType<typeof selectRendererCatalog>>;\ntype ControllerScope = { dispose(): Promise<void> };\n\n/** Per-build executable seams (`.middleware` on main, `.load` on renderer). */\ninterface AssemblyExecutables {\n readonly main: TestExecutableOverlays;\n readonly renderer: TestExecutableOverlays;\n /** Middleware identities the test replaced (only these get a registry executable). */\n readonly middleware: ReadonlySet<string>;\n}\n\ninterface AssemblyLiveState {\n composed: WoodComposeResult | null;\n env: TestEnvironment | null;\n renderer: RendererTier | null;\n router: WoodRouter | null;\n /** Set once overlays are resolved (before the router is constructed on first use). */\n executables: AssemblyExecutables | null;\n /** Set once the environment's runtime stages exist (after build). */\n runtime: AssemblyRuntime | null;\n /** dispose() was requested; a runtime that has not started must not start. */\n disposed: boolean;\n /** A runtime stage already rolled the environment back; never roll back twice. */\n rolledBack: boolean;\n}\n\n/** The managed main runtime: constructed router + `compose.start()` done. */\ninterface MainRuntime {\n readonly router: WoodRouter;\n}\n\n/**\n * Lazily initialized runtime stages. `main` constructs the managed router\n * and runs `compose.start()`; `renderer` (pages only) builds the renderer\n * tier over the main router. Each initializes exactly once on first use.\n */\ninterface AssemblyRuntime {\n readonly main: LazyRuntimeStage<MainRuntime>;\n readonly renderer: LazyRuntimeStage<RendererTier> | null;\n}\n\ninterface AssemblyInputs {\n readonly network: TestFetchRuntime;\n readonly state: BuilderState;\n readonly selection: ResolvedWoodSelection;\n readonly source: WoodConfigSource;\n readonly operations: Map<string, OperationDef>;\n readonly catalog: SelectedPageCatalog | null;\n readonly registryBefore: number;\n readonly mainRegistrations: RegistrationOnlyRoot;\n readonly rendererRegistrations: RegistrationOnlyRoot | null;\n readonly controllerScopes: ControllerScope[];\n readonly live: AssemblyLiveState;\n readonly context: WoodComposeContext;\n readonly dispose: () => Promise<void>;\n}\n\nfunction createComposeContext(\n source: WoodConfigSource,\n state: BuilderState,\n selection: ResolvedWoodSelection,\n operations: Map<string, OperationDef>,\n live: AssemblyLiveState,\n): WoodComposeContext {\n return {\n events: new RouterBridgeEvents(),\n configPath: source.configPath,\n configDir: source.configDir,\n config: source.config,\n selection,\n operations,\n importer: state.importer,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!live.env || (!live.router && !live.runtime)) {\n throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n }\n // Inside start() the router already exists (it is constructed before\n // start runs); after build, the first call initializes the runtime.\n const router = live.router ?? (await live.runtime!.main.acquire()).router;\n return (await ExecutionContext.run(\n live.env.root as never,\n () => router.dispatch(operation, input),\n )) as T;\n },\n };\n}\n\nfunction createAssemblyDisposer(\n live: AssemblyLiveState,\n mainRegistrations: RegistrationOnlyRoot,\n rendererRegistrations: RegistrationOnlyRoot | null,\n controllerScopes: ControllerScope[],\n network: TestFetchRuntime,\n): () => Promise<void> {\n let closing: Promise<void> | null = null;\n return (): Promise<void> => {\n live.disposed = true;\n return closing ??= cleanupAll('testWood cleanup failed', [\n // An initialization racing this dispose finishes first (it refuses to\n // complete once `disposed` is set) so every resource it created late is\n // released below. A rollback-triggered dispose skips this: its own\n // stage has already finished and waiting would be a self-await.\n ['runtime settle', async () => {\n if (live.rolledBack || !live.runtime) return;\n await live.runtime.main.settled();\n await live.runtime.renderer?.settled();\n }],\n ['renderer', async () => { if (live.renderer) await live.renderer.dispose(); }],\n ...[...controllerScopes].reverse().map(\n (scope, index) => [`controller scope ${controllerScopes.length - index}`, () => scope.dispose()] as const,\n ),\n ['compose drain', async () => {\n const root = live.env?.root ?? mainRegistrations.actualRoot;\n if (!root.isDisposed()) await ExecutionContext.run(root as never, () => live.composed?.drain?.());\n }],\n ['main root', async () => {\n if (live.env) await disposeRoot(live.env);\n else await mainRegistrations.actualRoot.dispose();\n }],\n ['unbuilt renderer root', async () => {\n if (!live.renderer && rendererRegistrations) await rendererRegistrations.actualRoot.dispose();\n }],\n ['compose external dispose', async () => { await live.composed?.dispose?.(); }],\n ['external fetch', () => network.dispose()],\n ]);\n };\n}\n\nasync function loadAssemblyControllers(\n inputs: AssemblyInputs,\n): Promise<{\n main: Map<string, ControllerClass>;\n renderer: Map<string, ControllerClass>;\n loaders: Map<string, DataLoader>;\n}> {\n const main = await loadMainControllers(inputs.source, inputs.operations, inputs.state.importer);\n const renderer = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererControllers(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, ControllerClass>();\n const loaders = inputs.catalog && !inputs.state.browserHost\n ? await loadRendererLoaders(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, DataLoader>();\n return { main, renderer, loaders };\n}\n\nfunction installAssemblyRuntime(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n installWoodRuntime(inputs.mainRegistrations.root, {\n controllersDir: inputs.source.mainControllersDir,\n middlewareDir: inputs.source.mainMiddlewareDir,\n mode: 'dev',\n registerRuntime: (configuredRouter) => {\n for (const [identity, controller] of mainControllers) {\n configuredRouter.registerControllerLoader(identity, () => Promise.resolve({ default: controller }));\n }\n configuredRouter.loadOperations(inputs.operations);\n registerMiddlewareSeams(inputs, configuredRouter);\n },\n });\n}\n\n/**\n * `.middleware(identity, …)`: the production identity keeps its membership,\n * order and inheritance (they come from the operations source); only the\n * executable satisfying it is replaced, through the router's registry seam.\n * The source executable is resolved lazily from `main.middleware`, exactly\n * as production would, and handed to the shared test.* runtime once.\n */\nfunction registerMiddlewareSeams(inputs: AssemblyInputs, configuredRouter: WoodRouter): void {\n const executables = inputs.live.executables;\n if (!executables || !executables.middleware.size) return;\n const sourceResolver = new MiddlewareResolver(inputs.source.mainMiddlewareDir);\n for (const spec of executables.middleware) {\n configuredRouter.registerMiddleware(spec, async (context: WoodMiddlewareContext) => {\n const original = await sourceResolver.resolve(spec);\n return executables.main.replace('middleware', spec, original as ReplacementFunction)(context);\n });\n }\n}\n\n/** Production middleware identities the selected operations run, in source order. */\nfunction selectedMiddleware(operations: ReadonlyMap<string, OperationDef>): string[] {\n const identities = new Set<string>();\n for (const operation of operations.values()) for (const spec of operation.middleware) identities.add(spec);\n return [...identities];\n}\n\n/**\n * Discover and import every companion data loader (`pages/chat.svelte` →\n * `pages/chat.load.ts`) of the selected pages/layouts. Discovery and import\n * happen at build (a missing default loader function fails the build); no\n * loader RUNS until a page opens. The identity is the config-relative\n * source path with export `default` — the `.load` seam identity.\n */\nasync function loadRendererLoaders(\n source: WoodConfigSource,\n views: readonly ViewDef[],\n importer: ModuleImporter,\n): Promise<Map<string, DataLoader>> {\n const loaders = new Map<string, DataLoader>();\n for (const view of views) {\n for (const viewPath of [...view.layouts, view.viewPath]) {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference || loaders.has(reference.identity)) continue;\n const mod = (await importer(reference.file)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new TestWoodError(`data loader ${reference.identity} (${reference.file}) has no default export`);\n }\n try {\n loaders.set(reference.identity, assertDataLoader(mod.default, `data loader ${reference.identity}`));\n } catch (error) {\n throw new TestWoodError(error instanceof Error ? error.message : String(error));\n }\n }\n }\n return loaders;\n}\n\n/** The native renderer loader identities: every discovered companion loader source. */\nfunction rendererLoaderReferences(loaders: ReadonlyMap<string, DataLoader>): LoaderReference[] {\n return [...loaders.keys()].map((identity) => Object.freeze({ path: identity, export: 'default' }));\n}\n\nasync function configureRendererRegistrations(inputs: AssemblyInputs): Promise<void> {\n const registrations = inputs.rendererRegistrations;\n if (!registrations) return;\n // Bound by the renderer tier on first use (see buildRendererTier); declared\n // here so the shared overlay catalog can resolve replacements for them.\n registrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n registrations.declareBinding(Navigation, 'singleton');\n registrations.declareBinding(WOOD_FRONTEND_BRIDGE, 'singleton');\n const composePath = inputs.source.hookModule(inputs.source.config.renderer.compose);\n if (composePath) {\n const composeRenderer = await importDefaultExport<(root: IContainer) => void | Promise<void>>(\n inputs.state.importer,\n composePath,\n 'renderer.compose',\n );\n if (typeof composeRenderer !== 'function') {\n throw new TestWoodError(`renderer.compose default export must be a function (${composePath})`);\n }\n await ExecutionContext.run(registrations.root as never, () => composeRenderer(registrations.root));\n }\n await ExecutionContext.run(\n registrations.root as never,\n () => inputs.live.composed?.configureRendererRoot?.(registrations.root),\n );\n}\n\nfunction registerControllersBeforeOverlays(\n inputs: AssemblyInputs,\n mainControllers: ReadonlyMap<string, ControllerClass>,\n rendererControllers: ReadonlyMap<string, ControllerClass>,\n): void {\n for (const controller of mainControllers.values()) {\n inputs.mainRegistrations.declare(controller);\n if (!inputs.mainRegistrations.actualRoot.isRegistered(controller)) {\n inputs.mainRegistrations.root.registerClass(controller);\n }\n }\n for (const controller of rendererControllers.values()) {\n inputs.rendererRegistrations?.declare(controller);\n }\n}\n\ninterface AssemblyIoc {\n readonly main: TestIocBuilder;\n readonly mainBorrowed: readonly unknown[];\n readonly renderer: TestIocBuilder;\n readonly rendererBorrowed: readonly unknown[];\n readonly overlays: readonly ResolvedTestOverlay[];\n readonly executables: AssemblyExecutables;\n}\n\n/**\n * Legacy plural calls are routed by owner token sets (unchanged); the\n * canonical singular replacements are resolved by the shared overlay engine\n * against each owner's DECLARED binding catalog — production registrations\n * recorded on the root, legacy explicit composition, auto-bound controllers,\n * the selected middleware identities and the page/layout loader identities.\n */\nfunction buildAssemblyIoc(\n inputs: AssemblyInputs,\n controllers: {\n main: ReadonlyMap<string, ControllerClass>;\n renderer: ReadonlyMap<string, ControllerClass>;\n loaders: ReadonlyMap<string, DataLoader>;\n },\n): AssemblyIoc {\n const catalogs: TestOwnerCatalog[] = [{\n owner: 'main',\n bindings: describeTestBindings(testIoc().use(inputs.mainRegistrations.module()), [...controllers.main.values()]),\n middleware: selectedMiddleware(inputs.operations),\n loaders: [],\n }];\n if (inputs.rendererRegistrations && inputs.catalog) {\n catalogs.push({\n owner: 'renderer',\n bindings: describeTestBindings(testIoc().use(inputs.rendererRegistrations.module()), [...controllers.renderer.values()]),\n middleware: [],\n loaders: rendererLoaderReferences(controllers.loaders),\n });\n }\n const overlays = resolveTestOverlays(inputs.state.overlays, catalogs);\n const mainPlan = applyTestBindings(testIoc(), overlays, 'main');\n const rendererPlan = applyTestBindings(testIoc(), overlays, 'renderer');\n return {\n main: mainPlan.builder,\n mainBorrowed: mainPlan.buildOptions.borrowedTokens ?? [],\n renderer: rendererPlan.builder,\n rendererBorrowed: rendererPlan.buildOptions.borrowedTokens ?? [],\n overlays,\n executables: {\n main: createTestExecutables(overlays, 'main'),\n renderer: createTestExecutables(overlays, 'renderer'),\n middleware: new Set(overlays\n .filter((overlay) => overlay.owner === 'main' && overlay.entry.kind === 'middleware')\n .map((overlay) => overlay.entry.target as string)),\n },\n };\n}\n\n/**\n * Roll the whole environment back after a failed first use: every resource a\n * partially initialized runtime created is released, and both the primary\n * error and any cleanup failures are preserved. Runs at most once.\n */\nfunction createRuntimeRollback(inputs: AssemblyInputs): (error: unknown) => Promise<never> {\n return (error) => {\n // Already rolled back, or dispose() is in flight / done: that disposer\n // owns the cleanup (it waits for this attempt), so never re-enter it here.\n if (inputs.live.rolledBack || inputs.live.disposed) return Promise.reject(error);\n inputs.live.rolledBack = true;\n return rollbackEnvironment(error, inputs.dispose, 'testWood runtime initialization failed and rollback failed');\n };\n}\n\nfunction assertRuntimeMayStart(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed before its ${stage} runtime was used`);\n }\n}\n\n/**\n * Re-checked after every await of a first initialization: dispose() may have\n * been requested meanwhile. The disposer waits for this attempt, so failing\n * here hands every late resource to it instead of exposing a disposed root.\n */\nfunction assertRuntimeMayContinue(inputs: AssemblyInputs, stage: string): void {\n if (inputs.live.disposed) {\n throw new TestWoodError(`this testWood environment was disposed while its ${stage} runtime was initializing`);\n }\n}\n\n/** First use: construct the managed router, then run `compose.start()` on the seamed graph. */\nasync function initializeMainRuntime(inputs: AssemblyInputs): Promise<MainRuntime> {\n assertRuntimeMayStart(inputs, 'main');\n const root = inputs.live.env!.root;\n const router = await Promise.resolve(\n ExecutionContext.run(root as never, () => root.get(WoodRouter)),\n ) as WoodRouter;\n inputs.live.router = router;\n assertRuntimeMayContinue(inputs, 'main');\n await ExecutionContext.run(root as never, () => inputs.live.composed?.start?.());\n assertRuntimeMayContinue(inputs, 'main');\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n return { router };\n}\n\ninterface RendererRuntimeInputs {\n readonly ioc: TestIocBuilder;\n readonly borrowedTokens: readonly unknown[];\n readonly controllerClasses: ReadonlyMap<string, ControllerClass>;\n readonly events: RouterBridgeEvents;\n readonly loaders: TestExecutableOverlays;\n /** Imported companion data loaders by `.load` identity (none has run). */\n readonly loaderModules: ReadonlyMap<string, DataLoader>;\n}\n\n/** First renderer use: the main runtime, then the renderer tier over its router. */\nasync function initializeRendererRuntime(\n inputs: AssemblyInputs,\n main: LazyRuntimeStage<MainRuntime>,\n renderer: RendererRuntimeInputs,\n): Promise<RendererTier> {\n const { router } = await main.acquire();\n assertRuntimeMayStart(inputs, 'renderer');\n const composed = inputs.live.composed;\n const configurePageScope = composed?.configurePageScope?.bind(composed);\n const hooks: WoodRendererHooks = {\n events: renderer.events,\n ...(configurePageScope ? { configurePageScope } : {}),\n };\n const tier = await buildRendererTier({\n source: inputs.source,\n router,\n catalog: inputs.catalog!,\n operations: inputs.operations,\n importer: inputs.state.importer,\n hooks,\n dom: null,\n ioc: renderer.ioc,\n borrowedTokens: renderer.borrowedTokens,\n registrations: inputs.rendererRegistrations!,\n controllerClasses: renderer.controllerClasses,\n loaders: renderer.loaders,\n loaderModules: renderer.loaderModules,\n });\n // Hand the tier to the disposer before refusing: a dispose() that raced\n // this build releases it through the regular 'renderer' cleanup step.\n inputs.live.renderer = tier;\n assertRuntimeMayContinue(inputs, 'renderer');\n return tier;\n}\n\nfunction createAssemblyRuntime(\n inputs: AssemblyInputs,\n renderer: RendererRuntimeInputs | null,\n): AssemblyRuntime {\n const rollback = createRuntimeRollback(inputs);\n const main = new LazyRuntimeStage<MainRuntime>(() => initializeMainRuntime(inputs), rollback);\n const rendererStage = renderer\n ? new LazyRuntimeStage<RendererTier>(() => initializeRendererRuntime(inputs, main, renderer), rollback)\n : null;\n return { main, renderer: rendererStage };\n}\n\n/**\n * Build: read the source, record registrations (compose, runtime module,\n * renderer hooks, controllers) and install the test overlays on the main root.\n * Nothing is resolved or started here — the managed router, `compose.start()`\n * and the renderer tier initialize on first explicit use.\n */\nasync function materializeEnvironment(inputs: AssemblyInputs): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n inputs.mainRegistrations.root.registerFunction(FETCH_RUNTIME, () => inputs.network, {owned: false, loadAs: LoadAs.Singleton});\n const controllers = await loadAssemblyControllers(inputs);\n inputs.live.composed = await runCompose(inputs.mainRegistrations.root, inputs.source, inputs.context);\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n installAssemblyRuntime(inputs, controllers.main);\n await configureRendererRegistrations(inputs);\n registerControllersBeforeOverlays(inputs, controllers.main, controllers.renderer);\n const ioc = buildAssemblyIoc(inputs, controllers);\n inputs.live.executables = ioc.executables;\n\n inputs.live.env = await buildRecordedEnvironment(ioc.main, inputs.mainRegistrations, 'main', ioc.mainBorrowed);\n const root = inputs.live.env.root;\n\n const renderer: RendererRuntimeInputs | null = inputs.catalog && inputs.rendererRegistrations\n ? {\n ioc: ioc.renderer,\n borrowedTokens: ioc.rendererBorrowed,\n controllerClasses: controllers.renderer,\n events: inputs.live.composed?.events ?? inputs.context.events,\n loaders: ioc.executables.renderer,\n loaderModules: controllers.loaders,\n }\n : null;\n const runtime = createAssemblyRuntime(inputs, renderer);\n inputs.live.runtime = runtime;\n\n const environment = environmentSurface({\n root,\n network: inputs.network,\n env: inputs.live.env,\n operations: inputs.operations,\n runtime,\n allOperations: inputs.source.operations,\n events: renderer?.events ?? inputs.live.composed?.events ?? inputs.context.events,\n dom: false,\n controllerScopes: inputs.controllerScopes,\n executables: ioc.executables,\n dispose: inputs.dispose,\n });\n if (inputs.state.browserHost) {\n const { createNativeBrowserEnvironment } = await import('./native_browser/native_browser_environment.js');\n return createNativeBrowserEnvironment({\n source: inputs.source,\n views: inputs.catalog!.all(),\n environment,\n network: inputs.network,\n events: () => inputs.live.composed?.events ?? inputs.context.events,\n }, inputs.state.browserHost);\n }\n return environment;\n}\n\nasync function rollbackEnvironment(\n error: unknown,\n dispose: () => Promise<void>,\n message = 'testWood build failed and rollback failed',\n): Promise<never> {\n try {\n await dispose();\n } catch (cleanupError) {\n const failures = [\n ...(error instanceof TestWoodCleanupError ? error.failures : []),\n ...(cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'build rollback', error: cleanupError }]),\n ];\n throw new TestWoodCleanupError(message, failures, error);\n }\n throw error;\n}\n\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selected: ResolvedWoodSelection | null,\n): Promise<TestWoodEnvironment | NativeWoodTestEnvironment<WoodBrowserHost>> {\n const source = loadWoodConfigSource(state.configPath);\n // No selection: every owner the config declares, whole ({}). A Wood\n // config declares both `main` and `renderer` sections.\n let selection = selected ?? resolveSelection(\n Object.fromEntries(DECLARED_OWNERS.filter((owner) => source.config[owner]).map((owner) => [owner, {}])),\n );\n // An omitted main owner exposes no operations; a selected renderer still gets\n // a main root for dependencies but does not widen the main operation aperture.\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selectRendererCatalog(source.views, selection);\n if (state.browserHost && !catalog) {\n throw new TestWoodError('browser() requires a selected renderer owner; add renderer to select()');\n }\n if (state.browserHost && stubEntries(state.overlays).some(entry => entry.kind === 'load' || entry.owner === 'renderer')) {\n throw new TestWoodError('Native browser renderer replacements are not supported yet: renderer code executes in its own realm. Keep main-process seams, or use the existing in-process renderer test environment for renderer .class/.method/.value/.load seams.');\n }\n const network = createTestFetch(state.fetchConfigurations.flatMap(defineFetchRoutes));\n const registryBefore = globalRegistrySize();\n const mainRegistrations = new RegistrationOnlyRoot('main.testing.compose', source.configPath);\n const rendererRegistrations = catalog && !state.browserHost\n ? new RegistrationOnlyRoot('configureRendererRoot', source.configPath)\n : null;\n const controllerScopes: ControllerScope[] = [];\n const live: AssemblyLiveState = {\n composed: null,\n env: null,\n renderer: null,\n router: null,\n executables: null,\n runtime: null,\n disposed: false,\n rolledBack: false,\n };\n const context = createComposeContext(source, state, selection, operations, live);\n const dispose = createAssemblyDisposer(\n live,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n network,\n );\n const inputs: AssemblyInputs = {\n network,\n state,\n selection,\n source,\n operations,\n catalog,\n registryBefore,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n live,\n context,\n dispose,\n };\n\n try {\n return await materializeEnvironment(inputs);\n } catch (error) {\n return rollbackEnvironment(error, dispose);\n }\n}\n\ninterface SurfaceParts {\n network: TestFetchRuntime;\n root: IContainer;\n env: TestEnvironment;\n operations: Map<string, OperationDef>;\n runtime: AssemblyRuntime;\n /** Every production operation, used to keep `api` object shape stable across selections. */\n allOperations: ReadonlyMap<string, OperationDef>;\n /** Backend push emitter (pages only); available from build, before any renderer use. */\n events: RouterBridgeEvents | null;\n dom: boolean;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n executables: AssemblyExecutables;\n dispose(): Promise<void>;\n}\n\nconst OPEN_FIRST = 'await open(page) (or another renderer call) first';\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, operations, allOperations, runtime, events, dom: domSelected, controllerScopes, executables, dispose } = parts;\n const pages = (): LazyRuntimeStage<RendererTier> => runtime.renderer ?? tierMissing('pages', 'renderer');\n const dom = async (): Promise<WoodDomDriver> => {\n if (!domSelected) tierMissing('dom', 'dom driver');\n return (await pages().acquire()).dom!;\n };\n\n // Stable lazy members: async use initializes the runtime; synchronous\n // members are served once it is live and fail with guidance before that.\n const router = lazyMember<WoodRouter>(\n 'router',\n runtime.main.map((main) => main.router),\n ['dispatch'],\n 'await call()/get()/controller() first',\n );\n const frontend = runtime.renderer\n ? lazyMember<FrontendApplicationSurface>(\n 'frontend',\n runtime.renderer.map((tier) => tier.frontend),\n ['open', 'act', 'settle', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n const rendererRoot = runtime.renderer\n ? lazyMember<IContainer>(\n 'rendererRoot',\n runtime.renderer.map((tier) => tier.rendererRoot),\n ['get', 'dispose'],\n OPEN_FIRST,\n )\n : null;\n\n const dispatchOperation = async (operation: string, input?: unknown): Promise<unknown> => {\n if (!operations.has(operation)) {\n throw new TestWoodError(\n `operation \"${operation}\" is not in the selected aperture; available operations:\\n ${[...operations.keys()].join('\\n ')}`,\n );\n }\n const { router: live } = await runtime.main.acquire();\n return await ExecutionContext.run(root as never, () => live.dispatch(operation, input));\n };\n const api = createOperationApi(allOperations.keys(), dispatchOperation) as WoodTestApi;\n\n return {\n root,\n router,\n operations,\n api,\n rawApi: api as WoodTestRawApi,\n call: dispatchOperation as TestWoodEnvironment['call'],\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n await runtime.main.acquire();\n const scope = root.extend();\n const ownsController = !root.isRegistered(controller);\n let instance: T | undefined;\n controllerScopes.push({ dispose: () => ExecutionContext.run(scope as never, () => cleanupAll('controller cleanup failed', [\n ['controller', async () => { if (ownsController) await disposeControllerLifecycle(instance); }],\n ['scope', () => scope.dispose()],\n ])) as Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n instance = await ExecutionContext.run(scope as never, () => scope.get(controller)) as T;\n guardControllerLifecycleOnce(instance);\n return instance;\n },\n get: async <T>(token: unknown): Promise<T> => {\n await runtime.main.acquire();\n return (await ExecutionContext.run(root as never, () => root.get(token))) as T;\n },\n\n get frontend() { return frontend ?? tierMissing('pages', 'renderer'); },\n get events() { return events ?? tierMissing('pages', 'renderer'); },\n get rendererRoot() { return rendererRoot ?? tierMissing('pages', 'renderer'); },\n open: async (page, params, query) => (await pages().acquire()).open(page, params, query),\n navigate: async (page, params, query) => (await pages().acquire()).navigate(page, params, query),\n current: () => pages().current?.current() ?? null,\n settle: async () => {\n await parts.network.settle();\n // Finalization applies to main-only roots too. Waiting must never start\n // an unused product or renderer merely to discover that it is idle.\n if (runtime.main.requested) await (await runtime.main.acquire()).router.drain();\n if (runtime.renderer?.requested) await (await runtime.renderer.acquire()).settle();\n },\n\n get target() {\n if (!domSelected) tierMissing('dom', 'dom driver');\n const tier = pages().current;\n if (!tier) throw new TestWoodError('no page is mounted; open(page) first');\n return tier.dom!.target;\n },\n click: async (selector) => (await dom()).click(selector),\n type: async (selector, text) => (await dom()).type(selector, text),\n submit: async (selector) => (await dom()).submit(selector),\n text: async (selector) => (await dom()).text(selector),\n exists: async (selector) => (await dom()).exists(selector),\n\n // Method expectations only: `settle()` is the completion contract for\n // owned renderer work, and a test may legitimately verify while a\n // deliberately deferred stream is still in flight.\n verify: async () => {\n const failures: Array<{ owner: string; error: unknown }> = [];\n await env.verify().catch((error) => failures.push({ owner: 'main', error }));\n await executables.main.verify().catch((error) => failures.push({ owner: 'main middleware', error }));\n const renderer = runtime.renderer?.current ?? null;\n if (renderer) {\n await renderer.verify().catch((error) => failures.push({ owner: 'renderer', error }));\n }\n await executables.renderer.verify().catch((error) => failures.push({ owner: 'renderer loaders', error }));\n if (failures.length === 1) throw failures[0]!.error;\n if (failures.length) {\n throw new TestWoodError(\n 'testWood verification failed in several owners:\\n ' +\n failures.map(({ owner, error }) =>\n `${owner}: ${error instanceof Error ? error.message : String(error)}`).join('\\n '),\n );\n }\n },\n dispose,\n // One inspection surface for both owners: a renderer-owned token's watched\n // method is readable through the same environment handle as a main one.\n // The main registry is consulted first; the renderer registry only once the\n // renderer runtime has actually been initialized.\n [ENV_REGISTRY]: {\n inspect(token: unknown, method: string) {\n const main = (env as unknown as Record<symbol, { inspect(token: unknown, method: string): unknown }>)[ENV_REGISTRY];\n const renderer = (runtime.renderer?.current as unknown as Record<symbol, { inspect(token: unknown, method: string): unknown }> | null | undefined)?.[ENV_REGISTRY];\n try {\n return main.inspect(token, method);\n } catch (error) {\n if (!renderer) throw error;\n try {\n return renderer.inspect(token, method);\n } catch {\n throw error;\n }\n }\n },\n },\n };\n}\n\nexport class TestWoodBuilder<H extends WoodBrowserHost | null = null> {\n private constructor(private readonly state: BuilderState) {}\n\n static create(configPath: string): TestWoodBuilder {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n return new TestWoodBuilder({\n configPath: path.resolve(configPath),\n selection: null,\n browserHost: null,\n fetchConfigurations: [],\n importer: defaultImporter,\n overlays: testStub(),\n });\n }\n\n fetch(configure: ConfigureFetch): TestWoodBuilder<H> {\n if (typeof configure !== 'function') throw new TypeError('fetch() requires a route configuration callback');\n return this.derive({fetchConfigurations: Object.freeze([...this.state.fetchConfigurations, configure])});\n }\n\n browser(): TestWoodBuilder<'playwright'>;\n browser(host: 'playwright'): TestWoodBuilder<'playwright'>;\n browser(host: 'happy_dom'): TestWoodBuilder<'happy_dom'>;\n browser(host: WoodBrowserHost = 'playwright'): TestWoodBuilder<WoodBrowserHost> {\n if (host !== 'playwright' && host !== 'happy_dom') throw new TestWoodError('browser() accepts playwright or happy_dom');\n return new TestWoodBuilder<WoodBrowserHost>({...this.state, browserHost: host});\n }\n\n /** Declare selected tiers, snapshotting the source selector at call time. */\n select(selection: WoodTestSelection): TestWoodBuilder<H> {\n const resolved = resolveSelection(selection);\n return this.derive({ selection: Object.freeze({\n ...(resolved.main !== null ? { main: resolved.main } : {}),\n ...(resolved.renderer !== null ? { renderer: resolved.renderer } : {}),\n }) });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder<H> {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /** Compose a reusable canonical testStub() replacement description. */\n use(preset: TestStub): TestWoodBuilder<H> {\n if (!(preset instanceof TestStub)) throw new TestWoodError('use() requires testStub()');\n return this.derive({ overlays: this.state.overlays.use(preset) });\n }\n\n /* -- canonical singular replacements (shared @noego/testing engine) ------ */\n\n /** Replace the class bound to an exact declared token; the declared lifetime is kept. */\n class<T>(target: ClassToken<T>, replacement: ReplacementClass<T>, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.class(target, replacement, options) });\n }\n\n /** Replace a declared factory/provider (dependencies and lifetime kept) or a callable value. */\n function(target: unknown, replacement: ReplacementFunction, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.function(target, replacement, options) });\n }\n\n /** A test.* method seam on the FINAL binding of an exact declared token. */\n method(target: unknown, method: string, replacement: MethodDescriptor, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.method(target, method, replacement, options) });\n }\n\n /** Provide a caller-owned value for an exact declared token (never disposed by the app). */\n value(target: unknown, replacement: unknown, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.value(target, replacement, options) });\n }\n\n /**\n * Replace the executable satisfying a production middleware identity\n * (`file.path:selector` as written in the operations source). Membership,\n * order and inheritance always come from the source.\n */\n middleware(identity: string, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.middleware(identity, replacement, options) });\n }\n\n /**\n * Replace the DATA LOADER behind a selected page/layout: the companion\n * `.load` source of a view (`pages/chat.svelte` → `pages/chat.load.ts`).\n * The identity is the config-relative source path (`ui/pages/chat.load.ts`),\n * export `default`; the executable receives the loader `RequestData`\n * (`url` = page identity, `params`, `query`) and yields the data the\n * controller's `initialize({ params, query, data })` receives. A source\n * with no companion loader is not a declared identity (build error).\n * Controllers are replaced with `.class`, never here.\n */\n load(identity: LoaderIdentity, replacement: ExecutableReplacement, options?: ReplacementOptions): TestWoodBuilder<H> {\n return this.derive({ overlays: this.state.overlays.load(identity, replacement, options) });\n }\n\n /** Build; without `select()`, every owner the config declares is selected whole. */\n async build(): Promise<WoodTestEnvironmentFor<H>> {\n return await assembleEnvironment(this.state, this.state.selection ? resolveSelection(this.state.selection) : null) as WoodTestEnvironmentFor<H>;\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder<H> {\n return new TestWoodBuilder({ ...this.state, ...patch });\n }\n}\n\nexport function testWood(configPath: string): TestWoodBuilder {\n return TestWoodBuilder.create(configPath);\n}\n"],"mappings":"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","names":[]}
@@ -23,6 +23,7 @@ __export(wood_env_renderer_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(wood_env_renderer_exports);
25
25
  var import_ioc = require("@noego/ioc");
26
+ var import_testing = require("@noego/testing");
26
27
  var import_in_process_router_transport = require("../transport/in_process_router_transport.js");
27
28
  var import_build_transport_bridge = require("../transport/build_transport_bridge.js");
28
29
  var import_create_router_bridge = require("./create_router_bridge.js");
@@ -273,7 +274,8 @@ async function buildRendererTier(options) {
273
274
  pendingWork: () => registry.pending(),
274
275
  verify: () => rendererEnv.verify(),
275
276
  dom: keeper ? domDriver(keeper) : null,
276
- dispose
277
+ dispose,
278
+ [import_testing.ENV_REGISTRY]: rendererEnv[import_testing.ENV_REGISTRY]
277
279
  };
278
280
  }
279
281
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/wood_env_renderer.ts"],"sourcesContent":["/**\n * The renderer tier of a `testWood(configPath)` environment: a fresh renderer\n * root (mirroring the production process split), an environment-local\n * Navigation, the generated bridge client over an in-process transport into\n * the main router, and the frontend application surface over the selected\n * page catalog. With a DOM mode, the page's real component is mounted by the\n * app's host module and driven through a selector-based surface.\n */\nimport type { IContainer } from '@noego/ioc';\nimport { ExecutionContext, LoadAs } from '@noego/ioc';\nimport type { TestExecutableOverlays, TestIocBuilder } from '@noego/testing';\nimport type { WoodRouter } from '../router/wood_router.cjs';\nimport { InProcessRouterTransport } from '../transport/in_process_router_transport.js';\nimport { buildTransportBridge } from '../transport/build_transport_bridge.js';\nimport { RouterBridgeEvents } from './create_router_bridge.js';\nimport type { OperationDef, ViewDef } from '../types/index.cjs';\nimport type { DataLoader, ViewLoaderSet } from '../loader/loader_orchestration.js';\nimport { Navigation } from '../navigation/index.js';\nimport type { PageCatalog } from '../frontend/page_catalog.js';\nimport { WorkRegistry } from '../frontend/work_registry.js';\nimport { TrackedTransport } from '../frontend/tracked_transport.js';\nimport {\n createFrontendSurface,\n type FrontendApplicationSurface,\n type PageScopeContext,\n} from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.cjs';\nimport { importDefaultExport, type ModuleImporter, type WoodConfigSource } from './wood_config_source.cjs';\nimport type { WoodDomMode } from './wood_test_selection.cjs';\nimport { leaseDomAnimation, type WoodAnimationLease } from './wood_dom_animation.cjs';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.cjs';\n\n/** What the app's `main.testing.compose` module may hand back for the renderer side. */\nexport interface WoodRendererHooks {\n /** Backend push emitter feeding renderer subscriptions (default: transport-owned). */\n events?: RouterBridgeEvents;\n /** Register app-owned renderer composition on the fresh renderer root. */\n configureRendererRoot?(root: IContainer): void | Promise<void>;\n /** Register per-scope renderer builtins on a new page/layout scope. */\n configurePageScope?(scope: IContainer, view: ViewDef, context: PageScopeContext): void | Promise<void>;\n}\n\n/** A page's real component mounted under a DOM driver (`renderer.testing.host`). */\nexport interface WoodMountedPage {\n readonly target: Element;\n flush?(): void | Promise<void>;\n unmount(): void;\n}\n\nexport type WoodPageHost = (identity: string, page: FrontendPage, target: Element) => WoodMountedPage;\n\nexport interface RendererTierOptions {\n source: WoodConfigSource;\n router: WoodRouter;\n catalog: PageCatalog;\n operations: Map<string, OperationDef>;\n importer: ModuleImporter;\n hooks: WoodRendererHooks | null;\n dom: WoodDomMode | null;\n ioc: TestIocBuilder;\n /** Tokens whose `.value`/callable `.function` replacements stay caller-owned. */\n borrowedTokens?: readonly unknown[];\n registrations: RegistrationOnlyRoot;\n controllerClasses: ReadonlyMap<string, ControllerClass>;\n /**\n * `.load` seams over the page/layout DATA loaders. The native loader\n * identity is the config-relative companion source path\n * (`ui/pages/chat.load.ts`), export `default`.\n */\n loaders?: TestExecutableOverlays;\n /** The imported companion loaders by identity (discovered at build, none has run). */\n loaderModules?: ReadonlyMap<string, DataLoader>;\n}\n\nexport interface WoodDomDriver {\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n}\n\nexport interface RendererTier {\n readonly frontend: FrontendApplicationSurface;\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n navigate(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n pendingWork(): ReturnType<WorkRegistry['pending']>;\n verify(): Promise<void>;\n dom: WoodDomDriver | null;\n dispose(): Promise<void>;\n}\ntype ControllerClass = new (...args: never[]) => unknown;\n\nfunction controllerLoader(\n source: WoodConfigSource,\n importer: ModuleImporter,\n preloaded: ReadonlyMap<string, ControllerClass>,\n) {\n const cache = new Map<string, Promise<ControllerClass>>();\n for (const [identity, controller] of preloaded) cache.set(identity, Promise.resolve(controller));\n return (identity: string): Promise<ControllerClass> => {\n let pending = cache.get(identity);\n if (!pending) {\n pending = importDefaultExport<ControllerClass>(\n importer,\n source.rendererControllerFile(identity),\n `renderer controller \"${identity}\"`,\n );\n cache.set(identity, pending);\n }\n return pending;\n };\n}\n\nasync function loadPageHost(\n source: WoodConfigSource,\n importer: ModuleImporter,\n dom: WoodDomMode,\n): Promise<WoodPageHost> {\n if (dom === 'chromium') {\n throw new TestWoodError(\"select({ dom: 'chromium' }) is not available yet; use 'happy-dom'\");\n }\n if (typeof document === 'undefined') {\n throw new TestWoodError(\n \"select({ dom: 'happy-dom' }) requires a DOM environment (// @vitest-environment happy-dom)\",\n );\n }\n const hostPath = source.hookModule(source.config.renderer.testing?.host);\n if (!hostPath) {\n throw new TestWoodError(\n 'select({ dom }) requires renderer.testing.host in wood.config.yml (a module whose default export mounts a page)',\n );\n }\n return importDefaultExport<WoodPageHost>(importer, hostPath, 'renderer.testing.host');\n}\n\n/**\n * Keeps the mounted component in step with the surface's current page. While\n * a page is mounted it holds a Web Animations lease on the target's realm so\n * Svelte transitions work under happy-dom (see `wood_dom_animation.ts`); the\n * lease is released on unmount, on a failed host mount, and on dispose.\n */\nexport class MountKeeper {\n private mounted: WoodMountedPage | null = null;\n private mountedFor: FrontendPage | null = null;\n private animation: WoodAnimationLease | null = null;\n\n constructor(private readonly host: WoodPageHost) {}\n\n get target(): Element {\n if (!this.mounted) throw new TestWoodError('no page is mounted; open(page) first');\n return this.mounted.target;\n }\n\n /** The Web Animations lease held for the current mount (null when nothing is mounted). */\n get animationLease(): WoodAnimationLease | null {\n return this.animation;\n }\n\n async flush(): Promise<void> {\n await this.mounted?.flush?.();\n }\n\n sync(current: FrontendPage | null): void {\n if (current === this.mountedFor) return;\n this.unmount();\n if (!current) return;\n const target = document.createElement('div');\n document.body.appendChild(target);\n const lease = leaseDomAnimation(target);\n try {\n this.mounted = this.host(current.identity, current, target);\n } catch (error) {\n lease.release();\n target.remove();\n throw error;\n }\n this.animation = lease;\n this.mountedFor = current;\n }\n\n unmount(): void {\n const mounted = this.mounted;\n const lease = this.animation;\n this.mounted = null;\n this.mountedFor = null;\n this.animation = null;\n try {\n mounted?.unmount();\n } finally {\n mounted?.target.remove();\n lease?.release();\n }\n }\n}\n\nfunction domDriver(keeper: MountKeeper): WoodDomDriver {\n const find = (selector: string): Element => {\n const found = keeper.target.querySelector(selector);\n if (!found) throw new TestWoodError(`no element matches \"${selector}\"`);\n return found;\n };\n return {\n get target() {\n return keeper.target;\n },\n async click(selector) {\n (find(selector) as HTMLElement).click();\n await keeper.flush();\n },\n async type(selector, text) {\n const input = find(selector) as HTMLInputElement;\n input.focus();\n input.value = text;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n await keeper.flush();\n },\n async submit(selector) {\n find(selector).dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await keeper.flush();\n },\n async text(selector) {\n return keeper.target.querySelector(selector)?.textContent ?? '';\n },\n async exists(selector) {\n return keeper.target.querySelector(selector) !== null;\n },\n };\n}\n\nexport async function buildRendererTier(options: RendererTierOptions): Promise<RendererTier> {\n const {\n source,\n router,\n catalog,\n operations,\n importer,\n hooks,\n dom,\n ioc,\n borrowedTokens,\n registrations,\n controllerClasses,\n loaders,\n loaderModules,\n } = options;\n const keeper = dom ? new MountKeeper(await loadPageHost(source, importer, dom)) : null;\n\n const registry = new WorkRegistry();\n const defaultNavigation = new Navigation();\n const innerTransport = new InProcessRouterTransport(router, hooks?.events ?? new RouterBridgeEvents());\n let currentOwner: () => string = () => 'application';\n const transport = new TrackedTransport(innerTransport, registry, () => currentOwner());\n const { bridge } = buildTransportBridge(operations, transport);\n\n registrations.root.registerFunction(Navigation, () => defaultNavigation, { loadAs: LoadAs.Singleton });\n registrations.root.registerFunction(WOOD_FRONTEND_BRIDGE, () => bridge, { loadAs: LoadAs.Singleton });\n\n const rendererEnv = await buildRecordedEnvironment(ioc, registrations, 'renderer', borrowedTokens);\n const rendererRoot = rendererEnv.root;\n let navigation: Navigation;\n const loadController = controllerLoader(source, importer, controllerClasses);\n const resolveController = (identity: string): Promise<ControllerClass> => loadController(identity);\n // `.load` seams are installed over the imported companion loaders NOW —\n // before any page resolves — one slot per loader identity. Nothing runs\n // here; a page open/navigation executes them through the shared\n // orchestration exactly as the production renderer does.\n const installedLoaders = new Map<string, DataLoader>();\n for (const [identity, original] of loaderModules ?? []) {\n installedLoaders.set(\n identity,\n loaders ? (loaders.replace('load', { path: identity, export: 'default' }, original) as DataLoader) : original,\n );\n }\n const loaderFor = (viewPath: string): DataLoader | null => {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference) return null;\n const installed = installedLoaders.get(reference.identity);\n if (!installed) {\n throw new TestWoodError(`data loader ${reference.identity} was not imported at build; rebuild the environment`);\n }\n return installed;\n };\n const resolveLoaders = (view: ViewDef): ViewLoaderSet => ({\n layouts: view.layouts.map((layoutPath) => loaderFor(layoutPath)),\n view: loaderFor(view.viewPath),\n });\n let frontend: FrontendApplicationSurface;\n try {\n navigation = await ExecutionContext.run(rendererRoot as never, () => rendererRoot.get(Navigation)) as Navigation;\n frontend = createFrontendSurface({\n catalog,\n rendererRoot: rendererRoot as never,\n registry,\n navigation,\n resolveController,\n resolveLoaders,\n ...(hooks?.configurePageScope\n ? { configurePageScope: (scope, view, context) => hooks.configurePageScope!(scope as IContainer, view, context) }\n : {}),\n });\n } catch (error) {\n try {\n await cleanupAll('renderer build rollback failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer root', () => ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n } catch (cleanupError) {\n const failures = cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'renderer rollback', error: cleanupError }];\n throw new TestWoodCleanupError('renderer build failed and rollback failed', failures, error);\n }\n throw error;\n }\n\n currentOwner = () => {\n const current = frontend.current();\n return current ? `page ${current.identity}` : 'application';\n };\n\n const syncMount = async (): Promise<void> => {\n if (!keeper) return;\n keeper.sync(frontend.current());\n await keeper.flush();\n };\n const settle = async (): Promise<void> => {\n await frontend.settle();\n if (!keeper) return;\n await syncMount();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n await keeper.flush();\n };\n\n let closing: Promise<void> | null = null;\n const dispose = (): Promise<void> => closing ??= cleanupAll('renderer cleanup failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer work drain (before frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer frontend', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.dispose()) as Promise<void>],\n ['renderer work drain (after frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer root', () =>\n ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n\n return {\n frontend,\n events: innerTransport.events,\n rendererRoot,\n async open(page, params, query) {\n const opened = await frontend.open({\n page,\n ...(params ? { params } : {}),\n ...(query ? { query } : {}),\n });\n await syncMount();\n return opened;\n },\n async navigate(page, params, query) {\n if (!frontend.current()) throw new TestWoodError('navigate() needs an open page; call open(page) first');\n await frontend.act(() => navigation.go(page, params, query));\n await syncMount();\n const current = frontend.current();\n if (!current) throw new TestWoodError(`navigate(\"${page}\") left no page open`);\n return current;\n },\n current: () => frontend.current(),\n settle,\n pendingWork: () => registry.pending(),\n verify: () => rendererEnv.verify(),\n dom: keeper ? domDriver(keeper) : null,\n dispose,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/wood_env_renderer.ts"],"sourcesContent":["/**\n * The renderer tier of a `testWood(configPath)` environment: a fresh renderer\n * root (mirroring the production process split), an environment-local\n * Navigation, the generated bridge client over an in-process transport into\n * the main router, and the frontend application surface over the selected\n * page catalog. With a DOM mode, the page's real component is mounted by the\n * app's host module and driven through a selector-based surface.\n */\nimport type { IContainer } from '@noego/ioc';\nimport { ExecutionContext, LoadAs } from '@noego/ioc';\nimport { ENV_REGISTRY, type TestExecutableOverlays, type TestIocBuilder } from '@noego/testing';\nimport type { WoodRouter } from '../router/wood_router.cjs';\nimport { InProcessRouterTransport } from '../transport/in_process_router_transport.js';\nimport { buildTransportBridge } from '../transport/build_transport_bridge.js';\nimport { RouterBridgeEvents } from './create_router_bridge.js';\nimport type { OperationDef, ViewDef } from '../types/index.cjs';\nimport type { DataLoader, ViewLoaderSet } from '../loader/loader_orchestration.js';\nimport { Navigation } from '../navigation/index.js';\nimport type { PageCatalog } from '../frontend/page_catalog.js';\nimport { WorkRegistry } from '../frontend/work_registry.js';\nimport { TrackedTransport } from '../frontend/tracked_transport.js';\nimport {\n createFrontendSurface,\n type FrontendApplicationSurface,\n type PageScopeContext,\n} from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.cjs';\nimport { importDefaultExport, type ModuleImporter, type WoodConfigSource } from './wood_config_source.cjs';\nimport type { WoodDomMode } from './wood_test_selection.cjs';\nimport { leaseDomAnimation, type WoodAnimationLease } from './wood_dom_animation.cjs';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.cjs';\n\n/** What the app's `main.testing.compose` module may hand back for the renderer side. */\nexport interface WoodRendererHooks {\n /** Backend push emitter feeding renderer subscriptions (default: transport-owned). */\n events?: RouterBridgeEvents;\n /** Register app-owned renderer composition on the fresh renderer root. */\n configureRendererRoot?(root: IContainer): void | Promise<void>;\n /** Register per-scope renderer builtins on a new page/layout scope. */\n configurePageScope?(scope: IContainer, view: ViewDef, context: PageScopeContext): void | Promise<void>;\n}\n\n/** A page's real component mounted under a DOM driver (`renderer.testing.host`). */\nexport interface WoodMountedPage {\n readonly target: Element;\n flush?(): void | Promise<void>;\n unmount(): void;\n}\n\nexport type WoodPageHost = (identity: string, page: FrontendPage, target: Element) => WoodMountedPage;\n\nexport interface RendererTierOptions {\n source: WoodConfigSource;\n router: WoodRouter;\n catalog: PageCatalog;\n operations: Map<string, OperationDef>;\n importer: ModuleImporter;\n hooks: WoodRendererHooks | null;\n dom: WoodDomMode | null;\n ioc: TestIocBuilder;\n /** Tokens whose `.value`/callable `.function` replacements stay caller-owned. */\n borrowedTokens?: readonly unknown[];\n registrations: RegistrationOnlyRoot;\n controllerClasses: ReadonlyMap<string, ControllerClass>;\n /**\n * `.load` seams over the page/layout DATA loaders. The native loader\n * identity is the config-relative companion source path\n * (`ui/pages/chat.load.ts`), export `default`.\n */\n loaders?: TestExecutableOverlays;\n /** The imported companion loaders by identity (discovered at build, none has run). */\n loaderModules?: ReadonlyMap<string, DataLoader>;\n}\n\nexport interface WoodDomDriver {\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n}\n\nexport interface RendererTier {\n readonly frontend: FrontendApplicationSurface;\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n navigate(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n pendingWork(): ReturnType<WorkRegistry['pending']>;\n verify(): Promise<void>;\n dom: WoodDomDriver | null;\n dispose(): Promise<void>;\n /** The renderer owner's method-seam registry (`test.inspect` on renderer tokens). */\n readonly [ENV_REGISTRY]: unknown;\n}\ntype ControllerClass = new (...args: never[]) => unknown;\n\nfunction controllerLoader(\n source: WoodConfigSource,\n importer: ModuleImporter,\n preloaded: ReadonlyMap<string, ControllerClass>,\n) {\n const cache = new Map<string, Promise<ControllerClass>>();\n for (const [identity, controller] of preloaded) cache.set(identity, Promise.resolve(controller));\n return (identity: string): Promise<ControllerClass> => {\n let pending = cache.get(identity);\n if (!pending) {\n pending = importDefaultExport<ControllerClass>(\n importer,\n source.rendererControllerFile(identity),\n `renderer controller \"${identity}\"`,\n );\n cache.set(identity, pending);\n }\n return pending;\n };\n}\n\nasync function loadPageHost(\n source: WoodConfigSource,\n importer: ModuleImporter,\n dom: WoodDomMode,\n): Promise<WoodPageHost> {\n if (dom === 'chromium') {\n throw new TestWoodError(\"select({ dom: 'chromium' }) is not available yet; use 'happy-dom'\");\n }\n if (typeof document === 'undefined') {\n throw new TestWoodError(\n \"select({ dom: 'happy-dom' }) requires a DOM environment (// @vitest-environment happy-dom)\",\n );\n }\n const hostPath = source.hookModule(source.config.renderer.testing?.host);\n if (!hostPath) {\n throw new TestWoodError(\n 'select({ dom }) requires renderer.testing.host in wood.config.yml (a module whose default export mounts a page)',\n );\n }\n return importDefaultExport<WoodPageHost>(importer, hostPath, 'renderer.testing.host');\n}\n\n/**\n * Keeps the mounted component in step with the surface's current page. While\n * a page is mounted it holds a Web Animations lease on the target's realm so\n * Svelte transitions work under happy-dom (see `wood_dom_animation.ts`); the\n * lease is released on unmount, on a failed host mount, and on dispose.\n */\nexport class MountKeeper {\n private mounted: WoodMountedPage | null = null;\n private mountedFor: FrontendPage | null = null;\n private animation: WoodAnimationLease | null = null;\n\n constructor(private readonly host: WoodPageHost) {}\n\n get target(): Element {\n if (!this.mounted) throw new TestWoodError('no page is mounted; open(page) first');\n return this.mounted.target;\n }\n\n /** The Web Animations lease held for the current mount (null when nothing is mounted). */\n get animationLease(): WoodAnimationLease | null {\n return this.animation;\n }\n\n async flush(): Promise<void> {\n await this.mounted?.flush?.();\n }\n\n sync(current: FrontendPage | null): void {\n if (current === this.mountedFor) return;\n this.unmount();\n if (!current) return;\n const target = document.createElement('div');\n document.body.appendChild(target);\n const lease = leaseDomAnimation(target);\n try {\n this.mounted = this.host(current.identity, current, target);\n } catch (error) {\n lease.release();\n target.remove();\n throw error;\n }\n this.animation = lease;\n this.mountedFor = current;\n }\n\n unmount(): void {\n const mounted = this.mounted;\n const lease = this.animation;\n this.mounted = null;\n this.mountedFor = null;\n this.animation = null;\n try {\n mounted?.unmount();\n } finally {\n mounted?.target.remove();\n lease?.release();\n }\n }\n}\n\nfunction domDriver(keeper: MountKeeper): WoodDomDriver {\n const find = (selector: string): Element => {\n const found = keeper.target.querySelector(selector);\n if (!found) throw new TestWoodError(`no element matches \"${selector}\"`);\n return found;\n };\n return {\n get target() {\n return keeper.target;\n },\n async click(selector) {\n (find(selector) as HTMLElement).click();\n await keeper.flush();\n },\n async type(selector, text) {\n const input = find(selector) as HTMLInputElement;\n input.focus();\n input.value = text;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n await keeper.flush();\n },\n async submit(selector) {\n find(selector).dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await keeper.flush();\n },\n async text(selector) {\n return keeper.target.querySelector(selector)?.textContent ?? '';\n },\n async exists(selector) {\n return keeper.target.querySelector(selector) !== null;\n },\n };\n}\n\nexport async function buildRendererTier(options: RendererTierOptions): Promise<RendererTier> {\n const {\n source,\n router,\n catalog,\n operations,\n importer,\n hooks,\n dom,\n ioc,\n borrowedTokens,\n registrations,\n controllerClasses,\n loaders,\n loaderModules,\n } = options;\n const keeper = dom ? new MountKeeper(await loadPageHost(source, importer, dom)) : null;\n\n const registry = new WorkRegistry();\n const defaultNavigation = new Navigation();\n const innerTransport = new InProcessRouterTransport(router, hooks?.events ?? new RouterBridgeEvents());\n let currentOwner: () => string = () => 'application';\n const transport = new TrackedTransport(innerTransport, registry, () => currentOwner());\n const { bridge } = buildTransportBridge(operations, transport);\n\n registrations.root.registerFunction(Navigation, () => defaultNavigation, { loadAs: LoadAs.Singleton });\n registrations.root.registerFunction(WOOD_FRONTEND_BRIDGE, () => bridge, { loadAs: LoadAs.Singleton });\n\n const rendererEnv = await buildRecordedEnvironment(ioc, registrations, 'renderer', borrowedTokens);\n const rendererRoot = rendererEnv.root;\n let navigation: Navigation;\n const loadController = controllerLoader(source, importer, controllerClasses);\n const resolveController = (identity: string): Promise<ControllerClass> => loadController(identity);\n // `.load` seams are installed over the imported companion loaders NOW —\n // before any page resolves — one slot per loader identity. Nothing runs\n // here; a page open/navigation executes them through the shared\n // orchestration exactly as the production renderer does.\n const installedLoaders = new Map<string, DataLoader>();\n for (const [identity, original] of loaderModules ?? []) {\n installedLoaders.set(\n identity,\n loaders ? (loaders.replace('load', { path: identity, export: 'default' }, original) as DataLoader) : original,\n );\n }\n const loaderFor = (viewPath: string): DataLoader | null => {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference) return null;\n const installed = installedLoaders.get(reference.identity);\n if (!installed) {\n throw new TestWoodError(`data loader ${reference.identity} was not imported at build; rebuild the environment`);\n }\n return installed;\n };\n const resolveLoaders = (view: ViewDef): ViewLoaderSet => ({\n layouts: view.layouts.map((layoutPath) => loaderFor(layoutPath)),\n view: loaderFor(view.viewPath),\n });\n let frontend: FrontendApplicationSurface;\n try {\n navigation = await ExecutionContext.run(rendererRoot as never, () => rendererRoot.get(Navigation)) as Navigation;\n frontend = createFrontendSurface({\n catalog,\n rendererRoot: rendererRoot as never,\n registry,\n navigation,\n resolveController,\n resolveLoaders,\n ...(hooks?.configurePageScope\n ? { configurePageScope: (scope, view, context) => hooks.configurePageScope!(scope as IContainer, view, context) }\n : {}),\n });\n } catch (error) {\n try {\n await cleanupAll('renderer build rollback failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer root', () => ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n } catch (cleanupError) {\n const failures = cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'renderer rollback', error: cleanupError }];\n throw new TestWoodCleanupError('renderer build failed and rollback failed', failures, error);\n }\n throw error;\n }\n\n currentOwner = () => {\n const current = frontend.current();\n return current ? `page ${current.identity}` : 'application';\n };\n\n const syncMount = async (): Promise<void> => {\n if (!keeper) return;\n keeper.sync(frontend.current());\n await keeper.flush();\n };\n const settle = async (): Promise<void> => {\n await frontend.settle();\n if (!keeper) return;\n await syncMount();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n await keeper.flush();\n };\n\n let closing: Promise<void> | null = null;\n const dispose = (): Promise<void> => closing ??= cleanupAll('renderer cleanup failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer work drain (before frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer frontend', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.dispose()) as Promise<void>],\n ['renderer work drain (after frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer root', () =>\n ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n\n return {\n frontend,\n events: innerTransport.events,\n rendererRoot,\n async open(page, params, query) {\n const opened = await frontend.open({\n page,\n ...(params ? { params } : {}),\n ...(query ? { query } : {}),\n });\n await syncMount();\n return opened;\n },\n async navigate(page, params, query) {\n if (!frontend.current()) throw new TestWoodError('navigate() needs an open page; call open(page) first');\n await frontend.act(() => navigation.go(page, params, query));\n await syncMount();\n const current = frontend.current();\n if (!current) throw new TestWoodError(`navigate(\"${page}\") left no page open`);\n return current;\n },\n current: () => frontend.current(),\n settle,\n pendingWork: () => registry.pending(),\n verify: () => rendererEnv.verify(),\n dom: keeper ? domDriver(keeper) : null,\n dispose,\n [ENV_REGISTRY]: (rendererEnv as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
1
  import { IContainer } from '@noego/ioc';
2
- import { TestIocBuilder, TestExecutableOverlays } from '@noego/testing';
2
+ import { ENV_REGISTRY, TestIocBuilder, TestExecutableOverlays } from '@noego/testing';
3
3
  import { WoodRouter } from '../router/wood_router.cjs';
4
4
  import { RouterBridgeEvents } from './create_router_bridge.cjs';
5
5
  import { OperationDef } from '../types/operations.cjs';
@@ -91,6 +91,8 @@ interface RendererTier {
91
91
  verify(): Promise<void>;
92
92
  dom: WoodDomDriver | null;
93
93
  dispose(): Promise<void>;
94
+ /** The renderer owner's method-seam registry (`test.inspect` on renderer tokens). */
95
+ readonly [ENV_REGISTRY]: unknown;
94
96
  }
95
97
  type ControllerClass = new (...args: never[]) => unknown;
96
98
  /**
@@ -1,5 +1,5 @@
1
1
  import { IContainer } from '@noego/ioc';
2
- import { TestIocBuilder, TestExecutableOverlays } from '@noego/testing';
2
+ import { ENV_REGISTRY, TestIocBuilder, TestExecutableOverlays } from '@noego/testing';
3
3
  import { WoodRouter } from '../router/wood_router.js';
4
4
  import { RouterBridgeEvents } from './create_router_bridge.js';
5
5
  import { OperationDef } from '../types/operations.js';
@@ -91,6 +91,8 @@ interface RendererTier {
91
91
  verify(): Promise<void>;
92
92
  dom: WoodDomDriver | null;
93
93
  dispose(): Promise<void>;
94
+ /** The renderer owner's method-seam registry (`test.inspect` on renderer tokens). */
95
+ readonly [ENV_REGISTRY]: unknown;
94
96
  }
95
97
  type ControllerClass = new (...args: never[]) => unknown;
96
98
  /**
@@ -1,4 +1,5 @@
1
1
  import { ExecutionContext, LoadAs } from "@noego/ioc";
2
+ import { ENV_REGISTRY } from "@noego/testing";
2
3
  import { InProcessRouterTransport } from "../transport/in_process_router_transport.js";
3
4
  import { buildTransportBridge } from "../transport/build_transport_bridge.js";
4
5
  import { RouterBridgeEvents } from "./create_router_bridge.js";
@@ -255,7 +256,8 @@ async function buildRendererTier(options) {
255
256
  pendingWork: () => registry.pending(),
256
257
  verify: () => rendererEnv.verify(),
257
258
  dom: keeper ? domDriver(keeper) : null,
258
- dispose
259
+ dispose,
260
+ [ENV_REGISTRY]: rendererEnv[ENV_REGISTRY]
259
261
  };
260
262
  }
261
263
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/wood_env_renderer.ts"],"sourcesContent":["/**\n * The renderer tier of a `testWood(configPath)` environment: a fresh renderer\n * root (mirroring the production process split), an environment-local\n * Navigation, the generated bridge client over an in-process transport into\n * the main router, and the frontend application surface over the selected\n * page catalog. With a DOM mode, the page's real component is mounted by the\n * app's host module and driven through a selector-based surface.\n */\nimport type { IContainer } from '@noego/ioc';\nimport { ExecutionContext, LoadAs } from '@noego/ioc';\nimport type { TestExecutableOverlays, TestIocBuilder } from '@noego/testing';\nimport type { WoodRouter } from '../router/wood_router.js';\nimport { InProcessRouterTransport } from '../transport/in_process_router_transport.js';\nimport { buildTransportBridge } from '../transport/build_transport_bridge.js';\nimport { RouterBridgeEvents } from './create_router_bridge.js';\nimport type { OperationDef, ViewDef } from '../types/index.js';\nimport type { DataLoader, ViewLoaderSet } from '../loader/loader_orchestration.js';\nimport { Navigation } from '../navigation/index.js';\nimport type { PageCatalog } from '../frontend/page_catalog.js';\nimport { WorkRegistry } from '../frontend/work_registry.js';\nimport { TrackedTransport } from '../frontend/tracked_transport.js';\nimport {\n createFrontendSurface,\n type FrontendApplicationSurface,\n type PageScopeContext,\n} from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.js';\nimport { importDefaultExport, type ModuleImporter, type WoodConfigSource } from './wood_config_source.js';\nimport type { WoodDomMode } from './wood_test_selection.js';\nimport { leaseDomAnimation, type WoodAnimationLease } from './wood_dom_animation.js';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.js';\n\n/** What the app's `main.testing.compose` module may hand back for the renderer side. */\nexport interface WoodRendererHooks {\n /** Backend push emitter feeding renderer subscriptions (default: transport-owned). */\n events?: RouterBridgeEvents;\n /** Register app-owned renderer composition on the fresh renderer root. */\n configureRendererRoot?(root: IContainer): void | Promise<void>;\n /** Register per-scope renderer builtins on a new page/layout scope. */\n configurePageScope?(scope: IContainer, view: ViewDef, context: PageScopeContext): void | Promise<void>;\n}\n\n/** A page's real component mounted under a DOM driver (`renderer.testing.host`). */\nexport interface WoodMountedPage {\n readonly target: Element;\n flush?(): void | Promise<void>;\n unmount(): void;\n}\n\nexport type WoodPageHost = (identity: string, page: FrontendPage, target: Element) => WoodMountedPage;\n\nexport interface RendererTierOptions {\n source: WoodConfigSource;\n router: WoodRouter;\n catalog: PageCatalog;\n operations: Map<string, OperationDef>;\n importer: ModuleImporter;\n hooks: WoodRendererHooks | null;\n dom: WoodDomMode | null;\n ioc: TestIocBuilder;\n /** Tokens whose `.value`/callable `.function` replacements stay caller-owned. */\n borrowedTokens?: readonly unknown[];\n registrations: RegistrationOnlyRoot;\n controllerClasses: ReadonlyMap<string, ControllerClass>;\n /**\n * `.load` seams over the page/layout DATA loaders. The native loader\n * identity is the config-relative companion source path\n * (`ui/pages/chat.load.ts`), export `default`.\n */\n loaders?: TestExecutableOverlays;\n /** The imported companion loaders by identity (discovered at build, none has run). */\n loaderModules?: ReadonlyMap<string, DataLoader>;\n}\n\nexport interface WoodDomDriver {\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n}\n\nexport interface RendererTier {\n readonly frontend: FrontendApplicationSurface;\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n navigate(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n pendingWork(): ReturnType<WorkRegistry['pending']>;\n verify(): Promise<void>;\n dom: WoodDomDriver | null;\n dispose(): Promise<void>;\n}\ntype ControllerClass = new (...args: never[]) => unknown;\n\nfunction controllerLoader(\n source: WoodConfigSource,\n importer: ModuleImporter,\n preloaded: ReadonlyMap<string, ControllerClass>,\n) {\n const cache = new Map<string, Promise<ControllerClass>>();\n for (const [identity, controller] of preloaded) cache.set(identity, Promise.resolve(controller));\n return (identity: string): Promise<ControllerClass> => {\n let pending = cache.get(identity);\n if (!pending) {\n pending = importDefaultExport<ControllerClass>(\n importer,\n source.rendererControllerFile(identity),\n `renderer controller \"${identity}\"`,\n );\n cache.set(identity, pending);\n }\n return pending;\n };\n}\n\nasync function loadPageHost(\n source: WoodConfigSource,\n importer: ModuleImporter,\n dom: WoodDomMode,\n): Promise<WoodPageHost> {\n if (dom === 'chromium') {\n throw new TestWoodError(\"select({ dom: 'chromium' }) is not available yet; use 'happy-dom'\");\n }\n if (typeof document === 'undefined') {\n throw new TestWoodError(\n \"select({ dom: 'happy-dom' }) requires a DOM environment (// @vitest-environment happy-dom)\",\n );\n }\n const hostPath = source.hookModule(source.config.renderer.testing?.host);\n if (!hostPath) {\n throw new TestWoodError(\n 'select({ dom }) requires renderer.testing.host in wood.config.yml (a module whose default export mounts a page)',\n );\n }\n return importDefaultExport<WoodPageHost>(importer, hostPath, 'renderer.testing.host');\n}\n\n/**\n * Keeps the mounted component in step with the surface's current page. While\n * a page is mounted it holds a Web Animations lease on the target's realm so\n * Svelte transitions work under happy-dom (see `wood_dom_animation.ts`); the\n * lease is released on unmount, on a failed host mount, and on dispose.\n */\nexport class MountKeeper {\n private mounted: WoodMountedPage | null = null;\n private mountedFor: FrontendPage | null = null;\n private animation: WoodAnimationLease | null = null;\n\n constructor(private readonly host: WoodPageHost) {}\n\n get target(): Element {\n if (!this.mounted) throw new TestWoodError('no page is mounted; open(page) first');\n return this.mounted.target;\n }\n\n /** The Web Animations lease held for the current mount (null when nothing is mounted). */\n get animationLease(): WoodAnimationLease | null {\n return this.animation;\n }\n\n async flush(): Promise<void> {\n await this.mounted?.flush?.();\n }\n\n sync(current: FrontendPage | null): void {\n if (current === this.mountedFor) return;\n this.unmount();\n if (!current) return;\n const target = document.createElement('div');\n document.body.appendChild(target);\n const lease = leaseDomAnimation(target);\n try {\n this.mounted = this.host(current.identity, current, target);\n } catch (error) {\n lease.release();\n target.remove();\n throw error;\n }\n this.animation = lease;\n this.mountedFor = current;\n }\n\n unmount(): void {\n const mounted = this.mounted;\n const lease = this.animation;\n this.mounted = null;\n this.mountedFor = null;\n this.animation = null;\n try {\n mounted?.unmount();\n } finally {\n mounted?.target.remove();\n lease?.release();\n }\n }\n}\n\nfunction domDriver(keeper: MountKeeper): WoodDomDriver {\n const find = (selector: string): Element => {\n const found = keeper.target.querySelector(selector);\n if (!found) throw new TestWoodError(`no element matches \"${selector}\"`);\n return found;\n };\n return {\n get target() {\n return keeper.target;\n },\n async click(selector) {\n (find(selector) as HTMLElement).click();\n await keeper.flush();\n },\n async type(selector, text) {\n const input = find(selector) as HTMLInputElement;\n input.focus();\n input.value = text;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n await keeper.flush();\n },\n async submit(selector) {\n find(selector).dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await keeper.flush();\n },\n async text(selector) {\n return keeper.target.querySelector(selector)?.textContent ?? '';\n },\n async exists(selector) {\n return keeper.target.querySelector(selector) !== null;\n },\n };\n}\n\nexport async function buildRendererTier(options: RendererTierOptions): Promise<RendererTier> {\n const {\n source,\n router,\n catalog,\n operations,\n importer,\n hooks,\n dom,\n ioc,\n borrowedTokens,\n registrations,\n controllerClasses,\n loaders,\n loaderModules,\n } = options;\n const keeper = dom ? new MountKeeper(await loadPageHost(source, importer, dom)) : null;\n\n const registry = new WorkRegistry();\n const defaultNavigation = new Navigation();\n const innerTransport = new InProcessRouterTransport(router, hooks?.events ?? new RouterBridgeEvents());\n let currentOwner: () => string = () => 'application';\n const transport = new TrackedTransport(innerTransport, registry, () => currentOwner());\n const { bridge } = buildTransportBridge(operations, transport);\n\n registrations.root.registerFunction(Navigation, () => defaultNavigation, { loadAs: LoadAs.Singleton });\n registrations.root.registerFunction(WOOD_FRONTEND_BRIDGE, () => bridge, { loadAs: LoadAs.Singleton });\n\n const rendererEnv = await buildRecordedEnvironment(ioc, registrations, 'renderer', borrowedTokens);\n const rendererRoot = rendererEnv.root;\n let navigation: Navigation;\n const loadController = controllerLoader(source, importer, controllerClasses);\n const resolveController = (identity: string): Promise<ControllerClass> => loadController(identity);\n // `.load` seams are installed over the imported companion loaders NOW —\n // before any page resolves — one slot per loader identity. Nothing runs\n // here; a page open/navigation executes them through the shared\n // orchestration exactly as the production renderer does.\n const installedLoaders = new Map<string, DataLoader>();\n for (const [identity, original] of loaderModules ?? []) {\n installedLoaders.set(\n identity,\n loaders ? (loaders.replace('load', { path: identity, export: 'default' }, original) as DataLoader) : original,\n );\n }\n const loaderFor = (viewPath: string): DataLoader | null => {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference) return null;\n const installed = installedLoaders.get(reference.identity);\n if (!installed) {\n throw new TestWoodError(`data loader ${reference.identity} was not imported at build; rebuild the environment`);\n }\n return installed;\n };\n const resolveLoaders = (view: ViewDef): ViewLoaderSet => ({\n layouts: view.layouts.map((layoutPath) => loaderFor(layoutPath)),\n view: loaderFor(view.viewPath),\n });\n let frontend: FrontendApplicationSurface;\n try {\n navigation = await ExecutionContext.run(rendererRoot as never, () => rendererRoot.get(Navigation)) as Navigation;\n frontend = createFrontendSurface({\n catalog,\n rendererRoot: rendererRoot as never,\n registry,\n navigation,\n resolveController,\n resolveLoaders,\n ...(hooks?.configurePageScope\n ? { configurePageScope: (scope, view, context) => hooks.configurePageScope!(scope as IContainer, view, context) }\n : {}),\n });\n } catch (error) {\n try {\n await cleanupAll('renderer build rollback failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer root', () => ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n } catch (cleanupError) {\n const failures = cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'renderer rollback', error: cleanupError }];\n throw new TestWoodCleanupError('renderer build failed and rollback failed', failures, error);\n }\n throw error;\n }\n\n currentOwner = () => {\n const current = frontend.current();\n return current ? `page ${current.identity}` : 'application';\n };\n\n const syncMount = async (): Promise<void> => {\n if (!keeper) return;\n keeper.sync(frontend.current());\n await keeper.flush();\n };\n const settle = async (): Promise<void> => {\n await frontend.settle();\n if (!keeper) return;\n await syncMount();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n await keeper.flush();\n };\n\n let closing: Promise<void> | null = null;\n const dispose = (): Promise<void> => closing ??= cleanupAll('renderer cleanup failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer work drain (before frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer frontend', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.dispose()) as Promise<void>],\n ['renderer work drain (after frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer root', () =>\n ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n\n return {\n frontend,\n events: innerTransport.events,\n rendererRoot,\n async open(page, params, query) {\n const opened = await frontend.open({\n page,\n ...(params ? { params } : {}),\n ...(query ? { query } : {}),\n });\n await syncMount();\n return opened;\n },\n async navigate(page, params, query) {\n if (!frontend.current()) throw new TestWoodError('navigate() needs an open page; call open(page) first');\n await frontend.act(() => navigation.go(page, params, query));\n await syncMount();\n const current = frontend.current();\n if (!current) throw new TestWoodError(`navigate(\"${page}\") left no page open`);\n return current;\n },\n current: () => frontend.current(),\n settle,\n pendingWork: () => registry.pending(),\n verify: () => rendererEnv.verify(),\n dom: keeper ? domDriver(keeper) : null,\n dispose,\n };\n}\n"],"mappings":"AASA,SAAS,kBAAkB,cAAc;AAGzC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,0BAA0B;AAGnC,SAAS,kBAAkB;AAE3B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,OAGK;AAEP,SAAS,eAAe,4BAA4B;AACpD,SAAS,2BAAuE;AAEhF,SAAS,yBAAkD;AAC3D;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAoEP,SAAS,iBACP,QACA,UACA,WACA;AACA,QAAM,QAAQ,oBAAI,IAAsC;AACxD,aAAW,CAAC,UAAU,UAAU,KAAK,UAAW,OAAM,IAAI,UAAU,QAAQ,QAAQ,UAAU,CAAC;AAC/F,SAAO,CAAC,aAA+C;AACrD,QAAI,UAAU,MAAM,IAAI,QAAQ;AAChC,QAAI,CAAC,SAAS;AACZ,gBAAU;AAAA,QACR;AAAA,QACA,OAAO,uBAAuB,QAAQ;AAAA,QACtC,wBAAwB,QAAQ;AAAA,MAClC;AACA,YAAM,IAAI,UAAU,OAAO;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAe,aACb,QACA,UACA,KACuB;AACvB,MAAI,QAAQ,YAAY;AACtB,UAAM,IAAI,cAAc,mEAAmE;AAAA,EAC7F;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,OAAO,WAAW,OAAO,OAAO,SAAS,SAAS,IAAI;AACvE,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO,oBAAkC,UAAU,UAAU,uBAAuB;AACtF;AAQO,MAAM,YAAY;AAAA,EAKvB,YAA6B,MAAoB;AAApB;AAJ7B,SAAQ,UAAkC;AAC1C,SAAQ,aAAkC;AAC1C,SAAQ,YAAuC;AAAA,EAEG;AAAA,EAElD,IAAI,SAAkB;AACpB,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,cAAc,sCAAsC;AACjF,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,IAAI,iBAA4C;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,KAAK,SAAS,QAAQ;AAAA,EAC9B;AAAA,EAEA,KAAK,SAAoC;AACvC,QAAI,YAAY,KAAK,WAAY;AACjC,SAAK,QAAQ;AACb,QAAI,CAAC,QAAS;AACd,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,aAAS,KAAK,YAAY,MAAM;AAChC,UAAM,QAAQ,kBAAkB,MAAM;AACtC,QAAI;AACF,WAAK,UAAU,KAAK,KAAK,QAAQ,UAAU,SAAS,MAAM;AAAA,IAC5D,SAAS,OAAO;AACd,YAAM,QAAQ;AACd,aAAO,OAAO;AACd,YAAM;AAAA,IACR;AACA,SAAK,YAAY;AACjB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,UAAgB;AACd,UAAM,UAAU,KAAK;AACrB,UAAM,QAAQ,KAAK;AACnB,SAAK,UAAU;AACf,SAAK,aAAa;AAClB,SAAK,YAAY;AACjB,QAAI;AACF,eAAS,QAAQ;AAAA,IACnB,UAAE;AACA,eAAS,OAAO,OAAO;AACvB,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;AAEA,SAAS,UAAU,QAAoC;AACrD,QAAM,OAAO,CAAC,aAA8B;AAC1C,UAAM,QAAQ,OAAO,OAAO,cAAc,QAAQ;AAClD,QAAI,CAAC,MAAO,OAAM,IAAI,cAAc,uBAAuB,QAAQ,GAAG;AACtE,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,IAAI,SAAS;AACX,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,MAAM,MAAM,UAAU;AACpB,MAAC,KAAK,QAAQ,EAAkB,MAAM;AACtC,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,IACA,MAAM,KAAK,UAAU,MAAM;AACzB,YAAM,QAAQ,KAAK,QAAQ;AAC3B,YAAM,MAAM;AACZ,YAAM,QAAQ;AACd,YAAM,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AACzD,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,IACA,MAAM,OAAO,UAAU;AACrB,WAAK,QAAQ,EAAE,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,MAAM,YAAY,KAAK,CAAC,CAAC;AACrF,YAAM,OAAO,MAAM;AAAA,IACrB;AAAA,IACA,MAAM,KAAK,UAAU;AACnB,aAAO,OAAO,OAAO,cAAc,QAAQ,GAAG,eAAe;AAAA,IAC/D;AAAA,IACA,MAAM,OAAO,UAAU;AACrB,aAAO,OAAO,OAAO,cAAc,QAAQ,MAAM;AAAA,IACnD;AAAA,EACF;AACF;AAEA,eAAsB,kBAAkB,SAAqD;AAC3F,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,SAAS,MAAM,IAAI,YAAY,MAAM,aAAa,QAAQ,UAAU,GAAG,CAAC,IAAI;AAElF,QAAM,WAAW,IAAI,aAAa;AAClC,QAAM,oBAAoB,IAAI,WAAW;AACzC,QAAM,iBAAiB,IAAI,yBAAyB,QAAQ,OAAO,UAAU,IAAI,mBAAmB,CAAC;AACrG,MAAI,eAA6B,MAAM;AACvC,QAAM,YAAY,IAAI,iBAAiB,gBAAgB,UAAU,MAAM,aAAa,CAAC;AACrF,QAAM,EAAE,OAAO,IAAI,qBAAqB,YAAY,SAAS;AAE7D,gBAAc,KAAK,iBAAiB,YAAY,MAAM,mBAAmB,EAAE,QAAQ,OAAO,UAAU,CAAC;AACrG,gBAAc,KAAK,iBAAiB,sBAAsB,MAAM,QAAQ,EAAE,QAAQ,OAAO,UAAU,CAAC;AAEpG,QAAM,cAAc,MAAM,yBAAyB,KAAK,eAAe,YAAY,cAAc;AACjG,QAAM,eAAe,YAAY;AACjC,MAAI;AACJ,QAAM,iBAAiB,iBAAiB,QAAQ,UAAU,iBAAiB;AAC3E,QAAM,oBAAoB,CAAC,aAA+C,eAAe,QAAQ;AAKjG,QAAM,mBAAmB,oBAAI,IAAwB;AACrD,aAAW,CAAC,UAAU,QAAQ,KAAK,iBAAiB,CAAC,GAAG;AACtD,qBAAiB;AAAA,MACf;AAAA,MACA,UAAW,QAAQ,QAAQ,QAAQ,EAAE,MAAM,UAAU,QAAQ,UAAU,GAAG,QAAQ,IAAmB;AAAA,IACvG;AAAA,EACF;AACA,QAAM,YAAY,CAAC,aAAwC;AACzD,UAAM,YAAY,OAAO,wBAAwB,QAAQ;AACzD,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,YAAY,iBAAiB,IAAI,UAAU,QAAQ;AACzD,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,cAAc,eAAe,UAAU,QAAQ,qDAAqD;AAAA,IAChH;AACA,WAAO;AAAA,EACT;AACA,QAAM,iBAAiB,CAAC,UAAkC;AAAA,IACxD,SAAS,KAAK,QAAQ,IAAI,CAAC,eAAe,UAAU,UAAU,CAAC;AAAA,IAC/D,MAAM,UAAU,KAAK,QAAQ;AAAA,EAC/B;AACA,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,iBAAiB,IAAI,cAAuB,MAAM,aAAa,IAAI,UAAU,CAAC;AACjG,eAAW,sBAAsB;AAAA,MAC/B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,OAAO,qBACP,EAAE,oBAAoB,CAAC,OAAO,MAAM,YAAY,MAAM,mBAAoB,OAAqB,MAAM,OAAO,EAAE,IAC9G,CAAC;AAAA,IACP,CAAC;AAAA,EACH,SAAS,OAAO;AACd,QAAI;AACF,YAAM,WAAW,kCAAkC;AAAA,QACjD,CAAC,kBAAkB,MAAM,QAAQ,QAAQ,CAAC;AAAA,QAC1C,CAAC,iBAAiB,MAAM,iBAAiB,IAAI,cAAuB,MAAM,YAAY,QAAQ,CAAC,CAAkB;AAAA,MACnH,CAAC;AAAA,IACH,SAAS,cAAc;AACrB,YAAM,WAAW,wBAAwB,uBACrC,aAAa,WACb,CAAC,EAAE,OAAO,qBAAqB,OAAO,aAAa,CAAC;AACxD,YAAM,IAAI,qBAAqB,6CAA6C,UAAU,KAAK;AAAA,IAC7F;AACA,UAAM;AAAA,EACR;AAEA,iBAAe,MAAM;AACnB,UAAM,UAAU,SAAS,QAAQ;AACjC,WAAO,UAAU,QAAQ,QAAQ,QAAQ,KAAK;AAAA,EAChD;AAEA,QAAM,YAAY,YAA2B;AAC3C,QAAI,CAAC,OAAQ;AACb,WAAO,KAAK,SAAS,QAAQ,CAAC;AAC9B,UAAM,OAAO,MAAM;AAAA,EACrB;AACA,QAAM,SAAS,YAA2B;AACxC,UAAM,SAAS,OAAO;AACtB,QAAI,CAAC,OAAQ;AACb,UAAM,UAAU;AAChB,UAAM,IAAI,QAAc,CAAC,YAAY,WAAW,SAAS,CAAC,CAAC;AAC3D,UAAM,OAAO,MAAM;AAAA,EACrB;AAEA,MAAI,UAAgC;AACpC,QAAM,UAAU,MAAqB,sBAAY,WAAW,2BAA2B;AAAA,IACrF,CAAC,kBAAkB,MAAM,QAAQ,QAAQ,CAAC;AAAA,IAC1C,CAAC,yCAAyC,MACxC,iBAAiB,IAAI,cAAuB,MAAM,SAAS,OAAO,CAAC,CAAkB;AAAA,IACvF,CAAC,qBAAqB,MACpB,iBAAiB,IAAI,cAAuB,MAAM,SAAS,QAAQ,CAAC,CAAkB;AAAA,IACxF,CAAC,wCAAwC,MACvC,iBAAiB,IAAI,cAAuB,MAAM,SAAS,OAAO,CAAC,CAAkB;AAAA,IACvF,CAAC,iBAAiB,MAChB,iBAAiB,IAAI,cAAuB,MAAM,YAAY,QAAQ,CAAC,CAAkB;AAAA,EAC7F,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,eAAe;AAAA,IACvB;AAAA,IACA,MAAM,KAAK,MAAM,QAAQ,OAAO;AAC9B,YAAM,SAAS,MAAM,SAAS,KAAK;AAAA,QACjC;AAAA,QACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,QAC3B,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,MAC3B,CAAC;AACD,YAAM,UAAU;AAChB,aAAO;AAAA,IACT;AAAA,IACA,MAAM,SAAS,MAAM,QAAQ,OAAO;AAClC,UAAI,CAAC,SAAS,QAAQ,EAAG,OAAM,IAAI,cAAc,sDAAsD;AACvG,YAAM,SAAS,IAAI,MAAM,WAAW,GAAG,MAAM,QAAQ,KAAK,CAAC;AAC3D,YAAM,UAAU;AAChB,YAAM,UAAU,SAAS,QAAQ;AACjC,UAAI,CAAC,QAAS,OAAM,IAAI,cAAc,aAAa,IAAI,sBAAsB;AAC7E,aAAO;AAAA,IACT;AAAA,IACA,SAAS,MAAM,SAAS,QAAQ;AAAA,IAChC;AAAA,IACA,aAAa,MAAM,SAAS,QAAQ;AAAA,IACpC,QAAQ,MAAM,YAAY,OAAO;AAAA,IACjC,KAAK,SAAS,UAAU,MAAM,IAAI;AAAA,IAClC;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/testing/wood_env_renderer.ts"],"sourcesContent":["/**\n * The renderer tier of a `testWood(configPath)` environment: a fresh renderer\n * root (mirroring the production process split), an environment-local\n * Navigation, the generated bridge client over an in-process transport into\n * the main router, and the frontend application surface over the selected\n * page catalog. With a DOM mode, the page's real component is mounted by the\n * app's host module and driven through a selector-based surface.\n */\nimport type { IContainer } from '@noego/ioc';\nimport { ExecutionContext, LoadAs } from '@noego/ioc';\nimport { ENV_REGISTRY, type TestExecutableOverlays, type TestIocBuilder } from '@noego/testing';\nimport type { WoodRouter } from '../router/wood_router.js';\nimport { InProcessRouterTransport } from '../transport/in_process_router_transport.js';\nimport { buildTransportBridge } from '../transport/build_transport_bridge.js';\nimport { RouterBridgeEvents } from './create_router_bridge.js';\nimport type { OperationDef, ViewDef } from '../types/index.js';\nimport type { DataLoader, ViewLoaderSet } from '../loader/loader_orchestration.js';\nimport { Navigation } from '../navigation/index.js';\nimport type { PageCatalog } from '../frontend/page_catalog.js';\nimport { WorkRegistry } from '../frontend/work_registry.js';\nimport { TrackedTransport } from '../frontend/tracked_transport.js';\nimport {\n createFrontendSurface,\n type FrontendApplicationSurface,\n type PageScopeContext,\n} from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport { TestWoodError, WOOD_FRONTEND_BRIDGE } from './test_wood.js';\nimport { importDefaultExport, type ModuleImporter, type WoodConfigSource } from './wood_config_source.js';\nimport type { WoodDomMode } from './wood_test_selection.js';\nimport { leaseDomAnimation, type WoodAnimationLease } from './wood_dom_animation.js';\nimport {\n buildRecordedEnvironment,\n cleanupAll,\n RegistrationOnlyRoot,\n TestWoodCleanupError,\n} from './registration_only_root.js';\n\n/** What the app's `main.testing.compose` module may hand back for the renderer side. */\nexport interface WoodRendererHooks {\n /** Backend push emitter feeding renderer subscriptions (default: transport-owned). */\n events?: RouterBridgeEvents;\n /** Register app-owned renderer composition on the fresh renderer root. */\n configureRendererRoot?(root: IContainer): void | Promise<void>;\n /** Register per-scope renderer builtins on a new page/layout scope. */\n configurePageScope?(scope: IContainer, view: ViewDef, context: PageScopeContext): void | Promise<void>;\n}\n\n/** A page's real component mounted under a DOM driver (`renderer.testing.host`). */\nexport interface WoodMountedPage {\n readonly target: Element;\n flush?(): void | Promise<void>;\n unmount(): void;\n}\n\nexport type WoodPageHost = (identity: string, page: FrontendPage, target: Element) => WoodMountedPage;\n\nexport interface RendererTierOptions {\n source: WoodConfigSource;\n router: WoodRouter;\n catalog: PageCatalog;\n operations: Map<string, OperationDef>;\n importer: ModuleImporter;\n hooks: WoodRendererHooks | null;\n dom: WoodDomMode | null;\n ioc: TestIocBuilder;\n /** Tokens whose `.value`/callable `.function` replacements stay caller-owned. */\n borrowedTokens?: readonly unknown[];\n registrations: RegistrationOnlyRoot;\n controllerClasses: ReadonlyMap<string, ControllerClass>;\n /**\n * `.load` seams over the page/layout DATA loaders. The native loader\n * identity is the config-relative companion source path\n * (`ui/pages/chat.load.ts`), export `default`.\n */\n loaders?: TestExecutableOverlays;\n /** The imported companion loaders by identity (discovered at build, none has run). */\n loaderModules?: ReadonlyMap<string, DataLoader>;\n}\n\nexport interface WoodDomDriver {\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n}\n\nexport interface RendererTier {\n readonly frontend: FrontendApplicationSurface;\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n navigate(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n pendingWork(): ReturnType<WorkRegistry['pending']>;\n verify(): Promise<void>;\n dom: WoodDomDriver | null;\n dispose(): Promise<void>;\n /** The renderer owner's method-seam registry (`test.inspect` on renderer tokens). */\n readonly [ENV_REGISTRY]: unknown;\n}\ntype ControllerClass = new (...args: never[]) => unknown;\n\nfunction controllerLoader(\n source: WoodConfigSource,\n importer: ModuleImporter,\n preloaded: ReadonlyMap<string, ControllerClass>,\n) {\n const cache = new Map<string, Promise<ControllerClass>>();\n for (const [identity, controller] of preloaded) cache.set(identity, Promise.resolve(controller));\n return (identity: string): Promise<ControllerClass> => {\n let pending = cache.get(identity);\n if (!pending) {\n pending = importDefaultExport<ControllerClass>(\n importer,\n source.rendererControllerFile(identity),\n `renderer controller \"${identity}\"`,\n );\n cache.set(identity, pending);\n }\n return pending;\n };\n}\n\nasync function loadPageHost(\n source: WoodConfigSource,\n importer: ModuleImporter,\n dom: WoodDomMode,\n): Promise<WoodPageHost> {\n if (dom === 'chromium') {\n throw new TestWoodError(\"select({ dom: 'chromium' }) is not available yet; use 'happy-dom'\");\n }\n if (typeof document === 'undefined') {\n throw new TestWoodError(\n \"select({ dom: 'happy-dom' }) requires a DOM environment (// @vitest-environment happy-dom)\",\n );\n }\n const hostPath = source.hookModule(source.config.renderer.testing?.host);\n if (!hostPath) {\n throw new TestWoodError(\n 'select({ dom }) requires renderer.testing.host in wood.config.yml (a module whose default export mounts a page)',\n );\n }\n return importDefaultExport<WoodPageHost>(importer, hostPath, 'renderer.testing.host');\n}\n\n/**\n * Keeps the mounted component in step with the surface's current page. While\n * a page is mounted it holds a Web Animations lease on the target's realm so\n * Svelte transitions work under happy-dom (see `wood_dom_animation.ts`); the\n * lease is released on unmount, on a failed host mount, and on dispose.\n */\nexport class MountKeeper {\n private mounted: WoodMountedPage | null = null;\n private mountedFor: FrontendPage | null = null;\n private animation: WoodAnimationLease | null = null;\n\n constructor(private readonly host: WoodPageHost) {}\n\n get target(): Element {\n if (!this.mounted) throw new TestWoodError('no page is mounted; open(page) first');\n return this.mounted.target;\n }\n\n /** The Web Animations lease held for the current mount (null when nothing is mounted). */\n get animationLease(): WoodAnimationLease | null {\n return this.animation;\n }\n\n async flush(): Promise<void> {\n await this.mounted?.flush?.();\n }\n\n sync(current: FrontendPage | null): void {\n if (current === this.mountedFor) return;\n this.unmount();\n if (!current) return;\n const target = document.createElement('div');\n document.body.appendChild(target);\n const lease = leaseDomAnimation(target);\n try {\n this.mounted = this.host(current.identity, current, target);\n } catch (error) {\n lease.release();\n target.remove();\n throw error;\n }\n this.animation = lease;\n this.mountedFor = current;\n }\n\n unmount(): void {\n const mounted = this.mounted;\n const lease = this.animation;\n this.mounted = null;\n this.mountedFor = null;\n this.animation = null;\n try {\n mounted?.unmount();\n } finally {\n mounted?.target.remove();\n lease?.release();\n }\n }\n}\n\nfunction domDriver(keeper: MountKeeper): WoodDomDriver {\n const find = (selector: string): Element => {\n const found = keeper.target.querySelector(selector);\n if (!found) throw new TestWoodError(`no element matches \"${selector}\"`);\n return found;\n };\n return {\n get target() {\n return keeper.target;\n },\n async click(selector) {\n (find(selector) as HTMLElement).click();\n await keeper.flush();\n },\n async type(selector, text) {\n const input = find(selector) as HTMLInputElement;\n input.focus();\n input.value = text;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n await keeper.flush();\n },\n async submit(selector) {\n find(selector).dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await keeper.flush();\n },\n async text(selector) {\n return keeper.target.querySelector(selector)?.textContent ?? '';\n },\n async exists(selector) {\n return keeper.target.querySelector(selector) !== null;\n },\n };\n}\n\nexport async function buildRendererTier(options: RendererTierOptions): Promise<RendererTier> {\n const {\n source,\n router,\n catalog,\n operations,\n importer,\n hooks,\n dom,\n ioc,\n borrowedTokens,\n registrations,\n controllerClasses,\n loaders,\n loaderModules,\n } = options;\n const keeper = dom ? new MountKeeper(await loadPageHost(source, importer, dom)) : null;\n\n const registry = new WorkRegistry();\n const defaultNavigation = new Navigation();\n const innerTransport = new InProcessRouterTransport(router, hooks?.events ?? new RouterBridgeEvents());\n let currentOwner: () => string = () => 'application';\n const transport = new TrackedTransport(innerTransport, registry, () => currentOwner());\n const { bridge } = buildTransportBridge(operations, transport);\n\n registrations.root.registerFunction(Navigation, () => defaultNavigation, { loadAs: LoadAs.Singleton });\n registrations.root.registerFunction(WOOD_FRONTEND_BRIDGE, () => bridge, { loadAs: LoadAs.Singleton });\n\n const rendererEnv = await buildRecordedEnvironment(ioc, registrations, 'renderer', borrowedTokens);\n const rendererRoot = rendererEnv.root;\n let navigation: Navigation;\n const loadController = controllerLoader(source, importer, controllerClasses);\n const resolveController = (identity: string): Promise<ControllerClass> => loadController(identity);\n // `.load` seams are installed over the imported companion loaders NOW —\n // before any page resolves — one slot per loader identity. Nothing runs\n // here; a page open/navigation executes them through the shared\n // orchestration exactly as the production renderer does.\n const installedLoaders = new Map<string, DataLoader>();\n for (const [identity, original] of loaderModules ?? []) {\n installedLoaders.set(\n identity,\n loaders ? (loaders.replace('load', { path: identity, export: 'default' }, original) as DataLoader) : original,\n );\n }\n const loaderFor = (viewPath: string): DataLoader | null => {\n const reference = source.rendererLoaderReference(viewPath);\n if (!reference) return null;\n const installed = installedLoaders.get(reference.identity);\n if (!installed) {\n throw new TestWoodError(`data loader ${reference.identity} was not imported at build; rebuild the environment`);\n }\n return installed;\n };\n const resolveLoaders = (view: ViewDef): ViewLoaderSet => ({\n layouts: view.layouts.map((layoutPath) => loaderFor(layoutPath)),\n view: loaderFor(view.viewPath),\n });\n let frontend: FrontendApplicationSurface;\n try {\n navigation = await ExecutionContext.run(rendererRoot as never, () => rendererRoot.get(Navigation)) as Navigation;\n frontend = createFrontendSurface({\n catalog,\n rendererRoot: rendererRoot as never,\n registry,\n navigation,\n resolveController,\n resolveLoaders,\n ...(hooks?.configurePageScope\n ? { configurePageScope: (scope, view, context) => hooks.configurePageScope!(scope as IContainer, view, context) }\n : {}),\n });\n } catch (error) {\n try {\n await cleanupAll('renderer build rollback failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer root', () => ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n } catch (cleanupError) {\n const failures = cleanupError instanceof TestWoodCleanupError\n ? cleanupError.failures\n : [{ owner: 'renderer rollback', error: cleanupError }];\n throw new TestWoodCleanupError('renderer build failed and rollback failed', failures, error);\n }\n throw error;\n }\n\n currentOwner = () => {\n const current = frontend.current();\n return current ? `page ${current.identity}` : 'application';\n };\n\n const syncMount = async (): Promise<void> => {\n if (!keeper) return;\n keeper.sync(frontend.current());\n await keeper.flush();\n };\n const settle = async (): Promise<void> => {\n await frontend.settle();\n if (!keeper) return;\n await syncMount();\n await new Promise<void>((resolve) => setTimeout(resolve, 0));\n await keeper.flush();\n };\n\n let closing: Promise<void> | null = null;\n const dispose = (): Promise<void> => closing ??= cleanupAll('renderer cleanup failed', [\n ['renderer mount', () => keeper?.unmount()],\n ['renderer work drain (before frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer frontend', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.dispose()) as Promise<void>],\n ['renderer work drain (after frontend)', () =>\n ExecutionContext.run(rendererRoot as never, () => frontend.settle()) as Promise<void>],\n ['renderer root', () =>\n ExecutionContext.run(rendererRoot as never, () => rendererEnv.dispose()) as Promise<void>],\n ]);\n\n return {\n frontend,\n events: innerTransport.events,\n rendererRoot,\n async open(page, params, query) {\n const opened = await frontend.open({\n page,\n ...(params ? { params } : {}),\n ...(query ? { query } : {}),\n });\n await syncMount();\n return opened;\n },\n async navigate(page, params, query) {\n if (!frontend.current()) throw new TestWoodError('navigate() needs an open page; call open(page) first');\n await frontend.act(() => navigation.go(page, params, query));\n await syncMount();\n const current = frontend.current();\n if (!current) throw new TestWoodError(`navigate(\"${page}\") left no page open`);\n return current;\n },\n current: () => frontend.current(),\n settle,\n pendingWork: () => registry.pending(),\n verify: () => rendererEnv.verify(),\n dom: keeper ? domDriver(keeper) : null,\n dispose,\n [ENV_REGISTRY]: (rendererEnv as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noego/wood",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",