@noego/wood 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +31 -0
  2. package/dist/client/index.cjs +18 -11
  3. package/dist/client/index.cjs.map +1 -1
  4. package/dist/client/index.d.cts +21 -1
  5. package/dist/client/index.d.ts +21 -1
  6. package/dist/client/index.js +17 -11
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/codegen/renderer_app_generator.cjs +2 -9
  9. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  10. package/dist/codegen/renderer_app_generator.js +2 -9
  11. package/dist/codegen/renderer_app_generator.js.map +1 -1
  12. package/dist/controller/cleanup.cjs +39 -0
  13. package/dist/controller/cleanup.cjs.map +1 -0
  14. package/dist/controller/cleanup.d.cts +4 -0
  15. package/dist/controller/cleanup.d.ts +4 -0
  16. package/dist/controller/cleanup.js +15 -0
  17. package/dist/controller/cleanup.js.map +1 -0
  18. package/dist/controller/controller_lifecycle.cjs +26 -2
  19. package/dist/controller/controller_lifecycle.cjs.map +1 -1
  20. package/dist/controller/controller_lifecycle.d.cts +3 -1
  21. package/dist/controller/controller_lifecycle.d.ts +3 -1
  22. package/dist/controller/controller_lifecycle.js +24 -1
  23. package/dist/controller/controller_lifecycle.js.map +1 -1
  24. package/dist/frontend/frontend_lifecycle.cjs +5 -7
  25. package/dist/frontend/frontend_lifecycle.cjs.map +1 -1
  26. package/dist/frontend/frontend_lifecycle.d.cts +1 -14
  27. package/dist/frontend/frontend_lifecycle.d.ts +1 -14
  28. package/dist/frontend/frontend_lifecycle.js +5 -7
  29. package/dist/frontend/frontend_lifecycle.js.map +1 -1
  30. package/dist/frontend/frontend_surface.cjs +130 -104
  31. package/dist/frontend/frontend_surface.cjs.map +1 -1
  32. package/dist/frontend/frontend_surface.js +131 -105
  33. package/dist/frontend/frontend_surface.js.map +1 -1
  34. package/dist/frontend/work_registry.cjs +29 -4
  35. package/dist/frontend/work_registry.cjs.map +1 -1
  36. package/dist/frontend/work_registry.d.cts +2 -0
  37. package/dist/frontend/work_registry.d.ts +2 -0
  38. package/dist/frontend/work_registry.js +29 -4
  39. package/dist/frontend/work_registry.js.map +1 -1
  40. package/dist/index.d.cts +2 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/ipc/index.cjs +6 -2
  43. package/dist/ipc/index.cjs.map +1 -1
  44. package/dist/ipc/index.d.cts +9 -0
  45. package/dist/ipc/index.d.ts +9 -0
  46. package/dist/ipc/index.js +6 -2
  47. package/dist/ipc/index.js.map +1 -1
  48. package/dist/main/index.cjs +53 -35
  49. package/dist/main/index.cjs.map +1 -1
  50. package/dist/main/index.d.cts +11 -2
  51. package/dist/main/index.d.ts +11 -2
  52. package/dist/main/index.js +56 -40
  53. package/dist/main/index.js.map +1 -1
  54. package/dist/main/startup_failure_handler.cjs +67 -0
  55. package/dist/main/startup_failure_handler.cjs.map +1 -0
  56. package/dist/main/startup_failure_handler.d.cts +16 -0
  57. package/dist/main/startup_failure_handler.d.ts +16 -0
  58. package/dist/main/startup_failure_handler.js +43 -0
  59. package/dist/main/startup_failure_handler.js.map +1 -0
  60. package/dist/router/index.d.cts +3 -0
  61. package/dist/router/index.d.ts +3 -0
  62. package/dist/router/wood_router.cjs +204 -123
  63. package/dist/router/wood_router.cjs.map +1 -1
  64. package/dist/router/wood_router.d.cts +20 -0
  65. package/dist/router/wood_router.d.ts +20 -0
  66. package/dist/router/wood_router.js +206 -125
  67. package/dist/router/wood_router.js.map +1 -1
  68. package/dist/runtime/index.cjs +13 -0
  69. package/dist/runtime/index.cjs.map +1 -1
  70. package/dist/runtime/index.d.cts +8 -4
  71. package/dist/runtime/index.d.ts +8 -4
  72. package/dist/runtime/index.js +8 -0
  73. package/dist/runtime/index.js.map +1 -1
  74. package/dist/runtime/renderer_config_extension.cjs +1 -2
  75. package/dist/runtime/renderer_config_extension.cjs.map +1 -1
  76. package/dist/runtime/renderer_config_extension.d.cts +1 -1
  77. package/dist/runtime/renderer_config_extension.d.ts +1 -1
  78. package/dist/runtime/renderer_config_extension.js +1 -2
  79. package/dist/runtime/renderer_config_extension.js.map +1 -1
  80. package/dist/runtime/renderer_log_dispatcher.cjs +2 -0
  81. package/dist/runtime/renderer_log_dispatcher.cjs.map +1 -1
  82. package/dist/runtime/renderer_log_dispatcher.d.cts +1 -1
  83. package/dist/runtime/renderer_log_dispatcher.d.ts +1 -1
  84. package/dist/runtime/renderer_log_dispatcher.js +1 -0
  85. package/dist/runtime/renderer_log_dispatcher.js.map +1 -1
  86. package/dist/runtime/runtime.cjs +107 -57
  87. package/dist/runtime/runtime.cjs.map +1 -1
  88. package/dist/runtime/runtime.d.cts +12 -0
  89. package/dist/runtime/runtime.d.ts +12 -0
  90. package/dist/runtime/runtime.js +110 -59
  91. package/dist/runtime/runtime.js.map +1 -1
  92. package/dist/runtime/runtime_module.cjs +58 -0
  93. package/dist/runtime/runtime_module.cjs.map +1 -0
  94. package/dist/runtime/runtime_module.d.cts +22 -0
  95. package/dist/runtime/runtime_module.d.ts +22 -0
  96. package/dist/runtime/runtime_module.js +30 -0
  97. package/dist/runtime/runtime_module.js.map +1 -0
  98. package/dist/testing/create_controller_harness.d.cts +3 -0
  99. package/dist/testing/create_controller_harness.d.ts +3 -0
  100. package/dist/testing/create_router_bridge.d.cts +3 -0
  101. package/dist/testing/create_router_bridge.d.ts +3 -0
  102. package/dist/testing/create_test_router.d.cts +3 -0
  103. package/dist/testing/create_test_router.d.ts +3 -0
  104. package/dist/testing/index.cjs +3 -0
  105. package/dist/testing/index.cjs.map +1 -1
  106. package/dist/testing/index.d.cts +6 -1
  107. package/dist/testing/index.d.ts +6 -1
  108. package/dist/testing/index.js +2 -0
  109. package/dist/testing/index.js.map +1 -1
  110. package/dist/testing/registration_only_root.cjs +168 -0
  111. package/dist/testing/registration_only_root.cjs.map +1 -0
  112. package/dist/testing/registration_only_root.d.cts +46 -0
  113. package/dist/testing/registration_only_root.d.ts +46 -0
  114. package/dist/testing/registration_only_root.js +141 -0
  115. package/dist/testing/registration_only_root.js.map +1 -0
  116. package/dist/testing/test_wood_config.cjs +382 -70
  117. package/dist/testing/test_wood_config.cjs.map +1 -1
  118. package/dist/testing/test_wood_config.d.cts +25 -1
  119. package/dist/testing/test_wood_config.d.ts +25 -1
  120. package/dist/testing/test_wood_config.js +388 -71
  121. package/dist/testing/test_wood_config.js.map +1 -1
  122. package/dist/testing/wood_dom_animation.cjs +213 -0
  123. package/dist/testing/wood_dom_animation.cjs.map +1 -0
  124. package/dist/testing/wood_dom_animation.d.cts +89 -0
  125. package/dist/testing/wood_dom_animation.d.ts +89 -0
  126. package/dist/testing/wood_dom_animation.js +185 -0
  127. package/dist/testing/wood_dom_animation.js.map +1 -0
  128. package/dist/testing/wood_env_renderer.cjs +81 -32
  129. package/dist/testing/wood_env_renderer.cjs.map +1 -1
  130. package/dist/testing/wood_env_renderer.d.cts +32 -1
  131. package/dist/testing/wood_env_renderer.d.ts +32 -1
  132. package/dist/testing/wood_env_renderer.js +84 -32
  133. package/dist/testing/wood_env_renderer.js.map +1 -1
  134. package/dist/transport/in_process_router_transport.d.cts +3 -0
  135. package/dist/transport/in_process_router_transport.d.ts +3 -0
  136. package/dist/transport/index.d.cts +3 -0
  137. package/dist/transport/index.d.ts +3 -0
  138. package/package.json +8 -6
  139. package/src/components/NavigationShell.svelte +12 -7
  140. package/src/controller/cleanup.ts +8 -0
  141. package/src/controller/controller_lifecycle.ts +24 -0
  142. package/src/navigation/layout_preservation.ts +45 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["/**\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' }, pages: ['settings.backend-service'] })\n * .classes([[BackendServiceLifecycleCoordinator, LifecycleStub]])\n * .methods([[LifecycleStub, { getStatus: test.once(test.returns(status)) }]])\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * `@noego/testing` vocabulary (`use/classes/functions/values/methods`, exact\n * tokens) decides what is replaced — controllers included, since the router\n * resolves them through the same root. Controllers, middleware and views are\n * auto-bound from the config; the app composes its backend through the\n * optional `main.testing.compose` hook and mounts pages through\n * `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 type { IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport { testIoc, TestIocBuilder, ENV_REGISTRY } from '@noego/testing';\nimport type { ConfigMap, MethodsConfig, TestEnvironment, UseInput } from '@noego/testing';\nimport { WoodRouter } from '../router/wood_router.cjs';\nimport type { OperationDef } 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 type { RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError } 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 resolveSelection,\n selectCatalog,\n selectOperations,\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';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\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 /** 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. */\n get<T>(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\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly ioc: TestIocBuilder;\n readonly importer: ModuleImporter;\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: '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\n/** A router over the selected aperture with every controller auto-bound from the config. */\nfunction buildRouter(\n root: IContainer,\n source: WoodConfigSource,\n operations: Map<string, OperationDef>,\n importer: ModuleImporter,\n): WoodRouter {\n const router = new WoodRouter({\n container: root,\n controllersDir: source.mainControllersDir,\n middlewareDir: source.mainMiddlewareDir,\n mode: 'dev',\n });\n const bound = new Set<string>();\n for (const operation of operations.values()) {\n if (bound.has(operation.controller)) continue;\n bound.add(operation.controller);\n const file = source.mainControllerFile(operation.controller);\n router.registerControllerLoader(operation.controller, () => importer(file));\n }\n router.loadOperations(operations);\n router.validateControllers();\n return router;\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\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selection: ResolvedWoodSelection,\n): Promise<TestWoodEnvironment> {\n const source = loadWoodConfigSource(state.configPath);\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selection.pages ? selectCatalog(source.views(), selection.pages) : null;\n\n const registryBefore = globalRegistrySize();\n let composed: WoodComposeResult | null = null;\n let renderer: RendererTier | null = null;\n const controllerScopes: Array<{ dispose(): Promise<void> }> = [];\n let router: WoodRouter | null = null;\n const context: WoodComposeContext = {\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 (!router) throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n const live = router;\n return (await ExecutionContext.run(env.root as never, () => live.dispatch(operation, input))) as T;\n },\n };\n\n // Production composition lands on the fresh root FIRST; the test's\n // substitutions are applied on top of it by the ioc builder, so a seam\n // always shadows what the app registered for the same token.\n const env: TestEnvironment = await state.ioc.build({\n prepare: async (root) => {\n composed = await runCompose(root, source, context);\n },\n });\n const root = env.root;\n\n const dispose = async (): Promise<void> => {\n if (renderer) await renderer.dispose();\n for (const scope of controllerScopes.reverse()) await scope.dispose();\n await composed?.drain?.();\n try {\n await disposeRoot(env);\n } finally {\n await composed?.dispose?.();\n }\n };\n\n try {\n router = buildRouter(root, source, operations, state.importer);\n await ExecutionContext.run(root as never, () => composed?.start?.());\n assertNoGlobalContainerLeak(registryBefore, source);\n if (catalog) {\n renderer = await buildRendererTier({\n source,\n router,\n catalog,\n operations,\n importer: state.importer,\n hooks: composed,\n dom: selection.dom,\n });\n }\n return environmentSurface({ root, env, router, operations, renderer, controllerScopes, dispose });\n } catch (error) {\n await dispose().catch(() => {});\n throw error;\n }\n}\n\ninterface SurfaceParts {\n root: IContainer;\n env: TestEnvironment;\n router: WoodRouter;\n operations: Map<string, OperationDef>;\n renderer: RendererTier | null;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n dispose(): Promise<void>;\n}\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, router, operations, renderer, controllerScopes, dispose } = parts;\n const dom = (): WoodDomDriver => renderer?.dom ?? tierMissing('dom', 'dom driver');\n const pages = (): RendererTier => renderer ?? tierMissing('pages', 'renderer');\n\n return {\n root,\n router,\n operations,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\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 return (await ExecutionContext.run(root as never, () => router.dispatch(operation, input))) as T;\n },\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n const scope = root.extend();\n controllerScopes.push(scope as unknown as { dispose(): Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n return (await scope.get(controller)) as T;\n },\n get: async <T>(token: unknown): Promise<T> => (await root.get(token)) as T,\n\n get frontend() { return pages().frontend; },\n get events() { return pages().events; },\n get rendererRoot() { return pages().rendererRoot; },\n open: async (page, params, query) => pages().open(page, params, query),\n navigate: async (page, params, query) => pages().navigate(page, params, query),\n current: () => pages().current(),\n settle: async () => pages().settle(),\n\n get target() { return dom().target; },\n click: async (selector) => dom().click(selector),\n type: async (selector, text) => dom().type(selector, text),\n submit: async (selector) => dom().submit(selector),\n text: async (selector) => dom().text(selector),\n exists: async (selector) => 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: () => env.verify(),\n dispose,\n [ENV_REGISTRY]: (env as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n\nexport class TestWoodBuilder {\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 ioc: testIoc(),\n importer: defaultImporter,\n });\n }\n\n /** Declare which tiers exist: `main`, `pages`, `dom`. Replaces any earlier selection. */\n select(selection: WoodTestSelection): TestWoodBuilder {\n resolveSelection(selection);\n return this.derive({ selection });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /* ---- Composition: exact @noego/testing semantics on the main root ---- */\n\n use(preset: UseInput): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.use(preset) });\n }\n\n classes(config: ConfigMap<new (...args: any[]) => unknown>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.classes(config) });\n }\n\n functions(config: ConfigMap<(...args: any[]) => any>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.functions(config) });\n }\n\n values(config: ConfigMap<unknown>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.values(config) });\n }\n\n methods(config: MethodsConfig): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.methods(config) });\n }\n\n async build(): Promise<TestWoodEnvironment> {\n if (!this.state.selection) {\n throw new TestWoodError('select() a tier before build(): { main }, { pages }, or both');\n }\n return assembleEnvironment(this.state, resolveSelection(this.state.selection));\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder {\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":["root","path"]}
1
+ {"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["import { disposeControllerLifecycle, guardControllerLifecycleOnce } from '../controller/controller_lifecycle.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' }, pages: ['settings.backend-service'] })\n * .classes([[BackendServiceLifecycleCoordinator, LifecycleStub]])\n * .methods([[LifecycleStub, { getStatus: test.once(test.returns(status)) }]])\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * `@noego/testing` vocabulary (`use/classes/functions/values/methods`, exact\n * tokens) decides what is replaced — controllers included, since the router\n * resolves them through the same root. Controllers, middleware and views are\n * auto-bound from the config; the app composes its backend through the\n * optional `main.testing.compose` hook and mounts pages through\n * `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 type { IContainer } from '@noego/ioc';\nimport { ExecutionContext, flattenModule } from '@noego/ioc';\nimport { testIoc, TestIocBuilder, ENV_REGISTRY } from '@noego/testing';\nimport type { ConfigMap, MethodsConfig, TestEnvironment, UseInput } from '@noego/testing';\nimport { installWoodRuntime, WoodRouter } from '../runtime/runtime_module.js';\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 type { 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 resolveSelection,\n selectCatalog,\n selectOperations,\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';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\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 /** 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. */\n get<T>(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 IocMethod = 'use' | 'classes' | 'functions' | 'values' | 'methods';\ntype IocCall = { method: IocMethod; args: unknown[]; flat: boolean; order: number };\ntype ControllerClass = new (...args: never[]) => unknown;\n\n/** Recorder used by explicit .main() and .renderer() owner targeting. */\nexport interface WoodRootComposer {\n use(preset: UseInput): WoodRootComposer;\n classes(config: ConfigMap<new (...args: any[]) => unknown>): WoodRootComposer;\n functions(config: ConfigMap<(...args: any[]) => any>): WoodRootComposer;\n values(config: ConfigMap<unknown>): WoodRootComposer;\n methods(config: MethodsConfig): WoodRootComposer;\n}\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly mainCalls: readonly IocCall[];\n readonly rendererCalls: readonly IocCall[];\n readonly importer: ModuleImporter;\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: '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\nfunction recordComposer(compose: (root: WoodRootComposer) => unknown): Omit<IocCall, 'order'>[] {\n if (typeof compose !== 'function') throw new TestWoodError('owner composition expects a callback');\n const calls: Omit<IocCall, 'order'>[] = [];\n const root: WoodRootComposer = {\n use: (preset) => { calls.push({ method: 'use', args: [preset], flat: false }); return root; },\n classes: (config) => { calls.push({ method: 'classes', args: [config], flat: false }); return root; },\n functions: (config) => { calls.push({ method: 'functions', args: [config], flat: false }); return root; },\n values: (config) => { calls.push({ method: 'values', args: [config], flat: false }); return root; },\n methods: (config) => { calls.push({ method: 'methods', args: [config], flat: false }); return root; },\n };\n compose(root);\n return calls;\n}\n\nfunction applyCalls(builder: TestIocBuilder, calls: readonly IocCall[]): TestIocBuilder {\n let next = builder;\n for (const call of [...calls].sort((a, b) => a.order - b.order)) {\n next = (next as unknown as Record<IocMethod, (...args: unknown[]) => TestIocBuilder>)[call.method](...call.args);\n }\n return next;\n}\n\n/** Discover ownership from declarations only; constructors/factories never run. */\nfunction ownerTokens(builder: TestIocBuilder): Set<unknown> {\n const tokens = new Set<unknown>();\n const visit = (token: unknown): void => {\n if (tokens.has(token)) return;\n tokens.add(token);\n if (typeof token !== 'function') return;\n const injected = (Reflect as any).getMetadata?.(Symbol.for('ioc:inject:tokens'), token) as Map<number, unknown> | undefined;\n const design = (Reflect as any).getMetadata?.('design:paramtypes', token) ?? [];\n const indices = new Set<number>([...design.keys(), ...(injected?.keys() ?? [])]);\n for (const index of indices) visit(injected?.has(index) ? injected.get(index) : design[index]);\n };\n for (const write of builder.writes) {\n if (write.op === 'use') {\n for (const registration of flattenModule(write.module)) {\n visit(registration.token);\n if (registration.kind === 'class') visit(registration.implementation);\n for (const dependency of registration.dependencies) visit(dependency);\n }\n } else if (write.op !== 'methods') {\n visit(write.key);\n if (write.op === 'classes') visit(write.implementation);\n }\n }\n return tokens;\n}\n\nfunction tokenLabel(token: unknown): string {\n if (typeof token === 'function' && token.name) return token.name;\n return String(token);\n}\n\nfunction callEntries(call: IocCall): Array<[unknown, unknown]> {\n // Calling the shared builder performs canonical tuple/Map/object validation\n // and descriptor normalization; this helper only splits owner routing.\n applyCalls(testIoc(), [{ ...call, order: 0 }]);\n const config = call.args[0] as any;\n if (Array.isArray(config) || config instanceof Map) return [...config];\n return Reflect.ownKeys(config).map((key) => [key, config[key]]);\n}\n\nfunction resolveOwnerToken(\n key: unknown,\n exact: boolean,\n main: Set<unknown>,\n renderer: Set<unknown>,\n method: IocMethod,\n): { token: unknown; owner: 'main' | 'renderer' } {\n let token = key;\n if (!exact && typeof key === 'string') {\n const matches = [...new Set([...main, ...renderer])].filter(\n (candidate) => candidate === key || (typeof candidate === 'function' && candidate.name === key),\n );\n if (matches.length > 1) {\n throw new TestWoodError(`Ambiguous ${method} replacement name \"${key}\"; use an exact token`);\n }\n token = matches[0] ?? key;\n }\n const ownsMain = main.has(token);\n const ownsRenderer = renderer.has(token);\n if (!ownsMain && !ownsRenderer) {\n throw new TestWoodError(\n `Unknown ${method} replacement ${tokenLabel(token)} in the selected roots; ` +\n 'register it with .main() or .renderer()',\n );\n }\n if (ownsMain && ownsRenderer) {\n throw new TestWoodError(\n `Ambiguous ${method} replacement ${tokenLabel(token)} belongs to main and renderer; ` +\n 'target .main() or .renderer() explicitly',\n );\n }\n return { token, owner: ownsMain ? 'main' : 'renderer' };\n}\n\nfunction routeCalls(\n calls: readonly IocCall[],\n main: Set<unknown>,\n renderer: Set<unknown>,\n target: 'main' | 'renderer',\n): IocCall[] {\n // Replacement implementations become registrations too. Predeclare their\n // owner so .methods([[Replacement, ...]]) is order-independent.\n for (const call of calls) {\n if (!call.flat || call.method !== 'classes') continue;\n const exact = Array.isArray(call.args[0]) || call.args[0] instanceof Map;\n for (const [key, implementation] of callEntries(call)) {\n const routed = resolveOwnerToken(key, exact || typeof key !== 'string', main, renderer, call.method);\n (routed.owner === 'main' ? main : renderer).add(implementation);\n }\n }\n\n const result: IocCall[] = [];\n for (const call of calls) {\n if (!call.flat) {\n if (target === 'main') result.push(call);\n continue;\n }\n const exact = Array.isArray(call.args[0]) || call.args[0] instanceof Map;\n for (const [key, value] of callEntries(call)) {\n const routed = resolveOwnerToken(key, exact || typeof key !== 'string', main, renderer, call.method);\n if (routed.owner === target) {\n result.push({ ...call, flat: false, args: [[[routed.token, value]]] });\n }\n }\n }\n return result;\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 = ReturnType<typeof selectCatalog>;\ntype ControllerScope = { dispose(): Promise<void> };\n\ninterface AssemblyLiveState {\n composed: WoodComposeResult | null;\n env: TestEnvironment | null;\n renderer: RendererTier | null;\n router: WoodRouter | null;\n}\n\ninterface AssemblyInputs {\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 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.router || !live.env) {\n throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n }\n const router = live.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): () => Promise<void> {\n let closing: Promise<void> | null = null;\n return (): Promise<void> => closing ??= cleanupAll('testWood cleanup failed', [\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 () => { await live.composed?.drain?.(); }],\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 ]);\n}\n\nasync function loadAssemblyControllers(\n inputs: AssemblyInputs,\n): Promise<{\n main: Map<string, ControllerClass>;\n renderer: Map<string, ControllerClass>;\n}> {\n const main = await loadMainControllers(inputs.source, inputs.operations, inputs.state.importer);\n const renderer = inputs.catalog\n ? await loadRendererControllers(inputs.source, inputs.catalog.all(), inputs.state.importer)\n : new Map<string, ControllerClass>();\n return { main, renderer };\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 },\n });\n}\n\nasync function configureRendererRegistrations(inputs: AssemblyInputs): Promise<void> {\n const registrations = inputs.rendererRegistrations;\n if (!registrations) return;\n registrations.declare(Navigation);\n registrations.declare(WOOD_FRONTEND_BRIDGE);\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\nfunction buildAssemblyIoc(\n inputs: AssemblyInputs,\n): { main: TestIocBuilder; renderer: TestIocBuilder } {\n const explicitMain = inputs.state.mainCalls.filter((call) => !call.flat);\n const mainOwners = new Set([\n ...inputs.mainRegistrations.owners,\n ...ownerTokens(applyCalls(testIoc(), explicitMain)),\n ]);\n const rendererOwners = new Set([\n ...(inputs.rendererRegistrations?.owners ?? []),\n ...ownerTokens(applyCalls(testIoc(), inputs.state.rendererCalls)),\n ]);\n const routedMain = routeCalls(inputs.state.mainCalls, mainOwners, rendererOwners, 'main');\n const routedRenderer = routeCalls(inputs.state.mainCalls, mainOwners, rendererOwners, 'renderer');\n return {\n main: applyCalls(testIoc(), routedMain),\n renderer: applyCalls(testIoc(), [...inputs.state.rendererCalls, ...routedRenderer]),\n };\n}\n\nasync function materializeEnvironment(inputs: AssemblyInputs): Promise<TestWoodEnvironment> {\n const controllers = await loadAssemblyControllers(inputs);\n inputs.live.composed = await runCompose(inputs.mainRegistrations.root, inputs.source, inputs.context);\n installAssemblyRuntime(inputs, controllers.main);\n await configureRendererRegistrations(inputs);\n registerControllersBeforeOverlays(inputs, controllers.main, controllers.renderer);\n const ioc = buildAssemblyIoc(inputs);\n\n inputs.live.env = await buildRecordedEnvironment(ioc.main, inputs.mainRegistrations, 'main');\n const root = inputs.live.env.root;\n inputs.live.router = await Promise.resolve(\n ExecutionContext.run(root as never, () => root.get(WoodRouter)),\n ) as WoodRouter;\n await ExecutionContext.run(root as never, () => inputs.live.composed?.start?.());\n assertNoGlobalContainerLeak(inputs.registryBefore, inputs.source);\n\n if (inputs.catalog && inputs.rendererRegistrations) {\n inputs.live.renderer = await buildRendererTier({\n source: inputs.source,\n router: inputs.live.router,\n catalog: inputs.catalog,\n operations: inputs.operations,\n importer: inputs.state.importer,\n hooks: inputs.live.composed,\n dom: inputs.selection.dom,\n ioc: ioc.renderer,\n registrations: inputs.rendererRegistrations,\n controllerClasses: controllers.renderer,\n });\n }\n return environmentSurface({\n root,\n env: inputs.live.env,\n router: inputs.live.router,\n operations: inputs.operations,\n renderer: inputs.live.renderer,\n controllerScopes: inputs.controllerScopes,\n dispose: inputs.dispose,\n });\n}\n\nasync function rollbackEnvironment(error: unknown, dispose: () => Promise<void>): 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('testWood build failed and rollback failed', 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 selection: ResolvedWoodSelection,\n): Promise<TestWoodEnvironment> {\n const source = loadWoodConfigSource(state.configPath);\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selection.pages ? selectCatalog(source.views(), selection.pages) : null;\n if (!catalog && state.rendererCalls.length) {\n throw new TestWoodError('.renderer() composition requires a selected pages tier');\n }\n\n const registryBefore = globalRegistrySize();\n const mainRegistrations = new RegistrationOnlyRoot('main.testing.compose');\n const rendererRegistrations = catalog\n ? new RegistrationOnlyRoot('configureRendererRoot')\n : null;\n const controllerScopes: ControllerScope[] = [];\n const live: AssemblyLiveState = {\n composed: null,\n env: null,\n renderer: null,\n router: null,\n };\n const context = createComposeContext(source, state, selection, operations, live);\n const dispose = createAssemblyDisposer(\n live,\n mainRegistrations,\n rendererRegistrations,\n controllerScopes,\n );\n const inputs: AssemblyInputs = {\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 root: IContainer;\n env: TestEnvironment;\n router: WoodRouter;\n operations: Map<string, OperationDef>;\n renderer: RendererTier | null;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n dispose(): Promise<void>;\n}\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, router, operations, renderer, controllerScopes, dispose } = parts;\n const dom = (): WoodDomDriver => renderer?.dom ?? tierMissing('dom', 'dom driver');\n const pages = (): RendererTier => renderer ?? tierMissing('pages', 'renderer');\n\n return {\n root,\n router,\n operations,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\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 return (await ExecutionContext.run(root as never, () => router.dispatch(operation, input))) as T;\n },\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\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> => (await root.get(token)) as T,\n\n get frontend() { return pages().frontend; },\n get events() { return pages().events; },\n get rendererRoot() { return pages().rendererRoot; },\n open: async (page, params, query) => pages().open(page, params, query),\n navigate: async (page, params, query) => pages().navigate(page, params, query),\n current: () => pages().current(),\n settle: async () => pages().settle(),\n\n get target() { return dom().target; },\n click: async (selector) => dom().click(selector),\n type: async (selector, text) => dom().type(selector, text),\n submit: async (selector) => dom().submit(selector),\n text: async (selector) => dom().text(selector),\n exists: async (selector) => 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 if (renderer) {\n await renderer.verify().catch((error) => failures.push({ owner: 'renderer', error }));\n }\n if (failures.length === 1) throw failures[0]!.error;\n if (failures.length) {\n throw new TestWoodError(\n 'testWood verification failed in both roots:\\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 {\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 mainCalls: [],\n rendererCalls: [],\n importer: defaultImporter,\n });\n }\n\n /** Declare which tiers exist: `main`, `pages`, `dom`. Replaces any earlier selection. */\n select(selection: WoodTestSelection): TestWoodBuilder {\n resolveSelection(selection);\n return this.derive({ selection });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /** Explicitly compose or replace dependencies on the main-process root. */\n main(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder {\n return this.append('main', recordComposer(compose));\n }\n\n /** Explicitly compose or replace dependencies on the renderer root. */\n renderer(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder {\n return this.append('renderer', recordComposer(compose));\n }\n\n /** Reusable presets retain the historical main-root default. */\n use(preset: UseInput): TestWoodBuilder {\n return this.append('main', [{ method: 'use', args: [preset], flat: false }]);\n }\n\n /** Exact-token replacement routed to the sole declared selected owner. */\n classes(config: ConfigMap<new (...args: any[]) => unknown>): TestWoodBuilder {\n return this.append('main', [{ method: 'classes', args: [config], flat: true }]);\n }\n\n /** Exact-token replacement routed to the sole declared selected owner. */\n functions(config: ConfigMap<(...args: any[]) => any>): TestWoodBuilder {\n return this.append('main', [{ method: 'functions', args: [config], flat: true }]);\n }\n\n /** Exact-token replacement routed to the sole declared selected owner. */\n values(config: ConfigMap<unknown>): TestWoodBuilder {\n return this.append('main', [{ method: 'values', args: [config], flat: true }]);\n }\n\n /** Exact-token method seams routed to the sole declared selected owner. */\n methods(config: MethodsConfig): TestWoodBuilder {\n return this.append('main', [{ method: 'methods', args: [config], flat: true }]);\n }\n\n async build(): Promise<TestWoodEnvironment> {\n if (!this.state.selection) {\n throw new TestWoodError('select() a tier before build(): { main }, { pages }, or both');\n }\n return assembleEnvironment(this.state, resolveSelection(this.state.selection));\n }\n\n private append(\n owner: 'main' | 'renderer',\n calls: ReadonlyArray<Omit<IocCall, 'order'>>,\n ): TestWoodBuilder {\n const start = this.state.mainCalls.length + this.state.rendererCalls.length;\n const ordered = calls.map((call, index) => ({ ...call, order: start + index }));\n // Preserve @noego/testing's call-time validation and descriptor semantics.\n applyCalls(testIoc(), ordered);\n return owner === 'main'\n ? this.derive({ mainCalls: [...this.state.mainCalls, ...ordered] })\n : this.derive({ rendererCalls: [...this.state.rendererCalls, ...ordered] });\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder {\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":["path"]}
@@ -10,12 +10,18 @@ import { ModuleImporter } from './wood_config_source.cjs';
10
10
  import { WoodTestSelection, ResolvedWoodSelection } from './wood_test_selection.cjs';
11
11
  import { WoodRendererHooks } from './wood_env_renderer.cjs';
12
12
  import '../types/context.cjs';
13
+ import '../validation/schema_validator.cjs';
14
+ import '../middleware/middleware_resolver.cjs';
15
+ import '../controller/controller_resolver.cjs';
13
16
  import '../navigation/index.cjs';
14
17
  import '../navigation/automation_introspection.cjs';
15
18
  import '../types/views.cjs';
16
19
  import '../frontend/page_catalog.cjs';
17
20
  import '../frontend/work_registry.cjs';
18
21
  import '../frontend/frontend_lifecycle.cjs';
22
+ import './wood_dom_animation.cjs';
23
+ import './registration_only_root.cjs';
24
+ import './test_wood.cjs';
19
25
 
20
26
  /** What the app's `main.testing.compose` default export receives. */
21
27
  interface WoodComposeContext {
@@ -90,6 +96,14 @@ interface TestWoodEnvironment {
90
96
  dispose(): Promise<void>;
91
97
  readonly [ENV_REGISTRY]: unknown;
92
98
  }
99
+ /** Recorder used by explicit .main() and .renderer() owner targeting. */
100
+ interface WoodRootComposer {
101
+ use(preset: UseInput): WoodRootComposer;
102
+ classes(config: ConfigMap<new (...args: any[]) => unknown>): WoodRootComposer;
103
+ functions(config: ConfigMap<(...args: any[]) => any>): WoodRootComposer;
104
+ values(config: ConfigMap<unknown>): WoodRootComposer;
105
+ methods(config: MethodsConfig): WoodRootComposer;
106
+ }
93
107
  declare class TestWoodBuilder {
94
108
  private readonly state;
95
109
  private constructor();
@@ -98,14 +112,24 @@ declare class TestWoodBuilder {
98
112
  select(selection: WoodTestSelection): TestWoodBuilder;
99
113
  /** Route hook/controller imports through a caller-owned importer (escape hatch). */
100
114
  importer(importer: ModuleImporter): TestWoodBuilder;
115
+ /** Explicitly compose or replace dependencies on the main-process root. */
116
+ main(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder;
117
+ /** Explicitly compose or replace dependencies on the renderer root. */
118
+ renderer(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder;
119
+ /** Reusable presets retain the historical main-root default. */
101
120
  use(preset: UseInput): TestWoodBuilder;
121
+ /** Exact-token replacement routed to the sole declared selected owner. */
102
122
  classes(config: ConfigMap<new (...args: any[]) => unknown>): TestWoodBuilder;
123
+ /** Exact-token replacement routed to the sole declared selected owner. */
103
124
  functions(config: ConfigMap<(...args: any[]) => any>): TestWoodBuilder;
125
+ /** Exact-token replacement routed to the sole declared selected owner. */
104
126
  values(config: ConfigMap<unknown>): TestWoodBuilder;
127
+ /** Exact-token method seams routed to the sole declared selected owner. */
105
128
  methods(config: MethodsConfig): TestWoodBuilder;
106
129
  build(): Promise<TestWoodEnvironment>;
130
+ private append;
107
131
  private derive;
108
132
  }
109
133
  declare function testWood(configPath: string): TestWoodBuilder;
110
134
 
111
- export { TestWoodBuilder, type TestWoodEnvironment, type WoodComposeContext, type WoodComposeMain, type WoodComposeResult, testWood };
135
+ export { TestWoodBuilder, type TestWoodEnvironment, type WoodComposeContext, type WoodComposeMain, type WoodComposeResult, type WoodRootComposer, testWood };
@@ -10,12 +10,18 @@ import { ModuleImporter } from './wood_config_source.js';
10
10
  import { WoodTestSelection, ResolvedWoodSelection } from './wood_test_selection.js';
11
11
  import { WoodRendererHooks } from './wood_env_renderer.js';
12
12
  import '../types/context.js';
13
+ import '../validation/schema_validator.js';
14
+ import '../middleware/middleware_resolver.js';
15
+ import '../controller/controller_resolver.js';
13
16
  import '../navigation/index.js';
14
17
  import '../navigation/automation_introspection.js';
15
18
  import '../types/views.js';
16
19
  import '../frontend/page_catalog.js';
17
20
  import '../frontend/work_registry.js';
18
21
  import '../frontend/frontend_lifecycle.js';
22
+ import './wood_dom_animation.js';
23
+ import './registration_only_root.js';
24
+ import './test_wood.js';
19
25
 
20
26
  /** What the app's `main.testing.compose` default export receives. */
21
27
  interface WoodComposeContext {
@@ -90,6 +96,14 @@ interface TestWoodEnvironment {
90
96
  dispose(): Promise<void>;
91
97
  readonly [ENV_REGISTRY]: unknown;
92
98
  }
99
+ /** Recorder used by explicit .main() and .renderer() owner targeting. */
100
+ interface WoodRootComposer {
101
+ use(preset: UseInput): WoodRootComposer;
102
+ classes(config: ConfigMap<new (...args: any[]) => unknown>): WoodRootComposer;
103
+ functions(config: ConfigMap<(...args: any[]) => any>): WoodRootComposer;
104
+ values(config: ConfigMap<unknown>): WoodRootComposer;
105
+ methods(config: MethodsConfig): WoodRootComposer;
106
+ }
93
107
  declare class TestWoodBuilder {
94
108
  private readonly state;
95
109
  private constructor();
@@ -98,14 +112,24 @@ declare class TestWoodBuilder {
98
112
  select(selection: WoodTestSelection): TestWoodBuilder;
99
113
  /** Route hook/controller imports through a caller-owned importer (escape hatch). */
100
114
  importer(importer: ModuleImporter): TestWoodBuilder;
115
+ /** Explicitly compose or replace dependencies on the main-process root. */
116
+ main(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder;
117
+ /** Explicitly compose or replace dependencies on the renderer root. */
118
+ renderer(compose: (root: WoodRootComposer) => unknown): TestWoodBuilder;
119
+ /** Reusable presets retain the historical main-root default. */
101
120
  use(preset: UseInput): TestWoodBuilder;
121
+ /** Exact-token replacement routed to the sole declared selected owner. */
102
122
  classes(config: ConfigMap<new (...args: any[]) => unknown>): TestWoodBuilder;
123
+ /** Exact-token replacement routed to the sole declared selected owner. */
103
124
  functions(config: ConfigMap<(...args: any[]) => any>): TestWoodBuilder;
125
+ /** Exact-token replacement routed to the sole declared selected owner. */
104
126
  values(config: ConfigMap<unknown>): TestWoodBuilder;
127
+ /** Exact-token method seams routed to the sole declared selected owner. */
105
128
  methods(config: MethodsConfig): TestWoodBuilder;
106
129
  build(): Promise<TestWoodEnvironment>;
130
+ private append;
107
131
  private derive;
108
132
  }
109
133
  declare function testWood(configPath: string): TestWoodBuilder;
110
134
 
111
- export { TestWoodBuilder, type TestWoodEnvironment, type WoodComposeContext, type WoodComposeMain, type WoodComposeResult, testWood };
135
+ export { TestWoodBuilder, type TestWoodEnvironment, type WoodComposeContext, type WoodComposeMain, type WoodComposeResult, type WoodRootComposer, testWood };