@noego/wood 0.6.1 → 0.7.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 (247) hide show
  1. package/README.md +7 -0
  2. package/dist/browser-test-setup.cjs +29 -0
  3. package/dist/browser-test-setup.cjs.map +1 -0
  4. package/dist/browser-test-setup.d.cts +8 -0
  5. package/dist/browser-test-setup.d.ts +8 -0
  6. package/dist/browser-test-setup.js +9 -0
  7. package/dist/browser-test-setup.js.map +1 -0
  8. package/dist/client/global_polyfill.d.cts +1 -0
  9. package/dist/client/global_polyfill.d.ts +1 -0
  10. package/dist/client/index.cjs +7 -0
  11. package/dist/client/index.cjs.map +1 -1
  12. package/dist/client/index.d.cts +8 -4
  13. package/dist/client/index.d.ts +8 -4
  14. package/dist/client/index.js +6 -1
  15. package/dist/client/index.js.map +1 -1
  16. package/dist/codegen/renderer_app_generator.cjs +97 -24
  17. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  18. package/dist/codegen/renderer_app_generator.js +97 -24
  19. package/dist/codegen/renderer_app_generator.js.map +1 -1
  20. package/dist/codegen/type_generator.cjs +9 -0
  21. package/dist/codegen/type_generator.cjs.map +1 -1
  22. package/dist/codegen/type_generator.js +9 -0
  23. package/dist/codegen/type_generator.js.map +1 -1
  24. package/dist/config/config_parser.cjs +10 -0
  25. package/dist/config/config_parser.cjs.map +1 -1
  26. package/dist/config/config_parser.js +10 -0
  27. package/dist/config/config_parser.js.map +1 -1
  28. package/dist/fetch.cjs +38 -0
  29. package/dist/fetch.cjs.map +1 -0
  30. package/dist/fetch.d.cts +7 -0
  31. package/dist/fetch.d.ts +7 -0
  32. package/dist/fetch.js +12 -0
  33. package/dist/fetch.js.map +1 -0
  34. package/dist/frontend/frontend_page.cjs +3 -3
  35. package/dist/frontend/frontend_page.cjs.map +1 -1
  36. package/dist/frontend/frontend_page.d.cts +1 -1
  37. package/dist/frontend/frontend_page.d.ts +1 -1
  38. package/dist/frontend/frontend_page.js +3 -3
  39. package/dist/frontend/frontend_page.js.map +1 -1
  40. package/dist/frontend/frontend_surface.cjs +31 -17
  41. package/dist/frontend/frontend_surface.cjs.map +1 -1
  42. package/dist/frontend/frontend_surface.d.cts +13 -2
  43. package/dist/frontend/frontend_surface.d.ts +13 -2
  44. package/dist/frontend/frontend_surface.js +37 -17
  45. package/dist/frontend/frontend_surface.js.map +1 -1
  46. package/dist/frontend/index.d.cts +3 -0
  47. package/dist/frontend/index.d.ts +3 -0
  48. package/dist/index.cjs +5 -0
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +27 -1
  51. package/dist/index.d.ts +27 -1
  52. package/dist/index.js +3 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/loader/index.cjs +19 -2
  55. package/dist/loader/index.cjs.map +1 -1
  56. package/dist/loader/index.d.cts +1 -0
  57. package/dist/loader/index.d.ts +1 -0
  58. package/dist/loader/index.js +19 -1
  59. package/dist/loader/index.js.map +1 -1
  60. package/dist/loader/loader_orchestration.cjs +82 -0
  61. package/dist/loader/loader_orchestration.cjs.map +1 -0
  62. package/dist/loader/loader_orchestration.d.cts +66 -0
  63. package/dist/loader/loader_orchestration.d.ts +66 -0
  64. package/dist/loader/loader_orchestration.js +51 -0
  65. package/dist/loader/loader_orchestration.js.map +1 -0
  66. package/dist/loader/loader_runner.cjs +46 -41
  67. package/dist/loader/loader_runner.cjs.map +1 -1
  68. package/dist/loader/loader_runner.d.cts +12 -6
  69. package/dist/loader/loader_runner.d.ts +12 -6
  70. package/dist/loader/loader_runner.js +50 -41
  71. package/dist/loader/loader_runner.js.map +1 -1
  72. package/dist/native_browser.types-BY2daUOS.d.ts +189 -0
  73. package/dist/native_browser.types-vWXtE4rM.d.cts +189 -0
  74. package/dist/navigation/index.cjs +15 -2
  75. package/dist/navigation/index.cjs.map +1 -1
  76. package/dist/navigation/index.d.cts +6 -28
  77. package/dist/navigation/index.d.ts +6 -28
  78. package/dist/navigation/index.js +8 -1
  79. package/dist/navigation/index.js.map +1 -1
  80. package/dist/navigation/layout_preservation.cjs.map +1 -1
  81. package/dist/navigation/layout_preservation.d.cts +27 -2
  82. package/dist/navigation/layout_preservation.d.ts +27 -2
  83. package/dist/navigation/layout_preservation.js.map +1 -1
  84. package/dist/runtime/get_container.cjs +2 -0
  85. package/dist/runtime/get_container.cjs.map +1 -1
  86. package/dist/runtime/get_container.js +2 -0
  87. package/dist/runtime/get_container.js.map +1 -1
  88. package/dist/runtime/index.cjs +5 -0
  89. package/dist/runtime/index.cjs.map +1 -1
  90. package/dist/runtime/index.d.cts +2 -0
  91. package/dist/runtime/index.d.ts +2 -0
  92. package/dist/runtime/index.js +3 -0
  93. package/dist/runtime/index.js.map +1 -1
  94. package/dist/runtime/runtime_capabilities.cjs +35 -0
  95. package/dist/runtime/runtime_capabilities.cjs.map +1 -0
  96. package/dist/runtime/runtime_capabilities.d.cts +6 -0
  97. package/dist/runtime/runtime_capabilities.d.ts +6 -0
  98. package/dist/runtime/runtime_capabilities.js +11 -0
  99. package/dist/runtime/runtime_capabilities.js.map +1 -0
  100. package/dist/runtime/runtime_module.cjs +2 -0
  101. package/dist/runtime/runtime_module.cjs.map +1 -1
  102. package/dist/runtime/runtime_module.js +2 -0
  103. package/dist/runtime/runtime_module.js.map +1 -1
  104. package/dist/test-wood.cjs +150 -0
  105. package/dist/test-wood.cjs.map +1 -0
  106. package/dist/test-wood.d.cts +62 -0
  107. package/dist/test-wood.d.ts +62 -0
  108. package/dist/test-wood.js +115 -0
  109. package/dist/test-wood.js.map +1 -0
  110. package/dist/testing/browser.cjs +92 -163
  111. package/dist/testing/browser.cjs.map +1 -1
  112. package/dist/testing/browser.d.cts +14 -13
  113. package/dist/testing/browser.d.ts +14 -13
  114. package/dist/testing/browser.js +88 -162
  115. package/dist/testing/browser.js.map +1 -1
  116. package/dist/testing/create_controller_harness.d.cts +3 -1
  117. package/dist/testing/create_controller_harness.d.ts +3 -1
  118. package/dist/testing/create_router_bridge.cjs +19 -15
  119. package/dist/testing/create_router_bridge.cjs.map +1 -1
  120. package/dist/testing/create_router_bridge.d.cts +8 -1
  121. package/dist/testing/create_router_bridge.d.ts +8 -1
  122. package/dist/testing/create_router_bridge.js +18 -15
  123. package/dist/testing/create_router_bridge.js.map +1 -1
  124. package/dist/testing/index.cjs +12 -4
  125. package/dist/testing/index.cjs.map +1 -1
  126. package/dist/testing/index.d.cts +12 -5
  127. package/dist/testing/index.d.ts +12 -5
  128. package/dist/testing/index.js +9 -3
  129. package/dist/testing/index.js.map +1 -1
  130. package/dist/testing/native_browser/browser_pool.cjs +31 -0
  131. package/dist/testing/native_browser/browser_pool.cjs.map +1 -0
  132. package/dist/testing/native_browser/browser_pool.d.cts +1 -0
  133. package/dist/testing/native_browser/browser_pool.d.ts +1 -0
  134. package/dist/testing/native_browser/browser_pool.js +6 -0
  135. package/dist/testing/native_browser/browser_pool.js.map +1 -0
  136. package/dist/testing/native_browser/happy_dom_tab.cjs +107 -0
  137. package/dist/testing/native_browser/happy_dom_tab.cjs.map +1 -0
  138. package/dist/testing/native_browser/happy_dom_tab.d.cts +35 -0
  139. package/dist/testing/native_browser/happy_dom_tab.d.ts +35 -0
  140. package/dist/testing/native_browser/happy_dom_tab.js +73 -0
  141. package/dist/testing/native_browser/happy_dom_tab.js.map +1 -0
  142. package/dist/testing/native_browser/happy_dom_transfer.cjs +29 -0
  143. package/dist/testing/native_browser/happy_dom_transfer.cjs.map +1 -0
  144. package/dist/testing/native_browser/happy_dom_transfer.d.cts +1 -0
  145. package/dist/testing/native_browser/happy_dom_transfer.d.ts +1 -0
  146. package/dist/testing/native_browser/happy_dom_transfer.js +5 -0
  147. package/dist/testing/native_browser/happy_dom_transfer.js.map +1 -0
  148. package/dist/testing/native_browser/host_transport.cjs +89 -0
  149. package/dist/testing/native_browser/host_transport.cjs.map +1 -0
  150. package/dist/testing/native_browser/host_transport.d.cts +44 -0
  151. package/dist/testing/native_browser/host_transport.d.ts +44 -0
  152. package/dist/testing/native_browser/host_transport.js +65 -0
  153. package/dist/testing/native_browser/host_transport.js.map +1 -0
  154. package/dist/testing/native_browser/native_browser.types.cjs +17 -0
  155. package/dist/testing/native_browser/native_browser.types.cjs.map +1 -0
  156. package/dist/testing/native_browser/native_browser.types.d.cts +31 -0
  157. package/dist/testing/native_browser/native_browser.types.d.ts +31 -0
  158. package/dist/testing/native_browser/native_browser.types.js +1 -0
  159. package/dist/testing/native_browser/native_browser.types.js.map +1 -0
  160. package/dist/testing/native_browser/native_browser_environment.cjs +143 -0
  161. package/dist/testing/native_browser/native_browser_environment.cjs.map +1 -0
  162. package/dist/testing/native_browser/native_browser_environment.d.cts +36 -0
  163. package/dist/testing/native_browser/native_browser_environment.d.ts +36 -0
  164. package/dist/testing/native_browser/native_browser_environment.js +109 -0
  165. package/dist/testing/native_browser/native_browser_environment.js.map +1 -0
  166. package/dist/testing/native_browser/native_browser_lifecycle.cjs +31 -0
  167. package/dist/testing/native_browser/native_browser_lifecycle.cjs.map +1 -0
  168. package/dist/testing/native_browser/native_browser_lifecycle.d.cts +1 -0
  169. package/dist/testing/native_browser/native_browser_lifecycle.d.ts +1 -0
  170. package/dist/testing/native_browser/native_browser_lifecycle.js +6 -0
  171. package/dist/testing/native_browser/native_browser_lifecycle.js.map +1 -0
  172. package/dist/testing/native_browser/playwright_tab.cjs +124 -0
  173. package/dist/testing/native_browser/playwright_tab.cjs.map +1 -0
  174. package/dist/testing/native_browser/playwright_tab.d.cts +35 -0
  175. package/dist/testing/native_browser/playwright_tab.d.ts +35 -0
  176. package/dist/testing/native_browser/playwright_tab.js +100 -0
  177. package/dist/testing/native_browser/playwright_tab.js.map +1 -0
  178. package/dist/testing/native_browser/renderer_compilation.cjs +149 -0
  179. package/dist/testing/native_browser/renderer_compilation.cjs.map +1 -0
  180. package/dist/testing/native_browser/renderer_compilation.d.cts +35 -0
  181. package/dist/testing/native_browser/renderer_compilation.d.ts +35 -0
  182. package/dist/testing/native_browser/renderer_compilation.js +115 -0
  183. package/dist/testing/native_browser/renderer_compilation.js.map +1 -0
  184. package/dist/testing/native_browser/renderer_entry.cjs +148 -0
  185. package/dist/testing/native_browser/renderer_entry.cjs.map +1 -0
  186. package/dist/testing/native_browser/renderer_entry.d.cts +13 -0
  187. package/dist/testing/native_browser/renderer_entry.d.ts +13 -0
  188. package/dist/testing/native_browser/renderer_entry.js +114 -0
  189. package/dist/testing/native_browser/renderer_entry.js.map +1 -0
  190. package/dist/testing/registration_only_root.cjs +61 -5
  191. package/dist/testing/registration_only_root.cjs.map +1 -1
  192. package/dist/testing/registration_only_root.d.cts +25 -4
  193. package/dist/testing/registration_only_root.d.ts +25 -4
  194. package/dist/testing/registration_only_root.js +60 -5
  195. package/dist/testing/registration_only_root.js.map +1 -1
  196. package/dist/testing/runner_importer.cjs +75 -0
  197. package/dist/testing/runner_importer.cjs.map +1 -0
  198. package/dist/testing/runner_importer.d.cts +8 -0
  199. package/dist/testing/runner_importer.d.ts +8 -0
  200. package/dist/testing/runner_importer.js +41 -0
  201. package/dist/testing/runner_importer.js.map +1 -0
  202. package/dist/testing/test_wood_config.cjs +387 -272
  203. package/dist/testing/test_wood_config.cjs.map +1 -1
  204. package/dist/testing/test_wood_config.d.cts +19 -123
  205. package/dist/testing/test_wood_config.d.ts +19 -123
  206. package/dist/testing/test_wood_config.js +396 -270
  207. package/dist/testing/test_wood_config.js.map +1 -1
  208. package/dist/testing/wood_config_source.cjs +12 -0
  209. package/dist/testing/wood_config_source.cjs.map +1 -1
  210. package/dist/testing/wood_config_source.d.cts +10 -0
  211. package/dist/testing/wood_config_source.d.ts +10 -0
  212. package/dist/testing/wood_config_source.js +12 -0
  213. package/dist/testing/wood_config_source.js.map +1 -1
  214. package/dist/testing/wood_env_renderer.cjs +28 -3
  215. package/dist/testing/wood_env_renderer.cjs.map +1 -1
  216. package/dist/testing/wood_env_renderer.d.cts +13 -1
  217. package/dist/testing/wood_env_renderer.d.ts +13 -1
  218. package/dist/testing/wood_env_renderer.js +28 -3
  219. package/dist/testing/wood_env_renderer.js.map +1 -1
  220. package/dist/testing/wood_lazy_runtime.cjs +109 -0
  221. package/dist/testing/wood_lazy_runtime.cjs.map +1 -0
  222. package/dist/testing/wood_lazy_runtime.d.cts +36 -0
  223. package/dist/testing/wood_lazy_runtime.d.ts +36 -0
  224. package/dist/testing/wood_lazy_runtime.js +84 -0
  225. package/dist/testing/wood_lazy_runtime.js.map +1 -0
  226. package/dist/testing/wood_test_selection.cjs +34 -55
  227. package/dist/testing/wood_test_selection.cjs.map +1 -1
  228. package/dist/testing/wood_test_selection.d.cts +18 -24
  229. package/dist/testing/wood_test_selection.d.ts +18 -24
  230. package/dist/testing/wood_test_selection.js +30 -54
  231. package/dist/testing/wood_test_selection.js.map +1 -1
  232. package/dist/testing-api.cjs +17 -0
  233. package/dist/testing-api.cjs.map +1 -0
  234. package/dist/testing-api.d.cts +18 -0
  235. package/dist/testing-api.d.ts +18 -0
  236. package/dist/testing-api.js +1 -0
  237. package/dist/testing-api.js.map +1 -0
  238. package/dist/types/config.cjs.map +1 -1
  239. package/dist/types/config.d.cts +4 -0
  240. package/dist/types/config.d.ts +4 -0
  241. package/docs/native-browser-testing.md +164 -0
  242. package/package.json +31 -5
  243. package/src/components/NavigationShell.browser.test.ts +10 -10
  244. package/src/components/NavigationShell.cleanup.browser.test.ts +57 -0
  245. package/src/components/NavigationShell.scopes.browser.test.ts +5 -5
  246. package/src/components/NavigationShell.svelte +50 -24
  247. package/src/navigation/layout_preservation.ts +3 -1
@@ -28,8 +28,11 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var browser_exports = {};
30
30
  __export(browser_exports, {
31
- browser: () => browser,
31
+ browserAssertions: () => browserAssertions,
32
32
  createBrowserBridgeFixture: () => createBrowserBridgeFixture,
33
+ createBrowserFixture: () => createBrowserFixture,
34
+ createWoodBrowserResolver: () => createWoodBrowserResolver,
35
+ loadSveltePlugin: () => loadSveltePlugin,
33
36
  startSharedBrowserViteServer: () => startSharedBrowserViteServer
34
37
  });
35
38
  module.exports = __toCommonJS(browser_exports);
@@ -40,6 +43,7 @@ var import_node_path = __toESM(require("node:path"), 1);
40
43
  var import_node_url = require("node:url");
41
44
  var import_tracing = require("../tracing/index.js");
42
45
  var import_trace_testing = require("../trace-testing/index.js");
46
+ var import_renderer_app_generator = require("../codegen/renderer_app_generator.cjs");
43
47
  var import_geometry = require("./geometry.js");
44
48
  const DEFAULT_VIEWPORT = {
45
49
  width: 1280,
@@ -97,6 +101,16 @@ async function waitForBoundedClose(promise, timeoutMs = DEFAULT_RESOURCE_CLOSE_T
97
101
  clearTimeout(timeout);
98
102
  }
99
103
  }
104
+ async function closeRequired(promise, owner) {
105
+ let timeout;
106
+ try {
107
+ await Promise.race([promise, new Promise((_, reject) => {
108
+ timeout = setTimeout(() => reject(new Error(`${owner} cleanup timed out`)), DEFAULT_RESOURCE_CLOSE_TIMEOUT_MS);
109
+ })]);
110
+ } finally {
111
+ if (timeout) clearTimeout(timeout);
112
+ }
113
+ }
100
114
  function channelToManifestEntry(channel, handler) {
101
115
  const pathSegments = channel.split(".").filter(Boolean);
102
116
  const action = pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : channel;
@@ -256,6 +270,11 @@ function createWoodBrowserResolver(rootDir) {
256
270
  name: "wood-browser-resolver",
257
271
  enforce: "pre",
258
272
  resolveId(source) {
273
+ if (source.startsWith("@noego/wood/")) {
274
+ const resolved = resolvePackageModule(rootDir, source);
275
+ const esm = resolved?.endsWith(".cjs") ? resolved.replace(/\.cjs$/, ".js") : resolved;
276
+ return esm && import_node_fs.default.existsSync(esm) ? esm : null;
277
+ }
259
278
  if (source === "svelte") {
260
279
  return svelteClientEntry;
261
280
  }
@@ -339,14 +358,14 @@ function workerRuntimeKey(input) {
339
358
  ].join("::");
340
359
  }
341
360
  async function createBrowserWorkerRuntime(input) {
342
- let browser2 = null;
361
+ let browser = null;
343
362
  let cacheRoot = null;
344
363
  let ownedServer = null;
345
364
  try {
346
365
  const playwrightBrowser = await loadPlaywright(input.browserName, input.rootDir);
347
366
  if (input.wsEndpoint) {
348
367
  try {
349
- browser2 = await playwrightBrowser.connect(input.wsEndpoint);
368
+ browser = await playwrightBrowser.connect(input.wsEndpoint);
350
369
  } catch (error) {
351
370
  if (!browserConnectWarnings.has(input.wsEndpoint)) {
352
371
  browserConnectWarnings.add(input.wsEndpoint);
@@ -357,8 +376,8 @@ async function createBrowserWorkerRuntime(input) {
357
376
  }
358
377
  }
359
378
  }
360
- if (!browser2) {
361
- browser2 = await playwrightBrowser.launch({ headless: input.headless });
379
+ if (!browser) {
380
+ browser = await playwrightBrowser.launch({ headless: input.headless });
362
381
  }
363
382
  let vite;
364
383
  if (input.viteUrl) {
@@ -378,7 +397,7 @@ async function createBrowserWorkerRuntime(input) {
378
397
  };
379
398
  }
380
399
  return {
381
- browser: browser2,
400
+ browser,
382
401
  vite,
383
402
  rootDir: input.rootDir,
384
403
  key: input.key,
@@ -388,7 +407,7 @@ async function createBrowserWorkerRuntime(input) {
388
407
  };
389
408
  } catch (error) {
390
409
  await Promise.all([
391
- browser2?.close().catch(() => void 0),
410
+ browser?.close().catch(() => void 0),
392
411
  ownedServer?.close().catch(() => void 0)
393
412
  ]);
394
413
  if (cacheRoot) {
@@ -499,7 +518,19 @@ function assertFileExists(filePath, label) {
499
518
  function writeEntry(tempDir, name, source) {
500
519
  import_node_fs.default.mkdirSync(tempDir, { recursive: true });
501
520
  const entryPath = import_node_path.default.join(tempDir, `${name}.ts`);
502
- import_node_fs.default.writeFileSync(entryPath, source);
521
+ import_node_fs.default.writeFileSync(entryPath, `import { unmount as __woodHarnessUnmount } from 'svelte';
522
+ ` + source + `
523
+ let __woodHarnessClosing;
524
+ window.__WOOD_BROWSER_UNMOUNT__ = () => __woodHarnessClosing ??= Promise.resolve().then(async () => {
525
+ const app = window.__WOOD_BROWSER_APP__;
526
+ if (!app) return;
527
+ try { await app.disposeNavigation?.(); }
528
+ finally {
529
+ await __woodHarnessUnmount(app);
530
+ window.__WOOD_BROWSER_APP__ = undefined;
531
+ }
532
+ });
533
+ `);
503
534
  return entryPath;
504
535
  }
505
536
  function writeHtml(tempDir, name, entryPath, cssFiles) {
@@ -532,103 +563,6 @@ function findView(views, routeKey) {
532
563
  }
533
564
  return found;
534
565
  }
535
- function resolveComponentPath(rootDir, componentDir, filePath) {
536
- if (import_node_path.default.isAbsolute(filePath)) return filePath;
537
- const base = componentDir ? resolveFromRoot(rootDir, componentDir) : rootDir;
538
- const componentDirName = componentDir ? import_node_path.default.basename(componentDir) : "";
539
- if (componentDirName && filePath.replace(/\\/g, "/").startsWith(`${componentDirName}/`)) {
540
- return import_node_path.default.resolve(base, filePath.slice(componentDirName.length + 1));
541
- }
542
- return import_node_path.default.resolve(base, filePath);
543
- }
544
- function buildRouteEntrySource(input) {
545
- const { view, rootDir, componentDir, routeKey, route } = input;
546
- const viewPath = resolveComponentPath(rootDir, componentDir, view.viewPath);
547
- const layoutPaths = view.layouts.map((layout) => resolveComponentPath(rootDir, componentDir, layout));
548
- const controllerPath = view.controller ? resolveComponentPath(rootDir, componentDir, view.controller) : null;
549
- const layoutControllerPaths = view.layoutControllers?.map(
550
- (controller) => controller ? resolveComponentPath(rootDir, componentDir, controller) : null
551
- ) ?? [];
552
- const imports = [
553
- `import { mount } from 'svelte';`,
554
- `import NavigationShell from '@noego/wood/navigation-shell';`,
555
- `import { getContainer } from '@noego/wood/client';`,
556
- `import { getNavigation } from '@noego/wood/navigation';`,
557
- `import ViewComponent from '${viteImport(viewPath)}';`,
558
- ...layoutPaths.map((layoutPath, index) => `import Layout${index} from '${viteImport(layoutPath)}';`),
559
- ...controllerPath ? [`import PageController from '${viteImport(controllerPath)}';`] : [],
560
- ...layoutControllerPaths.map(
561
- (controllerPath2, index) => controllerPath2 ? `import LayoutController${index} from '${viteImport(controllerPath2)}';` : ""
562
- ).filter(Boolean)
563
- ];
564
- const layoutControllerClasses = layoutControllerPaths.map(
565
- (controllerPath2, index) => controllerPath2 ? `LayoutController${index}` : "undefined"
566
- );
567
- return `${imports.join("\n")}
568
-
569
- const entry = {
570
- key: ${JSON.stringify(routeKey)},
571
- windowName: ${JSON.stringify(view.windowName)},
572
- pageName: ${JSON.stringify(view.pageName)},
573
- viewPath: ${JSON.stringify(view.viewPath)},
574
- layoutPaths: ${JSON.stringify(view.layouts)},
575
- layoutControllers: ${JSON.stringify(view.layoutControllers ?? [])},
576
- controller: ${JSON.stringify(view.controller)},
577
- layoutControllerClasses: [${layoutControllerClasses.join(", ")}],
578
- controllerClass: ${controllerPath ? "PageController" : "undefined"},
579
- view: ViewComponent,
580
- layouts: [${layoutPaths.map((_, index) => `Layout${index}`).join(", ")}],
581
- };
582
-
583
- function createScopedContainer() {
584
- const root = getContainer();
585
- const scope = root.extend();
586
- return scope;
587
- }
588
-
589
- async function resolveController(ControllerClass) {
590
- if (!ControllerClass) return undefined;
591
- const scope = createScopedContainer();
592
- scope.registerClass?.(ControllerClass);
593
- return await scope.get(ControllerClass);
594
- }
595
-
596
- async function createViewController(nextEntry) {
597
- return await resolveController(nextEntry.controllerClass);
598
- }
599
-
600
- async function createLayoutControllers(nextEntry) {
601
- const controllers = [];
602
- for (const ControllerClass of nextEntry.layoutControllerClasses) {
603
- controllers.push(await resolveController(ControllerClass));
604
- }
605
- return controllers;
606
- }
607
-
608
- function getViewManifestEntry(nextKey) {
609
- return nextKey === entry.key ? entry : null;
610
- }
611
-
612
- const navigation = getNavigation();
613
- navigation.replace(entry.key, ${JSON.stringify(route.params)}, ${JSON.stringify(route.query)});
614
-
615
- const controller = await createViewController(entry);
616
- const layoutControllers = await createLayoutControllers(entry);
617
-
618
- window.__WOOD_BROWSER_APP__ = mount(NavigationShell, {
619
- target: document.getElementById('app'),
620
- props: {
621
- manifest: { [entry.key]: entry },
622
- initialEntry: entry,
623
- initialController: controller,
624
- initialLayoutControllers: layoutControllers,
625
- createViewControllerFn: createViewController,
626
- createLayoutControllersFn: createLayoutControllers,
627
- getViewManifestEntryFn: getViewManifestEntry,
628
- },
629
- });
630
- `;
631
- }
632
566
  function buildGeneratedAppEntrySource(input) {
633
567
  const hasRouteContext = Object.keys(input.route.params).length > 0 || Object.keys(input.route.query).length > 0;
634
568
  const rendererTraceEnable = input.enableRendererTrace ? `try {
@@ -638,14 +572,14 @@ function buildGeneratedAppEntrySource(input) {
638
572
  traceConfiguration.configureEnabled(true);
639
573
  } catch (error) { /* best-effort renderer-trace enable */ }` : "";
640
574
  const navigationApply = hasRouteContext ? `getNavigation().replace(${JSON.stringify(input.routeKey)}, ${JSON.stringify(input.route.params)}, ${JSON.stringify(input.route.query)});` : "";
641
- return `import { app } from '${viteImport(input.generatedApp)}';
575
+ return `import * as generated from '${viteImport(input.generatedApp)}';
642
576
  import { getNavigation } from '${input.navigationModule}';
643
577
  import { getContainer, TraceProviderConfiguration } from '${input.clientModule}';
644
578
  window.__WOOD_BROWSER_DEFAULT_ROUTE__ = ${JSON.stringify(input.routeKey)};
645
- window.__WOOD_BROWSER_APP__ = await app(document.getElementById('app'));
579
+ window.__WOOD_BROWSER_APP__ = await generated.app(document.getElementById('app'), ${JSON.stringify(input.route)});
646
580
  window.__WOOD_BROWSER_NAV__ = getNavigation();
647
581
  ${rendererTraceEnable}
648
- ${navigationApply}
582
+ if (!generated.supportsInitialRoute) { ${navigationApply} }
649
583
  `;
650
584
  }
651
585
  function buildComponentEntrySource(input) {
@@ -1048,7 +982,7 @@ async function captureCoverage(page, selector) {
1048
982
  await cdp.detach().catch(() => void 0);
1049
983
  }
1050
984
  }
1051
- function createExpect(page, outputDir) {
985
+ function browserAssertions(page, outputDir = import_node_path.default.resolve(".wood-browser-output")) {
1052
986
  async function waitForVisible(selector) {
1053
987
  const locator = page.locator(selector).first();
1054
988
  try {
@@ -1369,19 +1303,7 @@ function createSurface(input) {
1369
1303
  page: input.page,
1370
1304
  bridge: input.bridge,
1371
1305
  navigation: createMountedNavigation(input.page),
1372
- expect: createExpect(input.page, input.outputDir),
1373
- locator(selector) {
1374
- return input.page.locator(selector);
1375
- },
1376
- async click(selector) {
1377
- await input.page.locator(selector).click();
1378
- },
1379
- async fill(selector, value) {
1380
- await input.page.locator(selector).fill(value);
1381
- },
1382
- async evaluate(fn) {
1383
- return await input.page.evaluate(fn);
1384
- },
1306
+ assertions: browserAssertions(input.page, input.outputDir),
1385
1307
  destroy: input.destroy
1386
1308
  };
1387
1309
  }
@@ -1418,17 +1340,17 @@ async function openMountPage(input) {
1418
1340
  bridge: mountedBridge,
1419
1341
  outputDir: import_node_path.default.join(input.runtime.rootDir, "test-results", "wood-browser"),
1420
1342
  destroy: async () => {
1421
- await page.evaluate(() => {
1422
- const app = window.__WOOD_BROWSER_APP__;
1423
- if (app && typeof app === "object" && typeof app.$destroy === "function") {
1424
- app.$destroy();
1425
- }
1426
- }).catch(() => void 0);
1343
+ if (!page.isClosed()) await page.evaluate(() => window.__WOOD_BROWSER_UNMOUNT__?.());
1427
1344
  }
1428
1345
  });
1429
1346
  }
1430
- async function createHarness(config = {}) {
1347
+ async function createBrowserFixture(config = {}) {
1431
1348
  const rootDir = normalizeRoot(config.rootDir);
1349
+ const css = (config.css ?? []).map((cssPath) => {
1350
+ const resolved = resolveFromRoot(rootDir, cssPath);
1351
+ assertFileExists(resolved, "CSS file");
1352
+ return resolved;
1353
+ });
1432
1354
  const browserName = config.browser?.browserName ?? "chromium";
1433
1355
  const headless = config.browser?.headless ?? true;
1434
1356
  const workerRuntime = await acquireBrowserWorkerRuntime({
@@ -1437,7 +1359,7 @@ async function createHarness(config = {}) {
1437
1359
  headless
1438
1360
  });
1439
1361
  let released = false;
1440
- let destroyed = false;
1362
+ let destroying;
1441
1363
  let tempDir = null;
1442
1364
  let context = null;
1443
1365
  let page = null;
@@ -1476,11 +1398,6 @@ async function createHarness(config = {}) {
1476
1398
  rootDir,
1477
1399
  transportCleanups: []
1478
1400
  };
1479
- const css = (config.css ?? []).map((cssPath) => {
1480
- const resolved = resolveFromRoot(rootDir, cssPath);
1481
- assertFileExists(resolved, "CSS file");
1482
- return resolved;
1483
- });
1484
1401
  return {
1485
1402
  page,
1486
1403
  ...traceRecorder ? { traceRecorder } : {},
@@ -1495,12 +1412,20 @@ async function createHarness(config = {}) {
1495
1412
  const viewsPath = resolveFromRoot(rootDir, config.viewsConfig);
1496
1413
  const views = await loadViews(viewsPath);
1497
1414
  const view = findView(views, routeKey);
1498
- entrySource = buildRouteEntrySource({
1415
+ const generatedApp = import_node_path.default.join(runtime.tempDir, `route-app-${routeKey.replace(/[^a-zA-Z0-9_-]/g, "-")}.ts`);
1416
+ import_node_fs.default.writeFileSync(generatedApp, (0, import_renderer_app_generator.generateRendererApp)([view], {
1417
+ componentDir: config.componentDir ? resolveFromRoot(rootDir, config.componentDir) : rootDir,
1418
+ outDir: runtime.tempDir,
1419
+ entryViewKey: routeKey
1420
+ }));
1421
+ entrySource = buildGeneratedAppEntrySource({
1422
+ generatedApp,
1499
1423
  routeKey,
1500
- view,
1501
- rootDir,
1502
- componentDir: config.componentDir,
1503
- route
1424
+ route,
1425
+ navigationModule: resolveEsmModule(rootDir, "@noego/wood/navigation"),
1426
+ clientModule: resolveEsmModule(rootDir, "@noego/wood/client"),
1427
+ enableRendererTrace: config.trace?.enabled === true,
1428
+ traceMinLevel: config.trace?.minLevel ?? "debug"
1504
1429
  });
1505
1430
  } else if (config.generatedApp) {
1506
1431
  const generatedApp = resolveFromRoot(rootDir, config.generatedApp);
@@ -1515,7 +1440,7 @@ async function createHarness(config = {}) {
1515
1440
  traceMinLevel: config.trace?.minLevel ?? "debug"
1516
1441
  });
1517
1442
  } else {
1518
- throw new Error("mountRoute requires either viewsConfig or generatedApp in browser.createHarness().");
1443
+ throw new Error("mountRoute requires either viewsConfig or generatedApp in createBrowserFixture().");
1519
1444
  }
1520
1445
  const mounted = await openMountPage({
1521
1446
  runtime,
@@ -1613,34 +1538,38 @@ async function createHarness(config = {}) {
1613
1538
  async flushRendererTraces() {
1614
1539
  await page.evaluate(() => window.__woodBrowserTraceFlush?.()).catch(() => void 0);
1615
1540
  },
1616
- async destroy() {
1617
- if (destroyed) {
1618
- return;
1619
- }
1620
- destroyed = true;
1621
- for (const cleanup of runtime.transportCleanups.splice(0)) {
1622
- try {
1623
- cleanup();
1624
- } catch {
1625
- }
1626
- }
1627
- await Promise.all([
1628
- waitForBoundedClose(page.close()),
1629
- waitForBoundedClose(context.close())
1630
- ]);
1631
- traceRecorder?.disable();
1632
- import_node_fs.default.rmSync(tempDir, { recursive: true, force: true });
1633
- releaseRuntimeOnce();
1541
+ destroy() {
1542
+ return destroying ?? (destroying = Promise.resolve().then(async () => {
1543
+ const errors = [];
1544
+ const attempt = async (action) => {
1545
+ try {
1546
+ await action();
1547
+ } catch (error) {
1548
+ errors.push(error);
1549
+ }
1550
+ };
1551
+ if (!page.isClosed()) await attempt(() => closeRequired(
1552
+ page.evaluate(() => window.__WOOD_BROWSER_UNMOUNT__?.()),
1553
+ "renderer"
1554
+ ));
1555
+ for (const cleanup of runtime.transportCleanups.splice(0)) await attempt(cleanup);
1556
+ await attempt(() => closeRequired(page.close(), "browser page"));
1557
+ await attempt(() => closeRequired(context.close(), "browser context"));
1558
+ await attempt(() => traceRecorder?.disable());
1559
+ await attempt(() => import_node_fs.default.rmSync(tempDir, { recursive: true, force: true }));
1560
+ releaseRuntimeOnce();
1561
+ if (errors.length) throw Object.assign(new Error("Wood browser cleanup failed: " + errors.map((error) => error instanceof Error ? error.message : String(error)).join("; ")), { errors });
1562
+ }));
1634
1563
  }
1635
1564
  };
1636
1565
  }
1637
- const browser = {
1638
- createHarness
1639
- };
1640
1566
  // Annotate the CommonJS export names for ESM import in node:
1641
1567
  0 && (module.exports = {
1642
- browser,
1568
+ browserAssertions,
1643
1569
  createBrowserBridgeFixture,
1570
+ createBrowserFixture,
1571
+ createWoodBrowserResolver,
1572
+ loadSveltePlugin,
1644
1573
  startSharedBrowserViteServer
1645
1574
  });
1646
1575
  //# sourceMappingURL=browser.cjs.map