@noego/wood 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) 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 +7 -4
  13. package/dist/client/index.d.ts +7 -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 +12 -2
  43. package/dist/frontend/frontend_surface.d.ts +12 -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 +2 -0
  47. package/dist/frontend/index.d.ts +2 -0
  48. package/dist/index.cjs +5 -0
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +25 -0
  51. package/dist/index.d.ts +25 -0
  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 +2 -0
  77. package/dist/navigation/index.d.ts +2 -0
  78. package/dist/navigation/index.js +8 -1
  79. package/dist/navigation/index.js.map +1 -1
  80. package/dist/runtime/get_container.cjs +2 -0
  81. package/dist/runtime/get_container.cjs.map +1 -1
  82. package/dist/runtime/get_container.js +2 -0
  83. package/dist/runtime/get_container.js.map +1 -1
  84. package/dist/runtime/index.cjs +5 -0
  85. package/dist/runtime/index.cjs.map +1 -1
  86. package/dist/runtime/index.d.cts +2 -0
  87. package/dist/runtime/index.d.ts +2 -0
  88. package/dist/runtime/index.js +3 -0
  89. package/dist/runtime/index.js.map +1 -1
  90. package/dist/runtime/runtime_capabilities.cjs +35 -0
  91. package/dist/runtime/runtime_capabilities.cjs.map +1 -0
  92. package/dist/runtime/runtime_capabilities.d.cts +6 -0
  93. package/dist/runtime/runtime_capabilities.d.ts +6 -0
  94. package/dist/runtime/runtime_capabilities.js +11 -0
  95. package/dist/runtime/runtime_capabilities.js.map +1 -0
  96. package/dist/runtime/runtime_module.cjs +2 -0
  97. package/dist/runtime/runtime_module.cjs.map +1 -1
  98. package/dist/runtime/runtime_module.js +2 -0
  99. package/dist/runtime/runtime_module.js.map +1 -1
  100. package/dist/test-wood.cjs +150 -0
  101. package/dist/test-wood.cjs.map +1 -0
  102. package/dist/test-wood.d.cts +62 -0
  103. package/dist/test-wood.d.ts +62 -0
  104. package/dist/test-wood.js +115 -0
  105. package/dist/test-wood.js.map +1 -0
  106. package/dist/testing/browser.cjs +92 -163
  107. package/dist/testing/browser.cjs.map +1 -1
  108. package/dist/testing/browser.d.cts +14 -13
  109. package/dist/testing/browser.d.ts +14 -13
  110. package/dist/testing/browser.js +88 -162
  111. package/dist/testing/browser.js.map +1 -1
  112. package/dist/testing/create_controller_harness.d.cts +2 -1
  113. package/dist/testing/create_controller_harness.d.ts +2 -1
  114. package/dist/testing/create_router_bridge.cjs +19 -15
  115. package/dist/testing/create_router_bridge.cjs.map +1 -1
  116. package/dist/testing/create_router_bridge.d.cts +8 -1
  117. package/dist/testing/create_router_bridge.d.ts +8 -1
  118. package/dist/testing/create_router_bridge.js +18 -15
  119. package/dist/testing/create_router_bridge.js.map +1 -1
  120. package/dist/testing/index.cjs +12 -4
  121. package/dist/testing/index.cjs.map +1 -1
  122. package/dist/testing/index.d.cts +11 -5
  123. package/dist/testing/index.d.ts +11 -5
  124. package/dist/testing/index.js +9 -3
  125. package/dist/testing/index.js.map +1 -1
  126. package/dist/testing/native_browser/browser_pool.cjs +31 -0
  127. package/dist/testing/native_browser/browser_pool.cjs.map +1 -0
  128. package/dist/testing/native_browser/browser_pool.d.cts +1 -0
  129. package/dist/testing/native_browser/browser_pool.d.ts +1 -0
  130. package/dist/testing/native_browser/browser_pool.js +6 -0
  131. package/dist/testing/native_browser/browser_pool.js.map +1 -0
  132. package/dist/testing/native_browser/happy_dom_tab.cjs +107 -0
  133. package/dist/testing/native_browser/happy_dom_tab.cjs.map +1 -0
  134. package/dist/testing/native_browser/happy_dom_tab.d.cts +35 -0
  135. package/dist/testing/native_browser/happy_dom_tab.d.ts +35 -0
  136. package/dist/testing/native_browser/happy_dom_tab.js +73 -0
  137. package/dist/testing/native_browser/happy_dom_tab.js.map +1 -0
  138. package/dist/testing/native_browser/happy_dom_transfer.cjs +29 -0
  139. package/dist/testing/native_browser/happy_dom_transfer.cjs.map +1 -0
  140. package/dist/testing/native_browser/happy_dom_transfer.d.cts +1 -0
  141. package/dist/testing/native_browser/happy_dom_transfer.d.ts +1 -0
  142. package/dist/testing/native_browser/happy_dom_transfer.js +5 -0
  143. package/dist/testing/native_browser/happy_dom_transfer.js.map +1 -0
  144. package/dist/testing/native_browser/host_transport.cjs +89 -0
  145. package/dist/testing/native_browser/host_transport.cjs.map +1 -0
  146. package/dist/testing/native_browser/host_transport.d.cts +44 -0
  147. package/dist/testing/native_browser/host_transport.d.ts +44 -0
  148. package/dist/testing/native_browser/host_transport.js +65 -0
  149. package/dist/testing/native_browser/host_transport.js.map +1 -0
  150. package/dist/testing/native_browser/native_browser.types.cjs +17 -0
  151. package/dist/testing/native_browser/native_browser.types.cjs.map +1 -0
  152. package/dist/testing/native_browser/native_browser.types.d.cts +31 -0
  153. package/dist/testing/native_browser/native_browser.types.d.ts +31 -0
  154. package/dist/testing/native_browser/native_browser.types.js +1 -0
  155. package/dist/testing/native_browser/native_browser.types.js.map +1 -0
  156. package/dist/testing/native_browser/native_browser_environment.cjs +143 -0
  157. package/dist/testing/native_browser/native_browser_environment.cjs.map +1 -0
  158. package/dist/testing/native_browser/native_browser_environment.d.cts +36 -0
  159. package/dist/testing/native_browser/native_browser_environment.d.ts +36 -0
  160. package/dist/testing/native_browser/native_browser_environment.js +109 -0
  161. package/dist/testing/native_browser/native_browser_environment.js.map +1 -0
  162. package/dist/testing/native_browser/native_browser_lifecycle.cjs +31 -0
  163. package/dist/testing/native_browser/native_browser_lifecycle.cjs.map +1 -0
  164. package/dist/testing/native_browser/native_browser_lifecycle.d.cts +1 -0
  165. package/dist/testing/native_browser/native_browser_lifecycle.d.ts +1 -0
  166. package/dist/testing/native_browser/native_browser_lifecycle.js +6 -0
  167. package/dist/testing/native_browser/native_browser_lifecycle.js.map +1 -0
  168. package/dist/testing/native_browser/playwright_tab.cjs +124 -0
  169. package/dist/testing/native_browser/playwright_tab.cjs.map +1 -0
  170. package/dist/testing/native_browser/playwright_tab.d.cts +35 -0
  171. package/dist/testing/native_browser/playwright_tab.d.ts +35 -0
  172. package/dist/testing/native_browser/playwright_tab.js +100 -0
  173. package/dist/testing/native_browser/playwright_tab.js.map +1 -0
  174. package/dist/testing/native_browser/renderer_compilation.cjs +149 -0
  175. package/dist/testing/native_browser/renderer_compilation.cjs.map +1 -0
  176. package/dist/testing/native_browser/renderer_compilation.d.cts +35 -0
  177. package/dist/testing/native_browser/renderer_compilation.d.ts +35 -0
  178. package/dist/testing/native_browser/renderer_compilation.js +115 -0
  179. package/dist/testing/native_browser/renderer_compilation.js.map +1 -0
  180. package/dist/testing/native_browser/renderer_entry.cjs +148 -0
  181. package/dist/testing/native_browser/renderer_entry.cjs.map +1 -0
  182. package/dist/testing/native_browser/renderer_entry.d.cts +13 -0
  183. package/dist/testing/native_browser/renderer_entry.d.ts +13 -0
  184. package/dist/testing/native_browser/renderer_entry.js +114 -0
  185. package/dist/testing/native_browser/renderer_entry.js.map +1 -0
  186. package/dist/testing/registration_only_root.cjs +61 -5
  187. package/dist/testing/registration_only_root.cjs.map +1 -1
  188. package/dist/testing/registration_only_root.d.cts +25 -4
  189. package/dist/testing/registration_only_root.d.ts +25 -4
  190. package/dist/testing/registration_only_root.js +60 -5
  191. package/dist/testing/registration_only_root.js.map +1 -1
  192. package/dist/testing/runner_importer.cjs +75 -0
  193. package/dist/testing/runner_importer.cjs.map +1 -0
  194. package/dist/testing/runner_importer.d.cts +8 -0
  195. package/dist/testing/runner_importer.d.ts +8 -0
  196. package/dist/testing/runner_importer.js +41 -0
  197. package/dist/testing/runner_importer.js.map +1 -0
  198. package/dist/testing/test_wood_config.cjs +407 -273
  199. package/dist/testing/test_wood_config.cjs.map +1 -1
  200. package/dist/testing/test_wood_config.d.cts +18 -123
  201. package/dist/testing/test_wood_config.d.ts +18 -123
  202. package/dist/testing/test_wood_config.js +416 -271
  203. package/dist/testing/test_wood_config.js.map +1 -1
  204. package/dist/testing/wood_config_source.cjs +12 -0
  205. package/dist/testing/wood_config_source.cjs.map +1 -1
  206. package/dist/testing/wood_config_source.d.cts +10 -0
  207. package/dist/testing/wood_config_source.d.ts +10 -0
  208. package/dist/testing/wood_config_source.js +12 -0
  209. package/dist/testing/wood_config_source.js.map +1 -1
  210. package/dist/testing/wood_env_renderer.cjs +31 -4
  211. package/dist/testing/wood_env_renderer.cjs.map +1 -1
  212. package/dist/testing/wood_env_renderer.d.cts +14 -1
  213. package/dist/testing/wood_env_renderer.d.ts +14 -1
  214. package/dist/testing/wood_env_renderer.js +31 -4
  215. package/dist/testing/wood_env_renderer.js.map +1 -1
  216. package/dist/testing/wood_lazy_runtime.cjs +109 -0
  217. package/dist/testing/wood_lazy_runtime.cjs.map +1 -0
  218. package/dist/testing/wood_lazy_runtime.d.cts +36 -0
  219. package/dist/testing/wood_lazy_runtime.d.ts +36 -0
  220. package/dist/testing/wood_lazy_runtime.js +84 -0
  221. package/dist/testing/wood_lazy_runtime.js.map +1 -0
  222. package/dist/testing/wood_test_selection.cjs +34 -55
  223. package/dist/testing/wood_test_selection.cjs.map +1 -1
  224. package/dist/testing/wood_test_selection.d.cts +18 -24
  225. package/dist/testing/wood_test_selection.d.ts +18 -24
  226. package/dist/testing/wood_test_selection.js +30 -54
  227. package/dist/testing/wood_test_selection.js.map +1 -1
  228. package/dist/testing-api.cjs +17 -0
  229. package/dist/testing-api.cjs.map +1 -0
  230. package/dist/testing-api.d.cts +18 -0
  231. package/dist/testing-api.d.ts +18 -0
  232. package/dist/testing-api.js +1 -0
  233. package/dist/testing-api.js.map +1 -0
  234. package/dist/types/config.cjs.map +1 -1
  235. package/dist/types/config.d.cts +4 -0
  236. package/dist/types/config.d.ts +4 -0
  237. package/docs/native-browser-testing.md +164 -0
  238. package/package.json +31 -5
  239. package/src/components/NavigationShell.browser.test.ts +10 -10
  240. package/src/components/NavigationShell.cleanup.browser.test.ts +57 -0
  241. package/src/components/NavigationShell.scopes.browser.test.ts +5 -5
  242. package/src/components/NavigationShell.svelte +50 -24
@@ -1,3 +1,4 @@
1
+ import * as playwright from 'playwright';
1
2
  import { c as TraceLevel } from '../trace_hub-Cpo_nVze.cjs';
2
3
  import { TraceRecorder } from '../trace-testing/trace_recorder.cjs';
3
4
  import { WoodRpcTransport } from '../transport/wood_rpc_transport.cjs';
@@ -344,14 +345,12 @@ interface WoodBrowserExpect {
344
345
  }): Promise<void>;
345
346
  }
346
347
  interface MountedWoodSurface {
347
- page: any;
348
+ /** Native Playwright Page — use Page/Locator APIs for every browser action. */
349
+ page: playwright.Page;
348
350
  bridge: MountedWoodBridge;
349
351
  navigation: MountedWoodNavigation;
350
- expect: WoodBrowserExpect;
351
- locator(selector: string): any;
352
- click(selector: string): Promise<void>;
353
- fill(selector: string, value: string): Promise<void>;
354
- evaluate<T>(fn: () => T | Promise<T>): Promise<T>;
352
+ /** Wood-specific geometry/visibility assertion utilities; not an action wrapper. */
353
+ assertions: WoodBrowserExpect;
355
354
  destroy(): Promise<void>;
356
355
  }
357
356
  interface MountedWoodRoute extends MountedWoodSurface {
@@ -359,8 +358,9 @@ interface MountedWoodRoute extends MountedWoodSurface {
359
358
  }
360
359
  type MountedWoodComponent = MountedWoodSurface;
361
360
  type MountedWoodTree = MountedWoodSurface;
362
- interface WoodBrowserHarness {
363
- page: any;
361
+ interface WoodBrowserFixture {
362
+ /** Native Playwright Page owned by this fixture session. */
363
+ page: playwright.Page;
364
364
  traceRecorder?: TraceRecorder;
365
365
  mountRoute(routeKey: string, options?: MountRouteOptions): Promise<MountedWoodRoute>;
366
366
  mountComponent(component: string, options?: MountComponentOptions): Promise<MountedWoodComponent>;
@@ -394,19 +394,20 @@ declare function createBrowserBridgeFixture(input: WoodBridgeFixtureInput | unde
394
394
  manifest: RuntimeBridgeManifestEntry[];
395
395
  bridgeInit: string;
396
396
  };
397
+ declare function createWoodBrowserResolver(rootDir: string): any;
398
+ declare function loadSveltePlugin(rootDir: string): Promise<any | null>;
397
399
  declare function startSharedBrowserViteServer(input: {
398
400
  rootDir: string;
399
401
  }): Promise<{
400
402
  url: string;
401
403
  close: () => Promise<void>;
402
404
  }>;
403
- declare function createHarness(config?: WoodBrowserHarnessConfig): Promise<WoodBrowserHarness>;
404
- declare const browser: {
405
- createHarness: typeof createHarness;
406
- };
405
+ declare function browserAssertions(page: playwright.Page, outputDir?: string): WoodBrowserExpect;
406
+ declare function createBrowserFixture(config?: WoodBrowserHarnessConfig): Promise<WoodBrowserFixture>;
407
407
  declare global {
408
408
  interface Window {
409
409
  __WOOD_BROWSER_APP__?: unknown;
410
+ __WOOD_BROWSER_UNMOUNT__?: () => Promise<void>;
410
411
  __WOOD_BROWSER_DEFAULT_ROUTE__?: string;
411
412
  __WOOD_BROWSER_NAV__?: unknown;
412
413
  __WOOD_BROWSER_BRIDGE_MANIFEST__?: RuntimeBridgeManifestEntry[];
@@ -419,4 +420,4 @@ declare global {
419
420
  }
420
421
  }
421
422
 
422
- export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browser, createBrowserBridgeFixture, startSharedBrowserViteServer };
423
+ export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserFixture, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browserAssertions, createBrowserBridgeFixture, createBrowserFixture, createWoodBrowserResolver, loadSveltePlugin, startSharedBrowserViteServer };
@@ -1,3 +1,4 @@
1
+ import * as playwright from 'playwright';
1
2
  import { c as TraceLevel } from '../trace_hub-Cpo_nVze.js';
2
3
  import { TraceRecorder } from '../trace-testing/trace_recorder.js';
3
4
  import { WoodRpcTransport } from '../transport/wood_rpc_transport.js';
@@ -344,14 +345,12 @@ interface WoodBrowserExpect {
344
345
  }): Promise<void>;
345
346
  }
346
347
  interface MountedWoodSurface {
347
- page: any;
348
+ /** Native Playwright Page — use Page/Locator APIs for every browser action. */
349
+ page: playwright.Page;
348
350
  bridge: MountedWoodBridge;
349
351
  navigation: MountedWoodNavigation;
350
- expect: WoodBrowserExpect;
351
- locator(selector: string): any;
352
- click(selector: string): Promise<void>;
353
- fill(selector: string, value: string): Promise<void>;
354
- evaluate<T>(fn: () => T | Promise<T>): Promise<T>;
352
+ /** Wood-specific geometry/visibility assertion utilities; not an action wrapper. */
353
+ assertions: WoodBrowserExpect;
355
354
  destroy(): Promise<void>;
356
355
  }
357
356
  interface MountedWoodRoute extends MountedWoodSurface {
@@ -359,8 +358,9 @@ interface MountedWoodRoute extends MountedWoodSurface {
359
358
  }
360
359
  type MountedWoodComponent = MountedWoodSurface;
361
360
  type MountedWoodTree = MountedWoodSurface;
362
- interface WoodBrowserHarness {
363
- page: any;
361
+ interface WoodBrowserFixture {
362
+ /** Native Playwright Page owned by this fixture session. */
363
+ page: playwright.Page;
364
364
  traceRecorder?: TraceRecorder;
365
365
  mountRoute(routeKey: string, options?: MountRouteOptions): Promise<MountedWoodRoute>;
366
366
  mountComponent(component: string, options?: MountComponentOptions): Promise<MountedWoodComponent>;
@@ -394,19 +394,20 @@ declare function createBrowserBridgeFixture(input: WoodBridgeFixtureInput | unde
394
394
  manifest: RuntimeBridgeManifestEntry[];
395
395
  bridgeInit: string;
396
396
  };
397
+ declare function createWoodBrowserResolver(rootDir: string): any;
398
+ declare function loadSveltePlugin(rootDir: string): Promise<any | null>;
397
399
  declare function startSharedBrowserViteServer(input: {
398
400
  rootDir: string;
399
401
  }): Promise<{
400
402
  url: string;
401
403
  close: () => Promise<void>;
402
404
  }>;
403
- declare function createHarness(config?: WoodBrowserHarnessConfig): Promise<WoodBrowserHarness>;
404
- declare const browser: {
405
- createHarness: typeof createHarness;
406
- };
405
+ declare function browserAssertions(page: playwright.Page, outputDir?: string): WoodBrowserExpect;
406
+ declare function createBrowserFixture(config?: WoodBrowserHarnessConfig): Promise<WoodBrowserFixture>;
407
407
  declare global {
408
408
  interface Window {
409
409
  __WOOD_BROWSER_APP__?: unknown;
410
+ __WOOD_BROWSER_UNMOUNT__?: () => Promise<void>;
410
411
  __WOOD_BROWSER_DEFAULT_ROUTE__?: string;
411
412
  __WOOD_BROWSER_NAV__?: unknown;
412
413
  __WOOD_BROWSER_BRIDGE_MANIFEST__?: RuntimeBridgeManifestEntry[];
@@ -419,4 +420,4 @@ declare global {
419
420
  }
420
421
  }
421
422
 
422
- export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browser, createBrowserBridgeFixture, startSharedBrowserViteServer };
423
+ export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserFixture, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browserAssertions, createBrowserBridgeFixture, createBrowserFixture, createWoodBrowserResolver, loadSveltePlugin, startSharedBrowserViteServer };
@@ -7,6 +7,7 @@ import {
7
7
  dispatchRendererTracePayload
8
8
  } from "../tracing/index.js";
9
9
  import { TraceRecorder } from "../trace-testing/index.js";
10
+ import { generateRendererApp } from "../codegen/renderer_app_generator.js";
10
11
  import {
11
12
  axisEdges,
12
13
  axisGap,
@@ -71,6 +72,16 @@ async function waitForBoundedClose(promise, timeoutMs = DEFAULT_RESOURCE_CLOSE_T
71
72
  clearTimeout(timeout);
72
73
  }
73
74
  }
75
+ async function closeRequired(promise, owner) {
76
+ let timeout;
77
+ try {
78
+ await Promise.race([promise, new Promise((_, reject) => {
79
+ timeout = setTimeout(() => reject(new Error(`${owner} cleanup timed out`)), DEFAULT_RESOURCE_CLOSE_TIMEOUT_MS);
80
+ })]);
81
+ } finally {
82
+ if (timeout) clearTimeout(timeout);
83
+ }
84
+ }
74
85
  function channelToManifestEntry(channel, handler) {
75
86
  const pathSegments = channel.split(".").filter(Boolean);
76
87
  const action = pathSegments.length > 0 ? pathSegments[pathSegments.length - 1] : channel;
@@ -230,6 +241,11 @@ function createWoodBrowserResolver(rootDir) {
230
241
  name: "wood-browser-resolver",
231
242
  enforce: "pre",
232
243
  resolveId(source) {
244
+ if (source.startsWith("@noego/wood/")) {
245
+ const resolved = resolvePackageModule(rootDir, source);
246
+ const esm = resolved?.endsWith(".cjs") ? resolved.replace(/\.cjs$/, ".js") : resolved;
247
+ return esm && fs.existsSync(esm) ? esm : null;
248
+ }
233
249
  if (source === "svelte") {
234
250
  return svelteClientEntry;
235
251
  }
@@ -313,14 +329,14 @@ function workerRuntimeKey(input) {
313
329
  ].join("::");
314
330
  }
315
331
  async function createBrowserWorkerRuntime(input) {
316
- let browser2 = null;
332
+ let browser = null;
317
333
  let cacheRoot = null;
318
334
  let ownedServer = null;
319
335
  try {
320
336
  const playwrightBrowser = await loadPlaywright(input.browserName, input.rootDir);
321
337
  if (input.wsEndpoint) {
322
338
  try {
323
- browser2 = await playwrightBrowser.connect(input.wsEndpoint);
339
+ browser = await playwrightBrowser.connect(input.wsEndpoint);
324
340
  } catch (error) {
325
341
  if (!browserConnectWarnings.has(input.wsEndpoint)) {
326
342
  browserConnectWarnings.add(input.wsEndpoint);
@@ -331,8 +347,8 @@ async function createBrowserWorkerRuntime(input) {
331
347
  }
332
348
  }
333
349
  }
334
- if (!browser2) {
335
- browser2 = await playwrightBrowser.launch({ headless: input.headless });
350
+ if (!browser) {
351
+ browser = await playwrightBrowser.launch({ headless: input.headless });
336
352
  }
337
353
  let vite;
338
354
  if (input.viteUrl) {
@@ -352,7 +368,7 @@ async function createBrowserWorkerRuntime(input) {
352
368
  };
353
369
  }
354
370
  return {
355
- browser: browser2,
371
+ browser,
356
372
  vite,
357
373
  rootDir: input.rootDir,
358
374
  key: input.key,
@@ -362,7 +378,7 @@ async function createBrowserWorkerRuntime(input) {
362
378
  };
363
379
  } catch (error) {
364
380
  await Promise.all([
365
- browser2?.close().catch(() => void 0),
381
+ browser?.close().catch(() => void 0),
366
382
  ownedServer?.close().catch(() => void 0)
367
383
  ]);
368
384
  if (cacheRoot) {
@@ -473,7 +489,19 @@ function assertFileExists(filePath, label) {
473
489
  function writeEntry(tempDir, name, source) {
474
490
  fs.mkdirSync(tempDir, { recursive: true });
475
491
  const entryPath = path.join(tempDir, `${name}.ts`);
476
- fs.writeFileSync(entryPath, source);
492
+ fs.writeFileSync(entryPath, `import { unmount as __woodHarnessUnmount } from 'svelte';
493
+ ` + source + `
494
+ let __woodHarnessClosing;
495
+ window.__WOOD_BROWSER_UNMOUNT__ = () => __woodHarnessClosing ??= Promise.resolve().then(async () => {
496
+ const app = window.__WOOD_BROWSER_APP__;
497
+ if (!app) return;
498
+ try { await app.disposeNavigation?.(); }
499
+ finally {
500
+ await __woodHarnessUnmount(app);
501
+ window.__WOOD_BROWSER_APP__ = undefined;
502
+ }
503
+ });
504
+ `);
477
505
  return entryPath;
478
506
  }
479
507
  function writeHtml(tempDir, name, entryPath, cssFiles) {
@@ -506,103 +534,6 @@ function findView(views, routeKey) {
506
534
  }
507
535
  return found;
508
536
  }
509
- function resolveComponentPath(rootDir, componentDir, filePath) {
510
- if (path.isAbsolute(filePath)) return filePath;
511
- const base = componentDir ? resolveFromRoot(rootDir, componentDir) : rootDir;
512
- const componentDirName = componentDir ? path.basename(componentDir) : "";
513
- if (componentDirName && filePath.replace(/\\/g, "/").startsWith(`${componentDirName}/`)) {
514
- return path.resolve(base, filePath.slice(componentDirName.length + 1));
515
- }
516
- return path.resolve(base, filePath);
517
- }
518
- function buildRouteEntrySource(input) {
519
- const { view, rootDir, componentDir, routeKey, route } = input;
520
- const viewPath = resolveComponentPath(rootDir, componentDir, view.viewPath);
521
- const layoutPaths = view.layouts.map((layout) => resolveComponentPath(rootDir, componentDir, layout));
522
- const controllerPath = view.controller ? resolveComponentPath(rootDir, componentDir, view.controller) : null;
523
- const layoutControllerPaths = view.layoutControllers?.map(
524
- (controller) => controller ? resolveComponentPath(rootDir, componentDir, controller) : null
525
- ) ?? [];
526
- const imports = [
527
- `import { mount } from 'svelte';`,
528
- `import NavigationShell from '@noego/wood/navigation-shell';`,
529
- `import { getContainer } from '@noego/wood/client';`,
530
- `import { getNavigation } from '@noego/wood/navigation';`,
531
- `import ViewComponent from '${viteImport(viewPath)}';`,
532
- ...layoutPaths.map((layoutPath, index) => `import Layout${index} from '${viteImport(layoutPath)}';`),
533
- ...controllerPath ? [`import PageController from '${viteImport(controllerPath)}';`] : [],
534
- ...layoutControllerPaths.map(
535
- (controllerPath2, index) => controllerPath2 ? `import LayoutController${index} from '${viteImport(controllerPath2)}';` : ""
536
- ).filter(Boolean)
537
- ];
538
- const layoutControllerClasses = layoutControllerPaths.map(
539
- (controllerPath2, index) => controllerPath2 ? `LayoutController${index}` : "undefined"
540
- );
541
- return `${imports.join("\n")}
542
-
543
- const entry = {
544
- key: ${JSON.stringify(routeKey)},
545
- windowName: ${JSON.stringify(view.windowName)},
546
- pageName: ${JSON.stringify(view.pageName)},
547
- viewPath: ${JSON.stringify(view.viewPath)},
548
- layoutPaths: ${JSON.stringify(view.layouts)},
549
- layoutControllers: ${JSON.stringify(view.layoutControllers ?? [])},
550
- controller: ${JSON.stringify(view.controller)},
551
- layoutControllerClasses: [${layoutControllerClasses.join(", ")}],
552
- controllerClass: ${controllerPath ? "PageController" : "undefined"},
553
- view: ViewComponent,
554
- layouts: [${layoutPaths.map((_, index) => `Layout${index}`).join(", ")}],
555
- };
556
-
557
- function createScopedContainer() {
558
- const root = getContainer();
559
- const scope = root.extend();
560
- return scope;
561
- }
562
-
563
- async function resolveController(ControllerClass) {
564
- if (!ControllerClass) return undefined;
565
- const scope = createScopedContainer();
566
- scope.registerClass?.(ControllerClass);
567
- return await scope.get(ControllerClass);
568
- }
569
-
570
- async function createViewController(nextEntry) {
571
- return await resolveController(nextEntry.controllerClass);
572
- }
573
-
574
- async function createLayoutControllers(nextEntry) {
575
- const controllers = [];
576
- for (const ControllerClass of nextEntry.layoutControllerClasses) {
577
- controllers.push(await resolveController(ControllerClass));
578
- }
579
- return controllers;
580
- }
581
-
582
- function getViewManifestEntry(nextKey) {
583
- return nextKey === entry.key ? entry : null;
584
- }
585
-
586
- const navigation = getNavigation();
587
- navigation.replace(entry.key, ${JSON.stringify(route.params)}, ${JSON.stringify(route.query)});
588
-
589
- const controller = await createViewController(entry);
590
- const layoutControllers = await createLayoutControllers(entry);
591
-
592
- window.__WOOD_BROWSER_APP__ = mount(NavigationShell, {
593
- target: document.getElementById('app'),
594
- props: {
595
- manifest: { [entry.key]: entry },
596
- initialEntry: entry,
597
- initialController: controller,
598
- initialLayoutControllers: layoutControllers,
599
- createViewControllerFn: createViewController,
600
- createLayoutControllersFn: createLayoutControllers,
601
- getViewManifestEntryFn: getViewManifestEntry,
602
- },
603
- });
604
- `;
605
- }
606
537
  function buildGeneratedAppEntrySource(input) {
607
538
  const hasRouteContext = Object.keys(input.route.params).length > 0 || Object.keys(input.route.query).length > 0;
608
539
  const rendererTraceEnable = input.enableRendererTrace ? `try {
@@ -612,14 +543,14 @@ function buildGeneratedAppEntrySource(input) {
612
543
  traceConfiguration.configureEnabled(true);
613
544
  } catch (error) { /* best-effort renderer-trace enable */ }` : "";
614
545
  const navigationApply = hasRouteContext ? `getNavigation().replace(${JSON.stringify(input.routeKey)}, ${JSON.stringify(input.route.params)}, ${JSON.stringify(input.route.query)});` : "";
615
- return `import { app } from '${viteImport(input.generatedApp)}';
546
+ return `import * as generated from '${viteImport(input.generatedApp)}';
616
547
  import { getNavigation } from '${input.navigationModule}';
617
548
  import { getContainer, TraceProviderConfiguration } from '${input.clientModule}';
618
549
  window.__WOOD_BROWSER_DEFAULT_ROUTE__ = ${JSON.stringify(input.routeKey)};
619
- window.__WOOD_BROWSER_APP__ = await app(document.getElementById('app'));
550
+ window.__WOOD_BROWSER_APP__ = await generated.app(document.getElementById('app'), ${JSON.stringify(input.route)});
620
551
  window.__WOOD_BROWSER_NAV__ = getNavigation();
621
552
  ${rendererTraceEnable}
622
- ${navigationApply}
553
+ if (!generated.supportsInitialRoute) { ${navigationApply} }
623
554
  `;
624
555
  }
625
556
  function buildComponentEntrySource(input) {
@@ -1022,7 +953,7 @@ async function captureCoverage(page, selector) {
1022
953
  await cdp.detach().catch(() => void 0);
1023
954
  }
1024
955
  }
1025
- function createExpect(page, outputDir) {
956
+ function browserAssertions(page, outputDir = path.resolve(".wood-browser-output")) {
1026
957
  async function waitForVisible(selector) {
1027
958
  const locator = page.locator(selector).first();
1028
959
  try {
@@ -1343,19 +1274,7 @@ function createSurface(input) {
1343
1274
  page: input.page,
1344
1275
  bridge: input.bridge,
1345
1276
  navigation: createMountedNavigation(input.page),
1346
- expect: createExpect(input.page, input.outputDir),
1347
- locator(selector) {
1348
- return input.page.locator(selector);
1349
- },
1350
- async click(selector) {
1351
- await input.page.locator(selector).click();
1352
- },
1353
- async fill(selector, value) {
1354
- await input.page.locator(selector).fill(value);
1355
- },
1356
- async evaluate(fn) {
1357
- return await input.page.evaluate(fn);
1358
- },
1277
+ assertions: browserAssertions(input.page, input.outputDir),
1359
1278
  destroy: input.destroy
1360
1279
  };
1361
1280
  }
@@ -1392,17 +1311,17 @@ async function openMountPage(input) {
1392
1311
  bridge: mountedBridge,
1393
1312
  outputDir: path.join(input.runtime.rootDir, "test-results", "wood-browser"),
1394
1313
  destroy: async () => {
1395
- await page.evaluate(() => {
1396
- const app = window.__WOOD_BROWSER_APP__;
1397
- if (app && typeof app === "object" && typeof app.$destroy === "function") {
1398
- app.$destroy();
1399
- }
1400
- }).catch(() => void 0);
1314
+ if (!page.isClosed()) await page.evaluate(() => window.__WOOD_BROWSER_UNMOUNT__?.());
1401
1315
  }
1402
1316
  });
1403
1317
  }
1404
- async function createHarness(config = {}) {
1318
+ async function createBrowserFixture(config = {}) {
1405
1319
  const rootDir = normalizeRoot(config.rootDir);
1320
+ const css = (config.css ?? []).map((cssPath) => {
1321
+ const resolved = resolveFromRoot(rootDir, cssPath);
1322
+ assertFileExists(resolved, "CSS file");
1323
+ return resolved;
1324
+ });
1406
1325
  const browserName = config.browser?.browserName ?? "chromium";
1407
1326
  const headless = config.browser?.headless ?? true;
1408
1327
  const workerRuntime = await acquireBrowserWorkerRuntime({
@@ -1411,7 +1330,7 @@ async function createHarness(config = {}) {
1411
1330
  headless
1412
1331
  });
1413
1332
  let released = false;
1414
- let destroyed = false;
1333
+ let destroying;
1415
1334
  let tempDir = null;
1416
1335
  let context = null;
1417
1336
  let page = null;
@@ -1450,11 +1369,6 @@ async function createHarness(config = {}) {
1450
1369
  rootDir,
1451
1370
  transportCleanups: []
1452
1371
  };
1453
- const css = (config.css ?? []).map((cssPath) => {
1454
- const resolved = resolveFromRoot(rootDir, cssPath);
1455
- assertFileExists(resolved, "CSS file");
1456
- return resolved;
1457
- });
1458
1372
  return {
1459
1373
  page,
1460
1374
  ...traceRecorder ? { traceRecorder } : {},
@@ -1469,12 +1383,20 @@ async function createHarness(config = {}) {
1469
1383
  const viewsPath = resolveFromRoot(rootDir, config.viewsConfig);
1470
1384
  const views = await loadViews(viewsPath);
1471
1385
  const view = findView(views, routeKey);
1472
- entrySource = buildRouteEntrySource({
1386
+ const generatedApp = path.join(runtime.tempDir, `route-app-${routeKey.replace(/[^a-zA-Z0-9_-]/g, "-")}.ts`);
1387
+ fs.writeFileSync(generatedApp, generateRendererApp([view], {
1388
+ componentDir: config.componentDir ? resolveFromRoot(rootDir, config.componentDir) : rootDir,
1389
+ outDir: runtime.tempDir,
1390
+ entryViewKey: routeKey
1391
+ }));
1392
+ entrySource = buildGeneratedAppEntrySource({
1393
+ generatedApp,
1473
1394
  routeKey,
1474
- view,
1475
- rootDir,
1476
- componentDir: config.componentDir,
1477
- route
1395
+ route,
1396
+ navigationModule: resolveEsmModule(rootDir, "@noego/wood/navigation"),
1397
+ clientModule: resolveEsmModule(rootDir, "@noego/wood/client"),
1398
+ enableRendererTrace: config.trace?.enabled === true,
1399
+ traceMinLevel: config.trace?.minLevel ?? "debug"
1478
1400
  });
1479
1401
  } else if (config.generatedApp) {
1480
1402
  const generatedApp = resolveFromRoot(rootDir, config.generatedApp);
@@ -1489,7 +1411,7 @@ async function createHarness(config = {}) {
1489
1411
  traceMinLevel: config.trace?.minLevel ?? "debug"
1490
1412
  });
1491
1413
  } else {
1492
- throw new Error("mountRoute requires either viewsConfig or generatedApp in browser.createHarness().");
1414
+ throw new Error("mountRoute requires either viewsConfig or generatedApp in createBrowserFixture().");
1493
1415
  }
1494
1416
  const mounted = await openMountPage({
1495
1417
  runtime,
@@ -1587,33 +1509,37 @@ async function createHarness(config = {}) {
1587
1509
  async flushRendererTraces() {
1588
1510
  await page.evaluate(() => window.__woodBrowserTraceFlush?.()).catch(() => void 0);
1589
1511
  },
1590
- async destroy() {
1591
- if (destroyed) {
1592
- return;
1593
- }
1594
- destroyed = true;
1595
- for (const cleanup of runtime.transportCleanups.splice(0)) {
1596
- try {
1597
- cleanup();
1598
- } catch {
1599
- }
1600
- }
1601
- await Promise.all([
1602
- waitForBoundedClose(page.close()),
1603
- waitForBoundedClose(context.close())
1604
- ]);
1605
- traceRecorder?.disable();
1606
- fs.rmSync(tempDir, { recursive: true, force: true });
1607
- releaseRuntimeOnce();
1512
+ destroy() {
1513
+ return destroying ?? (destroying = Promise.resolve().then(async () => {
1514
+ const errors = [];
1515
+ const attempt = async (action) => {
1516
+ try {
1517
+ await action();
1518
+ } catch (error) {
1519
+ errors.push(error);
1520
+ }
1521
+ };
1522
+ if (!page.isClosed()) await attempt(() => closeRequired(
1523
+ page.evaluate(() => window.__WOOD_BROWSER_UNMOUNT__?.()),
1524
+ "renderer"
1525
+ ));
1526
+ for (const cleanup of runtime.transportCleanups.splice(0)) await attempt(cleanup);
1527
+ await attempt(() => closeRequired(page.close(), "browser page"));
1528
+ await attempt(() => closeRequired(context.close(), "browser context"));
1529
+ await attempt(() => traceRecorder?.disable());
1530
+ await attempt(() => fs.rmSync(tempDir, { recursive: true, force: true }));
1531
+ releaseRuntimeOnce();
1532
+ if (errors.length) throw Object.assign(new Error("Wood browser cleanup failed: " + errors.map((error) => error instanceof Error ? error.message : String(error)).join("; ")), { errors });
1533
+ }));
1608
1534
  }
1609
1535
  };
1610
1536
  }
1611
- const browser = {
1612
- createHarness
1613
- };
1614
1537
  export {
1615
- browser,
1538
+ browserAssertions,
1616
1539
  createBrowserBridgeFixture,
1540
+ createBrowserFixture,
1541
+ createWoodBrowserResolver,
1542
+ loadSveltePlugin,
1617
1543
  startSharedBrowserViteServer
1618
1544
  };
1619
1545
  //# sourceMappingURL=browser.js.map