@noego/wood 0.4.9 → 0.5.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 (493) hide show
  1. package/README.md +118 -42
  2. package/bin/axe.js +313 -98
  3. package/dist/{loader/__fixtures__/layouts/app.load.cjs → automation/actions.cjs} +3 -13
  4. package/dist/automation/actions.cjs.map +1 -0
  5. package/dist/automation/actions.d.cts +57 -0
  6. package/dist/automation/actions.d.ts +57 -0
  7. package/dist/automation/actions.js +1 -0
  8. package/dist/automation/actions.js.map +1 -0
  9. package/dist/automation/actions_executor.cjs +101 -0
  10. package/dist/automation/actions_executor.cjs.map +1 -0
  11. package/dist/automation/actions_executor.d.cts +16 -0
  12. package/dist/automation/actions_executor.d.ts +16 -0
  13. package/dist/automation/actions_executor.js +76 -0
  14. package/dist/automation/actions_executor.js.map +1 -0
  15. package/dist/automation/connection.cjs +86 -0
  16. package/dist/automation/connection.cjs.map +1 -0
  17. package/dist/automation/connection.d.cts +18 -0
  18. package/dist/automation/connection.d.ts +18 -0
  19. package/dist/automation/connection.js +51 -0
  20. package/dist/automation/connection.js.map +1 -0
  21. package/dist/{middleware/__fixtures__/middleware/sequence.cjs → automation/errors.cjs} +25 -22
  22. package/dist/automation/errors.cjs.map +1 -0
  23. package/dist/automation/errors.d.cts +31 -0
  24. package/dist/automation/errors.d.ts +31 -0
  25. package/dist/automation/errors.js +24 -0
  26. package/dist/automation/errors.js.map +1 -0
  27. package/dist/automation/index.cjs +49 -0
  28. package/dist/automation/index.cjs.map +1 -0
  29. package/dist/automation/index.d.cts +16 -0
  30. package/dist/automation/index.d.ts +16 -0
  31. package/dist/automation/index.js +15 -0
  32. package/dist/automation/index.js.map +1 -0
  33. package/dist/automation/introspection_reader.cjs +75 -0
  34. package/dist/automation/introspection_reader.cjs.map +1 -0
  35. package/dist/automation/introspection_reader.d.cts +20 -0
  36. package/dist/automation/introspection_reader.d.ts +20 -0
  37. package/dist/automation/introspection_reader.js +48 -0
  38. package/dist/automation/introspection_reader.js.map +1 -0
  39. package/dist/automation/locator.cjs +102 -0
  40. package/dist/automation/locator.cjs.map +1 -0
  41. package/dist/automation/locator.d.cts +51 -0
  42. package/dist/automation/locator.d.ts +51 -0
  43. package/dist/automation/locator.js +75 -0
  44. package/dist/automation/locator.js.map +1 -0
  45. package/dist/{loader/__fixtures__/views/test/page.load.cjs → automation/observation.cjs} +3 -13
  46. package/dist/automation/observation.cjs.map +1 -0
  47. package/dist/automation/observation.d.cts +44 -0
  48. package/dist/automation/observation.d.ts +44 -0
  49. package/dist/automation/observation.js +1 -0
  50. package/dist/automation/observation.js.map +1 -0
  51. package/dist/{controller/__fixtures__/controllers/test.cjs → automation/receipts.cjs} +3 -15
  52. package/dist/automation/receipts.cjs.map +1 -0
  53. package/dist/automation/receipts.d.cts +27 -0
  54. package/dist/automation/receipts.d.ts +27 -0
  55. package/dist/automation/receipts.js +1 -0
  56. package/dist/automation/receipts.js.map +1 -0
  57. package/dist/automation/scenario.cjs +696 -0
  58. package/dist/automation/scenario.cjs.map +1 -0
  59. package/dist/automation/scenario.d.cts +89 -0
  60. package/dist/automation/scenario.d.ts +89 -0
  61. package/dist/automation/scenario.js +660 -0
  62. package/dist/automation/scenario.js.map +1 -0
  63. package/dist/automation/scrolling.cjs +17 -0
  64. package/dist/automation/scrolling.cjs.map +1 -0
  65. package/dist/automation/scrolling.d.cts +51 -0
  66. package/dist/automation/scrolling.d.ts +51 -0
  67. package/dist/automation/scrolling.js +1 -0
  68. package/dist/automation/scrolling.js.map +1 -0
  69. package/dist/automation/scrolling_executor.cjs +109 -0
  70. package/dist/automation/scrolling_executor.cjs.map +1 -0
  71. package/dist/automation/scrolling_executor.d.cts +13 -0
  72. package/dist/automation/scrolling_executor.d.ts +13 -0
  73. package/dist/automation/scrolling_executor.js +84 -0
  74. package/dist/automation/scrolling_executor.js.map +1 -0
  75. package/dist/automation/target_identity.cjs +17 -0
  76. package/dist/automation/target_identity.cjs.map +1 -0
  77. package/dist/automation/target_identity.d.cts +28 -0
  78. package/dist/automation/target_identity.d.ts +28 -0
  79. package/dist/automation/target_identity.js +1 -0
  80. package/dist/automation/target_identity.js.map +1 -0
  81. package/dist/automation/target_registry.cjs +148 -0
  82. package/dist/automation/target_registry.cjs.map +1 -0
  83. package/dist/automation/target_registry.d.cts +32 -0
  84. package/dist/automation/target_registry.d.ts +32 -0
  85. package/dist/automation/target_registry.js +122 -0
  86. package/dist/automation/target_registry.js.map +1 -0
  87. package/dist/automation/waits.cjs +17 -0
  88. package/dist/automation/waits.cjs.map +1 -0
  89. package/dist/automation/waits.d.cts +44 -0
  90. package/dist/automation/waits.d.ts +44 -0
  91. package/dist/automation/waits.js +1 -0
  92. package/dist/automation/waits.js.map +1 -0
  93. package/dist/automation/waits_executor.cjs +104 -0
  94. package/dist/automation/waits_executor.cjs.map +1 -0
  95. package/dist/automation/waits_executor.d.cts +14 -0
  96. package/dist/automation/waits_executor.d.ts +14 -0
  97. package/dist/automation/waits_executor.js +79 -0
  98. package/dist/automation/waits_executor.js.map +1 -0
  99. package/dist/bridge/backend_bridge.cjs.map +1 -1
  100. package/dist/bridge/backend_bridge.js.map +1 -1
  101. package/dist/client/index.cjs +78 -21
  102. package/dist/client/index.cjs.map +1 -1
  103. package/dist/client/index.d.cts +19 -3
  104. package/dist/client/index.d.ts +19 -3
  105. package/dist/client/index.js +80 -22
  106. package/dist/client/index.js.map +1 -1
  107. package/dist/client/renderer_log_transport.cjs +151 -0
  108. package/dist/client/renderer_log_transport.cjs.map +1 -0
  109. package/dist/client/renderer_log_transport.d.cts +54 -0
  110. package/dist/client/renderer_log_transport.d.ts +54 -0
  111. package/dist/client/renderer_log_transport.js +123 -0
  112. package/dist/client/renderer_log_transport.js.map +1 -0
  113. package/dist/codegen/controller_registration_generator.cjs +13 -2
  114. package/dist/codegen/controller_registration_generator.cjs.map +1 -1
  115. package/dist/codegen/controller_registration_generator.d.cts +12 -1
  116. package/dist/codegen/controller_registration_generator.d.ts +12 -1
  117. package/dist/codegen/controller_registration_generator.js +10 -1
  118. package/dist/codegen/controller_registration_generator.js.map +1 -1
  119. package/dist/codegen/index.d.cts +1 -0
  120. package/dist/codegen/index.d.ts +1 -0
  121. package/dist/codegen/preload_generator.cjs +13 -6
  122. package/dist/codegen/preload_generator.cjs.map +1 -1
  123. package/dist/codegen/preload_generator.d.cts +11 -2
  124. package/dist/codegen/preload_generator.d.ts +11 -2
  125. package/dist/codegen/preload_generator.js +13 -6
  126. package/dist/codegen/preload_generator.js.map +1 -1
  127. package/dist/codegen/renderer_app_generator.cjs +95 -44
  128. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  129. package/dist/codegen/renderer_app_generator.js +95 -44
  130. package/dist/codegen/renderer_app_generator.js.map +1 -1
  131. package/dist/codegen/type_generator.cjs +1 -0
  132. package/dist/codegen/type_generator.cjs.map +1 -1
  133. package/dist/codegen/type_generator.js +1 -0
  134. package/dist/codegen/type_generator.js.map +1 -1
  135. package/dist/config/config_parser.cjs +95 -4
  136. package/dist/config/config_parser.cjs.map +1 -1
  137. package/dist/config/config_parser.d.cts +10 -2
  138. package/dist/config/config_parser.d.ts +10 -2
  139. package/dist/config/config_parser.js +92 -3
  140. package/dist/config/config_parser.js.map +1 -1
  141. package/dist/config/index.cjs +6 -2
  142. package/dist/config/index.cjs.map +1 -1
  143. package/dist/config/index.d.cts +2 -2
  144. package/dist/config/index.d.ts +2 -2
  145. package/dist/config/index.js +8 -2
  146. package/dist/config/index.js.map +1 -1
  147. package/dist/controller/controller_resolver.cjs +51 -3
  148. package/dist/controller/controller_resolver.cjs.map +1 -1
  149. package/dist/controller/controller_resolver.d.cts +27 -4
  150. package/dist/controller/controller_resolver.d.ts +27 -4
  151. package/dist/controller/controller_resolver.js +48 -2
  152. package/dist/controller/controller_resolver.js.map +1 -1
  153. package/dist/controller/index.cjs +6 -2
  154. package/dist/controller/index.cjs.map +1 -1
  155. package/dist/controller/index.d.cts +1 -1
  156. package/dist/controller/index.d.ts +1 -1
  157. package/dist/controller/index.js +4 -2
  158. package/dist/controller/index.js.map +1 -1
  159. package/dist/dev/wood_dev_server.cjs +4 -1
  160. package/dist/dev/wood_dev_server.cjs.map +1 -1
  161. package/dist/dev/wood_dev_server.js +5 -2
  162. package/dist/dev/wood_dev_server.js.map +1 -1
  163. package/dist/dev/yaml_watcher.cjs +1 -1
  164. package/dist/dev/yaml_watcher.cjs.map +1 -1
  165. package/dist/dev/yaml_watcher.d.cts +4 -0
  166. package/dist/dev/yaml_watcher.d.ts +4 -0
  167. package/dist/dev/yaml_watcher.js +1 -1
  168. package/dist/dev/yaml_watcher.js.map +1 -1
  169. package/dist/frontend/frontend_lifecycle.cjs +74 -0
  170. package/dist/frontend/frontend_lifecycle.cjs.map +1 -0
  171. package/dist/frontend/frontend_lifecycle.d.cts +50 -0
  172. package/dist/frontend/frontend_lifecycle.d.ts +50 -0
  173. package/dist/frontend/frontend_lifecycle.js +50 -0
  174. package/dist/frontend/frontend_lifecycle.js.map +1 -0
  175. package/dist/frontend/frontend_page.cjs +122 -0
  176. package/dist/frontend/frontend_page.cjs.map +1 -0
  177. package/dist/frontend/frontend_page.d.cts +62 -0
  178. package/dist/frontend/frontend_page.d.ts +62 -0
  179. package/dist/frontend/frontend_page.js +98 -0
  180. package/dist/frontend/frontend_page.js.map +1 -0
  181. package/dist/frontend/frontend_surface.cjs +249 -0
  182. package/dist/frontend/frontend_surface.cjs.map +1 -0
  183. package/dist/frontend/frontend_surface.d.cts +64 -0
  184. package/dist/frontend/frontend_surface.d.ts +64 -0
  185. package/dist/frontend/frontend_surface.js +224 -0
  186. package/dist/frontend/frontend_surface.js.map +1 -0
  187. package/dist/frontend/index.cjs +52 -0
  188. package/dist/frontend/index.cjs.map +1 -0
  189. package/dist/frontend/index.d.cts +10 -0
  190. package/dist/frontend/index.d.ts +10 -0
  191. package/dist/frontend/index.js +19 -0
  192. package/dist/frontend/index.js.map +1 -0
  193. package/dist/frontend/page_catalog.cjs +135 -0
  194. package/dist/frontend/page_catalog.cjs.map +1 -0
  195. package/dist/frontend/page_catalog.d.cts +56 -0
  196. package/dist/frontend/page_catalog.d.ts +56 -0
  197. package/dist/frontend/page_catalog.js +108 -0
  198. package/dist/frontend/page_catalog.js.map +1 -0
  199. package/dist/frontend/tracked_transport.cjs +55 -0
  200. package/dist/frontend/tracked_transport.cjs.map +1 -0
  201. package/dist/frontend/tracked_transport.d.cts +36 -0
  202. package/dist/frontend/tracked_transport.d.ts +36 -0
  203. package/dist/frontend/tracked_transport.js +31 -0
  204. package/dist/frontend/tracked_transport.js.map +1 -0
  205. package/dist/frontend/work_registry.cjs +153 -0
  206. package/dist/frontend/work_registry.cjs.map +1 -0
  207. package/dist/frontend/work_registry.d.cts +70 -0
  208. package/dist/frontend/work_registry.d.ts +70 -0
  209. package/dist/frontend/work_registry.js +128 -0
  210. package/dist/frontend/work_registry.js.map +1 -0
  211. package/dist/index.cjs +24 -0
  212. package/dist/index.cjs.map +1 -1
  213. package/dist/index.d.cts +11 -7
  214. package/dist/index.d.ts +11 -7
  215. package/dist/index.js +27 -4
  216. package/dist/index.js.map +1 -1
  217. package/dist/main/index.cjs +10 -0
  218. package/dist/main/index.cjs.map +1 -1
  219. package/dist/main/index.d.cts +14 -2
  220. package/dist/main/index.d.ts +14 -2
  221. package/dist/main/index.js +16 -0
  222. package/dist/main/index.js.map +1 -1
  223. package/dist/middleware/middleware_resolver.cjs +22 -1
  224. package/dist/middleware/middleware_resolver.cjs.map +1 -1
  225. package/dist/middleware/middleware_resolver.d.cts +13 -1
  226. package/dist/middleware/middleware_resolver.d.ts +13 -1
  227. package/dist/middleware/middleware_resolver.js +22 -1
  228. package/dist/middleware/middleware_resolver.js.map +1 -1
  229. package/dist/navigation/automation_introspection.cjs +110 -0
  230. package/dist/navigation/automation_introspection.cjs.map +1 -0
  231. package/dist/navigation/automation_introspection.d.cts +60 -0
  232. package/dist/navigation/automation_introspection.d.ts +60 -0
  233. package/dist/navigation/automation_introspection.js +85 -0
  234. package/dist/navigation/automation_introspection.js.map +1 -0
  235. package/dist/navigation/index.cjs +10 -2
  236. package/dist/navigation/index.cjs.map +1 -1
  237. package/dist/navigation/index.d.cts +27 -1
  238. package/dist/navigation/index.d.ts +27 -1
  239. package/dist/navigation/index.js +6 -1
  240. package/dist/navigation/index.js.map +1 -1
  241. package/dist/{middleware/__fixtures__/middleware/auth.cjs → navigation/layout_preservation.cjs} +18 -20
  242. package/dist/navigation/layout_preservation.cjs.map +1 -0
  243. package/dist/navigation/layout_preservation.d.cts +2 -0
  244. package/dist/navigation/layout_preservation.d.ts +2 -0
  245. package/dist/navigation/layout_preservation.js +18 -0
  246. package/dist/navigation/layout_preservation.js.map +1 -0
  247. package/dist/parser/index.cjs +2 -0
  248. package/dist/parser/index.cjs.map +1 -1
  249. package/dist/parser/index.d.cts +1 -1
  250. package/dist/parser/index.d.ts +1 -1
  251. package/dist/parser/index.js +2 -1
  252. package/dist/parser/index.js.map +1 -1
  253. package/dist/parser/operations_parser.cjs +6 -0
  254. package/dist/parser/operations_parser.cjs.map +1 -1
  255. package/dist/parser/operations_parser.d.cts +9 -2
  256. package/dist/parser/operations_parser.d.ts +9 -2
  257. package/dist/parser/operations_parser.js +5 -0
  258. package/dist/parser/operations_parser.js.map +1 -1
  259. package/dist/parser/views_parser.cjs.map +1 -1
  260. package/dist/parser/views_parser.js.map +1 -1
  261. package/dist/router/index.d.cts +1 -0
  262. package/dist/router/index.d.ts +1 -0
  263. package/dist/router/wood_router.cjs +35 -3
  264. package/dist/router/wood_router.cjs.map +1 -1
  265. package/dist/router/wood_router.d.cts +15 -0
  266. package/dist/router/wood_router.d.ts +15 -0
  267. package/dist/router/wood_router.js +36 -4
  268. package/dist/router/wood_router.js.map +1 -1
  269. package/dist/runtime/get_container.cjs +9 -34
  270. package/dist/runtime/get_container.cjs.map +1 -1
  271. package/dist/runtime/get_container.d.cts +24 -4
  272. package/dist/runtime/get_container.d.ts +24 -4
  273. package/dist/runtime/get_container.js +8 -23
  274. package/dist/runtime/get_container.js.map +1 -1
  275. package/dist/runtime/index.cjs +9 -0
  276. package/dist/runtime/index.cjs.map +1 -1
  277. package/dist/runtime/index.d.cts +5 -2
  278. package/dist/runtime/index.d.ts +5 -2
  279. package/dist/runtime/index.js +10 -1
  280. package/dist/runtime/index.js.map +1 -1
  281. package/dist/runtime/renderer_config_extension.cjs +50 -0
  282. package/dist/runtime/renderer_config_extension.cjs.map +1 -0
  283. package/dist/runtime/renderer_config_extension.d.cts +19 -0
  284. package/dist/runtime/renderer_config_extension.d.ts +19 -0
  285. package/dist/runtime/renderer_config_extension.js +24 -0
  286. package/dist/runtime/renderer_config_extension.js.map +1 -0
  287. package/dist/runtime/renderer_log_batch_receiver.cjs +73 -0
  288. package/dist/runtime/renderer_log_batch_receiver.cjs.map +1 -0
  289. package/dist/runtime/renderer_log_batch_receiver.d.cts +29 -0
  290. package/dist/runtime/renderer_log_batch_receiver.d.ts +29 -0
  291. package/dist/runtime/renderer_log_batch_receiver.js +48 -0
  292. package/dist/runtime/renderer_log_batch_receiver.js.map +1 -0
  293. package/dist/runtime/renderer_trace_batch_receiver_registry.d.cts +1 -1
  294. package/dist/runtime/renderer_trace_batch_receiver_registry.d.ts +1 -1
  295. package/dist/runtime/runtime.cjs +14 -0
  296. package/dist/runtime/runtime.cjs.map +1 -1
  297. package/dist/runtime/runtime.d.cts +2 -1
  298. package/dist/runtime/runtime.d.ts +2 -1
  299. package/dist/runtime/runtime.js +14 -0
  300. package/dist/runtime/runtime.js.map +1 -1
  301. package/dist/stick/index.cjs.map +1 -1
  302. package/dist/stick/index.js.map +1 -1
  303. package/dist/testing/automation_assertions.cjs +417 -0
  304. package/dist/testing/automation_assertions.cjs.map +1 -0
  305. package/dist/testing/automation_assertions.d.cts +44 -0
  306. package/dist/testing/automation_assertions.d.ts +44 -0
  307. package/dist/testing/automation_assertions.js +385 -0
  308. package/dist/testing/automation_assertions.js.map +1 -0
  309. package/dist/testing/browser.cjs +130 -57
  310. package/dist/testing/browser.cjs.map +1 -1
  311. package/dist/testing/browser.d.cts +10 -4
  312. package/dist/testing/browser.d.ts +10 -4
  313. package/dist/testing/browser.js +132 -53
  314. package/dist/testing/browser.js.map +1 -1
  315. package/dist/testing/create_controller_harness.cjs +28 -8
  316. package/dist/testing/create_controller_harness.cjs.map +1 -1
  317. package/dist/testing/create_controller_harness.d.cts +18 -2
  318. package/dist/testing/create_controller_harness.d.ts +18 -2
  319. package/dist/testing/create_controller_harness.js +28 -8
  320. package/dist/testing/create_controller_harness.js.map +1 -1
  321. package/dist/testing/create_router_bridge.d.cts +1 -0
  322. package/dist/testing/create_router_bridge.d.ts +1 -0
  323. package/dist/testing/create_test_router.d.cts +1 -0
  324. package/dist/testing/create_test_router.d.ts +1 -0
  325. package/dist/testing/geometry.cjs +109 -0
  326. package/dist/testing/geometry.cjs.map +1 -0
  327. package/dist/testing/geometry.d.cts +45 -0
  328. package/dist/testing/geometry.d.ts +45 -0
  329. package/dist/testing/geometry.js +79 -0
  330. package/dist/testing/geometry.js.map +1 -0
  331. package/dist/testing/index.cjs +62 -2
  332. package/dist/testing/index.cjs.map +1 -1
  333. package/dist/testing/index.d.cts +23 -4
  334. package/dist/testing/index.d.ts +23 -4
  335. package/dist/testing/index.js +50 -2
  336. package/dist/testing/index.js.map +1 -1
  337. package/dist/{controller/__fixtures__/controllers/not_a_class.cjs → testing/test_wood.cjs} +15 -7
  338. package/dist/testing/test_wood.cjs.map +1 -0
  339. package/dist/testing/test_wood.d.cts +16 -0
  340. package/dist/testing/test_wood.d.ts +16 -0
  341. package/dist/testing/test_wood.js +12 -0
  342. package/dist/testing/test_wood.js.map +1 -0
  343. package/dist/testing/test_wood_config.cjs +272 -0
  344. package/dist/testing/test_wood_config.cjs.map +1 -0
  345. package/dist/testing/test_wood_config.d.cts +111 -0
  346. package/dist/testing/test_wood_config.d.ts +111 -0
  347. package/dist/testing/test_wood_config.js +247 -0
  348. package/dist/testing/test_wood_config.js.map +1 -0
  349. package/dist/testing/wood_config_source.cjs +146 -0
  350. package/dist/testing/wood_config_source.cjs.map +1 -0
  351. package/dist/testing/wood_config_source.d.cts +36 -0
  352. package/dist/testing/wood_config_source.d.ts +36 -0
  353. package/dist/testing/wood_config_source.js +112 -0
  354. package/dist/testing/wood_config_source.js.map +1 -0
  355. package/dist/testing/wood_env_renderer.cjs +210 -0
  356. package/dist/testing/wood_env_renderer.cjs.map +1 -0
  357. package/dist/testing/wood_env_renderer.d.cts +74 -0
  358. package/dist/testing/wood_env_renderer.d.ts +74 -0
  359. package/dist/testing/wood_env_renderer.js +188 -0
  360. package/dist/testing/wood_env_renderer.js.map +1 -0
  361. package/dist/testing/wood_test_selection.cjs +94 -0
  362. package/dist/testing/wood_test_selection.cjs.map +1 -0
  363. package/dist/testing/wood_test_selection.d.cts +39 -0
  364. package/dist/testing/wood_test_selection.d.ts +39 -0
  365. package/dist/testing/wood_test_selection.js +68 -0
  366. package/dist/testing/wood_test_selection.js.map +1 -0
  367. package/dist/trace-testing/index.d.cts +1 -2
  368. package/dist/trace-testing/index.d.ts +1 -2
  369. package/dist/trace-testing/trace_recorder.cjs +1 -1
  370. package/dist/trace-testing/trace_recorder.cjs.map +1 -1
  371. package/dist/trace-testing/trace_recorder.d.cts +1 -2
  372. package/dist/trace-testing/trace_recorder.d.ts +1 -2
  373. package/dist/trace-testing/trace_recorder.js +1 -1
  374. package/dist/trace-testing/trace_recorder.js.map +1 -1
  375. package/dist/trace-transport/index.d.cts +1 -1
  376. package/dist/trace-transport/index.d.ts +1 -1
  377. package/dist/{trace_hub-Cob54qRx.d.cts → trace_hub-Cpo_nVze.d.cts} +21 -1
  378. package/dist/{trace_hub-Cob54qRx.d.ts → trace_hub-Cpo_nVze.d.ts} +21 -1
  379. package/dist/tracing/index.cjs +9 -2
  380. package/dist/tracing/index.cjs.map +1 -1
  381. package/dist/tracing/index.d.cts +3 -2
  382. package/dist/tracing/index.d.ts +3 -2
  383. package/dist/tracing/index.js +7 -1
  384. package/dist/tracing/index.js.map +1 -1
  385. package/dist/tracing/renderer_trace_batch_host.d.cts +1 -1
  386. package/dist/tracing/renderer_trace_batch_host.d.ts +1 -1
  387. package/dist/tracing/renderer_trace_batch_transport.cjs +48 -6
  388. package/dist/tracing/renderer_trace_batch_transport.cjs.map +1 -1
  389. package/dist/tracing/renderer_trace_batch_transport.d.cts +17 -1
  390. package/dist/tracing/renderer_trace_batch_transport.d.ts +17 -1
  391. package/dist/tracing/renderer_trace_batch_transport.js +48 -6
  392. package/dist/tracing/renderer_trace_batch_transport.js.map +1 -1
  393. package/dist/tracing/trace_batch_adapter.cjs +110 -0
  394. package/dist/tracing/trace_batch_adapter.cjs.map +1 -0
  395. package/dist/tracing/trace_batch_adapter.d.cts +36 -0
  396. package/dist/tracing/trace_batch_adapter.d.ts +36 -0
  397. package/dist/tracing/trace_batch_adapter.js +86 -0
  398. package/dist/tracing/trace_batch_adapter.js.map +1 -0
  399. package/dist/tracing/trace_commit_writer.d.cts +1 -1
  400. package/dist/tracing/trace_commit_writer.d.ts +1 -1
  401. package/dist/tracing/trace_hub.cjs +87 -20
  402. package/dist/tracing/trace_hub.cjs.map +1 -1
  403. package/dist/tracing/trace_hub.d.cts +1 -1
  404. package/dist/tracing/trace_hub.d.ts +1 -1
  405. package/dist/tracing/trace_hub.js +85 -20
  406. package/dist/tracing/trace_hub.js.map +1 -1
  407. package/dist/tracing/trace_provider.cjs +21 -6
  408. package/dist/tracing/trace_provider.cjs.map +1 -1
  409. package/dist/tracing/trace_provider.d.cts +13 -4
  410. package/dist/tracing/trace_provider.d.ts +13 -4
  411. package/dist/tracing/trace_provider.js +22 -7
  412. package/dist/tracing/trace_provider.js.map +1 -1
  413. package/dist/tracing/types/wood_trace_batch.types.cjs.map +1 -1
  414. package/dist/tracing/types/wood_trace_batch.types.d.cts +15 -1
  415. package/dist/tracing/types/wood_trace_batch.types.d.ts +15 -1
  416. package/dist/tracing/types/wood_trace_sink.types.d.cts +1 -1
  417. package/dist/tracing/types/wood_trace_sink.types.d.ts +1 -1
  418. package/dist/tracing/wood_trace_batch_contract.cjs +2 -0
  419. package/dist/tracing/wood_trace_batch_contract.cjs.map +1 -1
  420. package/dist/tracing/wood_trace_batch_contract.d.cts +1 -1
  421. package/dist/tracing/wood_trace_batch_contract.d.ts +1 -1
  422. package/dist/tracing/wood_trace_batch_contract.js +2 -0
  423. package/dist/tracing/wood_trace_batch_contract.js.map +1 -1
  424. package/dist/transport/build_transport_bridge.cjs +4 -2
  425. package/dist/transport/build_transport_bridge.cjs.map +1 -1
  426. package/dist/transport/build_transport_bridge.d.cts +7 -1
  427. package/dist/transport/build_transport_bridge.d.ts +7 -1
  428. package/dist/transport/build_transport_bridge.js +2 -1
  429. package/dist/transport/build_transport_bridge.js.map +1 -1
  430. package/dist/transport/in_process_router_transport.d.cts +1 -0
  431. package/dist/transport/in_process_router_transport.d.ts +1 -0
  432. package/dist/transport/index.d.cts +1 -0
  433. package/dist/transport/index.d.ts +1 -0
  434. package/dist/types/config.cjs.map +1 -1
  435. package/dist/types/config.d.cts +47 -1
  436. package/dist/types/config.d.ts +47 -1
  437. package/dist/types/context.cjs +12 -0
  438. package/dist/types/context.cjs.map +1 -1
  439. package/dist/types/context.d.cts +27 -1
  440. package/dist/types/context.d.ts +27 -1
  441. package/dist/types/context.js +4 -0
  442. package/dist/types/context.js.map +1 -1
  443. package/dist/types/index.d.cts +2 -2
  444. package/dist/types/index.d.ts +2 -2
  445. package/dist/types/operations.cjs.map +1 -1
  446. package/dist/types/operations.d.cts +2 -0
  447. package/dist/types/operations.d.ts +2 -0
  448. package/docs/axe.md +706 -0
  449. package/package.json +19 -5
  450. package/src/components/NavigationShell.scopes.browser.test.ts +363 -0
  451. package/src/components/NavigationShell.svelte +126 -49
  452. package/dist/controller/__fixtures__/controllers/chat.cjs +0 -32
  453. package/dist/controller/__fixtures__/controllers/chat.cjs.map +0 -1
  454. package/dist/controller/__fixtures__/controllers/chat.d.cts +0 -11
  455. package/dist/controller/__fixtures__/controllers/chat.d.ts +0 -11
  456. package/dist/controller/__fixtures__/controllers/chat.js +0 -12
  457. package/dist/controller/__fixtures__/controllers/chat.js.map +0 -1
  458. package/dist/controller/__fixtures__/controllers/not_a_class.cjs.map +0 -1
  459. package/dist/controller/__fixtures__/controllers/not_a_class.d.cts +0 -3
  460. package/dist/controller/__fixtures__/controllers/not_a_class.d.ts +0 -3
  461. package/dist/controller/__fixtures__/controllers/not_a_class.js +0 -5
  462. package/dist/controller/__fixtures__/controllers/not_a_class.js.map +0 -1
  463. package/dist/controller/__fixtures__/controllers/settings.controller.cjs +0 -32
  464. package/dist/controller/__fixtures__/controllers/settings.controller.cjs.map +0 -1
  465. package/dist/controller/__fixtures__/controllers/settings.controller.d.cts +0 -12
  466. package/dist/controller/__fixtures__/controllers/settings.controller.d.ts +0 -12
  467. package/dist/controller/__fixtures__/controllers/settings.controller.js +0 -12
  468. package/dist/controller/__fixtures__/controllers/settings.controller.js.map +0 -1
  469. package/dist/controller/__fixtures__/controllers/test.cjs.map +0 -1
  470. package/dist/controller/__fixtures__/controllers/test.d.cts +0 -7
  471. package/dist/controller/__fixtures__/controllers/test.d.ts +0 -7
  472. package/dist/controller/__fixtures__/controllers/test.js +0 -9
  473. package/dist/controller/__fixtures__/controllers/test.js.map +0 -1
  474. package/dist/loader/__fixtures__/layouts/app.load.cjs.map +0 -1
  475. package/dist/loader/__fixtures__/layouts/app.load.d.cts +0 -5
  476. package/dist/loader/__fixtures__/layouts/app.load.d.ts +0 -5
  477. package/dist/loader/__fixtures__/layouts/app.load.js +0 -7
  478. package/dist/loader/__fixtures__/layouts/app.load.js.map +0 -1
  479. package/dist/loader/__fixtures__/views/test/page.load.cjs.map +0 -1
  480. package/dist/loader/__fixtures__/views/test/page.load.d.cts +0 -6
  481. package/dist/loader/__fixtures__/views/test/page.load.d.ts +0 -6
  482. package/dist/loader/__fixtures__/views/test/page.load.js +0 -7
  483. package/dist/loader/__fixtures__/views/test/page.load.js.map +0 -1
  484. package/dist/middleware/__fixtures__/middleware/auth.cjs.map +0 -1
  485. package/dist/middleware/__fixtures__/middleware/auth.d.cts +0 -6
  486. package/dist/middleware/__fixtures__/middleware/auth.d.ts +0 -6
  487. package/dist/middleware/__fixtures__/middleware/auth.js +0 -19
  488. package/dist/middleware/__fixtures__/middleware/auth.js.map +0 -1
  489. package/dist/middleware/__fixtures__/middleware/sequence.cjs.map +0 -1
  490. package/dist/middleware/__fixtures__/middleware/sequence.d.cts +0 -7
  491. package/dist/middleware/__fixtures__/middleware/sequence.d.ts +0 -7
  492. package/dist/middleware/__fixtures__/middleware/sequence.js +0 -21
  493. package/dist/middleware/__fixtures__/middleware/sequence.js.map +0 -1
@@ -22,6 +22,7 @@ __export(create_controller_harness_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(create_controller_harness_exports);
24
24
  var import_node_fs = require("node:fs");
25
+ var import_ioc = require("@noego/ioc");
25
26
  var import_client = require("../client/index.js");
26
27
  var import_ipc = require("../ipc/index.js");
27
28
  var import_navigation = require("../navigation/index.js");
@@ -29,6 +30,8 @@ var import_tracing = require("../tracing/index.js");
29
30
  var import_trace_testing = require("../trace-testing/index.js");
30
31
  var import_operations_parser = require("../parser/operations_parser.cjs");
31
32
  var import_controller_lifecycle = require("../controller/controller_lifecycle.cjs");
33
+ var import_get_container = require("../runtime/get_container.js");
34
+ var import_build_transport_bridge = require("../transport/build_transport_bridge.js");
32
35
  var import_create_router_bridge = require("./create_router_bridge.js");
33
36
  var import_create_test_router = require("./create_test_router.js");
34
37
  function readOperations(input) {
@@ -52,7 +55,7 @@ function createRpcManifest(operations) {
52
55
  controller: operation.controller,
53
56
  action: operation.action,
54
57
  path: operation.channel.split("."),
55
- hasInput: !!operation.inputSchema
58
+ hasInput: (0, import_build_transport_bridge.operationHasInput)(operation)
56
59
  }));
57
60
  }
58
61
  function registerClientBuiltins(container) {
@@ -63,11 +66,12 @@ function registerClientBuiltins(container) {
63
66
  traceConfiguration.configureMode("test");
64
67
  traceConfiguration.configureRuntime("renderer");
65
68
  traceConfiguration.configureEnabled(true);
66
- container.registerFunction(import_navigation.Navigation, () => (0, import_navigation.getNavigation)());
67
- container.registerFunction(import_client.App, () => (0, import_client.getApp)());
68
- container.registerFunction(import_client.Ipc, () => (0, import_ipc.getIpc)());
69
- container.registerFunction(WindowControls, () => (0, import_client.getWindowControls)());
70
- container.registerFunction(import_tracing.TraceProviderConfiguration, () => traceConfiguration);
69
+ const singleton = { loadAs: import_ioc.LoadAs.Singleton };
70
+ container.registerFunction(import_navigation.Navigation, () => (0, import_navigation.getNavigation)(), singleton);
71
+ container.registerFunction(import_client.App, () => (0, import_client.getApp)(), singleton);
72
+ container.registerFunction(import_client.Ipc, () => (0, import_ipc.getIpc)(), singleton);
73
+ container.registerFunction(WindowControls, () => (0, import_client.getWindowControls)(), singleton);
74
+ container.registerFunction(import_tracing.TraceProviderConfiguration, () => traceConfiguration, singleton);
71
75
  }
72
76
  class WindowControls {
73
77
  async open(_windowId, _options) {
@@ -110,7 +114,7 @@ async function createControllerHarness(options) {
110
114
  (0, import_client.clearClientNavigationForTests)();
111
115
  (0, import_client.clearClientWindowControlsForTests)();
112
116
  (0, import_client.clearClientLoggersForTests)();
113
- const scopedContainer = options.container.extend();
117
+ const scopedContainer = options.clientContainer === "extend" ? options.container.extend() : (0, import_get_container.createWoodRoot)();
114
118
  registerClientBuiltins(scopedContainer);
115
119
  if (typeof scopedContainer.registerClass === "function") {
116
120
  scopedContainer.registerClass(options.controllerClass);
@@ -120,9 +124,24 @@ async function createControllerHarness(options) {
120
124
  bridge: harnessBridge,
121
125
  events,
122
126
  navigation,
123
- container: scopedContainer
127
+ container: scopedContainer,
128
+ backendContainer: options.container
124
129
  });
125
130
  const controller = await scopedContainer.instance(options.controllerClass);
131
+ const bridgeHandlers = (surface) => {
132
+ if (!surface || typeof surface !== "object") return;
133
+ const record = surface;
134
+ for (const key of Object.keys(record)) {
135
+ const handler = record[key];
136
+ if (typeof handler !== "function") continue;
137
+ record[key] = (...args) => import_ioc.ExecutionContext.current() !== void 0 ? handler(...args) : import_ioc.ExecutionContext.run(
138
+ scopedContainer,
139
+ () => handler(...args)
140
+ );
141
+ }
142
+ };
143
+ bridgeHandlers(controller.input);
144
+ bridgeHandlers(controller.events);
126
145
  const trace = await scopedContainer.instance(import_tracing.TraceProvider);
127
146
  const traceRecorder = await scopedContainer.instance(import_trace_testing.TraceRecorder);
128
147
  if (options.initialize && typeof controller.initialize === "function") {
@@ -143,6 +162,7 @@ async function createControllerHarness(options) {
143
162
  events,
144
163
  router,
145
164
  container: scopedContainer,
165
+ backendContainer: options.container,
146
166
  navigation,
147
167
  traceRecorder,
148
168
  destroy: async () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { TraceRecorder } from '../trace-testing/index.js';\nimport { parseOperations } from '../parser/operations_parser.cjs';\nimport type { OperationDef } from '../types/operations.cjs';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.cjs';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n traceRecorder: TraceRecorder;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n const traceRecorder = await scopedContainer.instance(TraceRecorder);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n traceRecorder,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { LoadAs, ExecutionContext } from '@noego/ioc';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { TraceRecorder } from '../trace-testing/index.js';\nimport { parseOperations } from '../parser/operations_parser.cjs';\nimport type { OperationDef } from '../types/operations.cjs';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.cjs';\nimport { createWoodRoot } from '../runtime/get_container.js';\nimport { operationHasInput } from '../transport/build_transport_bridge.js';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n /** The renderer root (fresh per harness). */\n container: any;\n /** The backend root (options.container) — resolve backend services here. */\n backendContainer: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n /**\n * How the client (renderer) container relates to the backend root.\n * 'fresh-root' (default) mirrors production's process split: a separate\n * renderer hierarchy, cross-root traffic through the bridge globals only.\n * 'extend' is the merged node-frontend tier: renderer services resolve\n * backend singletons directly in one hierarchy — for integration tests\n * that intentionally span both sides in-process.\n */\n clientContainer?: 'fresh-root' | 'extend';\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n /** The renderer root the controller lives in (fresh per harness). */\n container: any;\n /** The backend root supplied via options.container — resolve backend services here. */\n backendContainer: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n traceRecorder: TraceRecorder;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: operationHasInput(operation),\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n // Explicit Singleton lifetime: these factories return root-global values,\n // and registerFunction defaults to Transient, which would override the\n // classes' own @Component(Singleton) declarations and trip ioc's\n // captive-lifetime validation for singleton dependents.\n const singleton = { loadAs: LoadAs.Singleton };\n container.registerFunction(Navigation, () => getNavigation(), singleton);\n container.registerFunction(App, () => getApp(), singleton);\n container.registerFunction(Ipc, () => getIpc(), singleton);\n container.registerFunction(WindowControls, () => getWindowControls(), singleton);\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration, singleton);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n // The client container is a FRESH RENDERER ROOT, mirroring production\n // (renderer and main are separate processes with separate roots). A child\n // scope of the backend root would share singleton storage, so renderer\n // builtins (notably TraceProviderConfiguration with runtime 'renderer')\n // would collide with the backend's own singletons. Cross-root traffic\n // flows through the bridge globals, exactly as in production.\n const scopedContainer = options.clientContainer === 'extend'\n ? options.container.extend()\n : (createWoodRoot() as any);\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n backendContainer: options.container,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n // Controller interaction surfaces (input/events handlers) are plain data\n // properties holding arrow functions — they bypass the IoC context proxy,\n // so invoking them from a test would run context-less and singleton\n // collaborators (e.g. TraceProvider) would resolve root-scope state\n // instead of this harness scope's registrations. Bridge them so every\n // handler runs under the harness scope unless a context is already active.\n const bridgeHandlers = (surface: unknown): void => {\n if (!surface || typeof surface !== 'object') return;\n const record = surface as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n const handler = record[key];\n if (typeof handler !== 'function') continue;\n record[key] = (...args: unknown[]) =>\n ExecutionContext.current() !== undefined\n ? (handler as (...a: unknown[]) => unknown)(...args)\n : ExecutionContext.run(scopedContainer as any, () =>\n (handler as (...a: unknown[]) => unknown)(...args),\n );\n }\n };\n bridgeHandlers((controller as { input?: unknown }).input);\n bridgeHandlers((controller as { events?: unknown }).events);\n const trace = await scopedContainer.instance(TraceProvider);\n const traceRecorder = await scopedContainer.instance(TraceRecorder);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n backendContainer: options.container,\n navigation,\n traceRecorder,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -2,13 +2,14 @@ import { Navigation } from '../navigation/index.cjs';
2
2
  import { TraceRecorder } from '../trace-testing/trace_recorder.cjs';
3
3
  import { RouterBridgeResult } from './create_router_bridge.cjs';
4
4
  import { createTestRouter } from './create_test_router.cjs';
5
+ import '../navigation/automation_introspection.cjs';
5
6
  import '@noego/trace/testing';
6
- import '../trace_hub-Cob54qRx.cjs';
7
+ import '../trace_hub-Cpo_nVze.cjs';
7
8
  import 'rxjs';
8
9
  import '../tracing/trace_provider.cjs';
9
- import '@noego/ioc';
10
10
  import '../router/wood_router.cjs';
11
11
  import '../types/operations.cjs';
12
+ import '../types/context.cjs';
12
13
 
13
14
  interface CreateControllerHarnessOptions<T> {
14
15
  controllerClass: new (...args: any[]) => T;
@@ -22,13 +23,28 @@ interface CreateControllerHarnessOptions<T> {
22
23
  bridge: Record<string, any>;
23
24
  events: RouterBridgeResult['events'];
24
25
  navigation: Navigation;
26
+ /** The renderer root (fresh per harness). */
25
27
  container: any;
28
+ /** The backend root (options.container) — resolve backend services here. */
29
+ backendContainer: any;
26
30
  }) => void | Promise<void>;
27
31
  initialize?: boolean;
32
+ /**
33
+ * How the client (renderer) container relates to the backend root.
34
+ * 'fresh-root' (default) mirrors production's process split: a separate
35
+ * renderer hierarchy, cross-root traffic through the bridge globals only.
36
+ * 'extend' is the merged node-frontend tier: renderer services resolve
37
+ * backend singletons directly in one hierarchy — for integration tests
38
+ * that intentionally span both sides in-process.
39
+ */
40
+ clientContainer?: 'fresh-root' | 'extend';
28
41
  }
29
42
  interface ControllerHarnessResult<T> extends RouterBridgeResult {
30
43
  controller: T;
44
+ /** The renderer root the controller lives in (fresh per harness). */
31
45
  container: any;
46
+ /** The backend root supplied via options.container — resolve backend services here. */
47
+ backendContainer: any;
32
48
  router: ReturnType<typeof createTestRouter>['router'];
33
49
  navigation: Navigation;
34
50
  traceRecorder: TraceRecorder;
@@ -2,13 +2,14 @@ import { Navigation } from '../navigation/index.js';
2
2
  import { TraceRecorder } from '../trace-testing/trace_recorder.js';
3
3
  import { RouterBridgeResult } from './create_router_bridge.js';
4
4
  import { createTestRouter } from './create_test_router.js';
5
+ import '../navigation/automation_introspection.js';
5
6
  import '@noego/trace/testing';
6
- import '../trace_hub-Cob54qRx.js';
7
+ import '../trace_hub-Cpo_nVze.js';
7
8
  import 'rxjs';
8
9
  import '../tracing/trace_provider.js';
9
- import '@noego/ioc';
10
10
  import '../router/wood_router.js';
11
11
  import '../types/operations.js';
12
+ import '../types/context.js';
12
13
 
13
14
  interface CreateControllerHarnessOptions<T> {
14
15
  controllerClass: new (...args: any[]) => T;
@@ -22,13 +23,28 @@ interface CreateControllerHarnessOptions<T> {
22
23
  bridge: Record<string, any>;
23
24
  events: RouterBridgeResult['events'];
24
25
  navigation: Navigation;
26
+ /** The renderer root (fresh per harness). */
25
27
  container: any;
28
+ /** The backend root (options.container) — resolve backend services here. */
29
+ backendContainer: any;
26
30
  }) => void | Promise<void>;
27
31
  initialize?: boolean;
32
+ /**
33
+ * How the client (renderer) container relates to the backend root.
34
+ * 'fresh-root' (default) mirrors production's process split: a separate
35
+ * renderer hierarchy, cross-root traffic through the bridge globals only.
36
+ * 'extend' is the merged node-frontend tier: renderer services resolve
37
+ * backend singletons directly in one hierarchy — for integration tests
38
+ * that intentionally span both sides in-process.
39
+ */
40
+ clientContainer?: 'fresh-root' | 'extend';
28
41
  }
29
42
  interface ControllerHarnessResult<T> extends RouterBridgeResult {
30
43
  controller: T;
44
+ /** The renderer root the controller lives in (fresh per harness). */
31
45
  container: any;
46
+ /** The backend root supplied via options.container — resolve backend services here. */
47
+ backendContainer: any;
32
48
  router: ReturnType<typeof createTestRouter>['router'];
33
49
  navigation: Navigation;
34
50
  traceRecorder: TraceRecorder;
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { LoadAs, ExecutionContext } from "@noego/ioc";
2
3
  import {
3
4
  App,
4
5
  clearClientAppForTests,
@@ -17,6 +18,8 @@ import { TraceProvider, TraceProviderConfiguration } from "../tracing/index.js";
17
18
  import { TraceRecorder } from "../trace-testing/index.js";
18
19
  import { parseOperations } from "../parser/operations_parser.js";
19
20
  import { disposeControllerLifecycle } from "../controller/controller_lifecycle.js";
21
+ import { createWoodRoot } from "../runtime/get_container.js";
22
+ import { operationHasInput } from "../transport/build_transport_bridge.js";
20
23
  import { createRouterBridge } from "./create_router_bridge.js";
21
24
  import { createTestRouter } from "./create_test_router.js";
22
25
  function readOperations(input) {
@@ -40,7 +43,7 @@ function createRpcManifest(operations) {
40
43
  controller: operation.controller,
41
44
  action: operation.action,
42
45
  path: operation.channel.split("."),
43
- hasInput: !!operation.inputSchema
46
+ hasInput: operationHasInput(operation)
44
47
  }));
45
48
  }
46
49
  function registerClientBuiltins(container) {
@@ -51,11 +54,12 @@ function registerClientBuiltins(container) {
51
54
  traceConfiguration.configureMode("test");
52
55
  traceConfiguration.configureRuntime("renderer");
53
56
  traceConfiguration.configureEnabled(true);
54
- container.registerFunction(Navigation, () => getNavigation());
55
- container.registerFunction(App, () => getApp());
56
- container.registerFunction(Ipc, () => getIpc());
57
- container.registerFunction(WindowControls, () => getWindowControls());
58
- container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);
57
+ const singleton = { loadAs: LoadAs.Singleton };
58
+ container.registerFunction(Navigation, () => getNavigation(), singleton);
59
+ container.registerFunction(App, () => getApp(), singleton);
60
+ container.registerFunction(Ipc, () => getIpc(), singleton);
61
+ container.registerFunction(WindowControls, () => getWindowControls(), singleton);
62
+ container.registerFunction(TraceProviderConfiguration, () => traceConfiguration, singleton);
59
63
  }
60
64
  class WindowControls {
61
65
  async open(_windowId, _options) {
@@ -98,7 +102,7 @@ async function createControllerHarness(options) {
98
102
  clearClientNavigationForTests();
99
103
  clearClientWindowControlsForTests();
100
104
  clearClientLoggersForTests();
101
- const scopedContainer = options.container.extend();
105
+ const scopedContainer = options.clientContainer === "extend" ? options.container.extend() : createWoodRoot();
102
106
  registerClientBuiltins(scopedContainer);
103
107
  if (typeof scopedContainer.registerClass === "function") {
104
108
  scopedContainer.registerClass(options.controllerClass);
@@ -108,9 +112,24 @@ async function createControllerHarness(options) {
108
112
  bridge: harnessBridge,
109
113
  events,
110
114
  navigation,
111
- container: scopedContainer
115
+ container: scopedContainer,
116
+ backendContainer: options.container
112
117
  });
113
118
  const controller = await scopedContainer.instance(options.controllerClass);
119
+ const bridgeHandlers = (surface) => {
120
+ if (!surface || typeof surface !== "object") return;
121
+ const record = surface;
122
+ for (const key of Object.keys(record)) {
123
+ const handler = record[key];
124
+ if (typeof handler !== "function") continue;
125
+ record[key] = (...args) => ExecutionContext.current() !== void 0 ? handler(...args) : ExecutionContext.run(
126
+ scopedContainer,
127
+ () => handler(...args)
128
+ );
129
+ }
130
+ };
131
+ bridgeHandlers(controller.input);
132
+ bridgeHandlers(controller.events);
114
133
  const trace = await scopedContainer.instance(TraceProvider);
115
134
  const traceRecorder = await scopedContainer.instance(TraceRecorder);
116
135
  if (options.initialize && typeof controller.initialize === "function") {
@@ -131,6 +150,7 @@ async function createControllerHarness(options) {
131
150
  events,
132
151
  router,
133
152
  container: scopedContainer,
153
+ backendContainer: options.container,
134
154
  navigation,
135
155
  traceRecorder,
136
156
  destroy: async () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { TraceRecorder } from '../trace-testing/index.js';\nimport { parseOperations } from '../parser/operations_parser.js';\nimport type { OperationDef } from '../types/operations.js';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.js';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n container: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n container: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n traceRecorder: TraceRecorder;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: !!operation.inputSchema,\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n container.registerFunction(Navigation, () => getNavigation());\n container.registerFunction(App, () => getApp());\n container.registerFunction(Ipc, () => getIpc());\n container.registerFunction(WindowControls, () => getWindowControls());\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n const scopedContainer = options.container.extend();\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n const trace = await scopedContainer.instance(TraceProvider);\n const traceRecorder = await scopedContainer.instance(TraceRecorder);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n navigation,\n traceRecorder,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/testing/create_controller_harness.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { LoadAs, ExecutionContext } from '@noego/ioc';\nimport {\n App,\n clearClientAppForTests,\n clearClientContainerForTests,\n clearClientLoggersForTests,\n clearClientNavigationForTests,\n clearClientWindowControlsForTests,\n getApp,\n getWindowControls,\n Ipc,\n WOOD_INTERNAL_BRIDGE_KEY,\n} from '../client/index.js';\nimport { getIpc } from '../ipc/index.js';\nimport { Navigation, getNavigation } from '../navigation/index.js';\nimport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nimport { TraceRecorder } from '../trace-testing/index.js';\nimport { parseOperations } from '../parser/operations_parser.js';\nimport type { OperationDef } from '../types/operations.js';\nimport { disposeControllerLifecycle } from '../controller/controller_lifecycle.js';\nimport { createWoodRoot } from '../runtime/get_container.js';\nimport { operationHasInput } from '../transport/build_transport_bridge.js';\nimport { createRouterBridge, type RouterBridgeResult } from './create_router_bridge.js';\nimport { createTestRouter } from './create_test_router.js';\n\nexport interface CreateControllerHarnessOptions<T> {\n controllerClass: new (...args: any[]) => T;\n container: any;\n operations: string | string[];\n controllersDir: string;\n middlewareDir?: string;\n configureContainer?: (container: any) => void | Promise<void>;\n configureRouter?: (router: ReturnType<typeof createTestRouter>['router']) => void | Promise<void>;\n prepareClient?: (context: {\n bridge: Record<string, any>;\n events: RouterBridgeResult['events'];\n navigation: Navigation;\n /** The renderer root (fresh per harness). */\n container: any;\n /** The backend root (options.container) — resolve backend services here. */\n backendContainer: any;\n }) => void | Promise<void>;\n initialize?: boolean;\n /**\n * How the client (renderer) container relates to the backend root.\n * 'fresh-root' (default) mirrors production's process split: a separate\n * renderer hierarchy, cross-root traffic through the bridge globals only.\n * 'extend' is the merged node-frontend tier: renderer services resolve\n * backend singletons directly in one hierarchy — for integration tests\n * that intentionally span both sides in-process.\n */\n clientContainer?: 'fresh-root' | 'extend';\n}\n\nexport interface ControllerHarnessResult<T> extends RouterBridgeResult {\n controller: T;\n /** The renderer root the controller lives in (fresh per harness). */\n container: any;\n /** The backend root supplied via options.container — resolve backend services here. */\n backendContainer: any;\n router: ReturnType<typeof createTestRouter>['router'];\n navigation: Navigation;\n traceRecorder: TraceRecorder;\n destroy: () => void | Promise<void>;\n}\n\ntype MutableGlobal = typeof globalThis & {\n [WOOD_INTERNAL_BRIDGE_KEY]?: unknown;\n wood?: unknown;\n};\n\nfunction readOperations(input: string): Map<string, OperationDef> {\n if (input.includes('\\n') || input.includes(':')) {\n return parseOperations(input);\n }\n\n return parseOperations(readFileSync(input, 'utf-8'));\n}\n\nfunction mergeOperations(inputs: string | string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n\n for (const input of Array.isArray(inputs) ? inputs : [inputs]) {\n for (const [channel, operation] of readOperations(input)) {\n merged.set(channel, operation);\n }\n }\n\n return merged;\n}\n\nfunction createRpcManifest(operations: Map<string, OperationDef>) {\n return Array.from(operations.values()).map((operation) => ({\n channel: operation.channel,\n controller: operation.controller,\n action: operation.action,\n path: operation.channel.split('.'),\n hasInput: operationHasInput(operation),\n }));\n}\n\nfunction registerClientBuiltins(container: any): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureMode('test');\n traceConfiguration.configureRuntime('renderer');\n traceConfiguration.configureEnabled(true);\n\n // Explicit Singleton lifetime: these factories return root-global values,\n // and registerFunction defaults to Transient, which would override the\n // classes' own @Component(Singleton) declarations and trip ioc's\n // captive-lifetime validation for singleton dependents.\n const singleton = { loadAs: LoadAs.Singleton };\n container.registerFunction(Navigation, () => getNavigation(), singleton);\n container.registerFunction(App, () => getApp(), singleton);\n container.registerFunction(Ipc, () => getIpc(), singleton);\n container.registerFunction(WindowControls, () => getWindowControls(), singleton);\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration, singleton);\n}\n\nclass WindowControls {\n async open(_windowId: string, _options?: Record<string, unknown>): Promise<void> {}\n async close(_windowId?: string): Promise<void> {}\n async focus(_windowId: string): Promise<void> {}\n async minimize(_windowId?: string): Promise<void> {}\n async maximize(_windowId?: string): Promise<void> {}\n}\n\nexport async function createControllerHarness<T>(\n options: CreateControllerHarnessOptions<T>,\n): Promise<ControllerHarnessResult<T>> {\n await options.configureContainer?.(options.container);\n\n if (typeof options.container.instance === 'function') {\n const rootTraceConfiguration = await options.container.instance(TraceProviderConfiguration);\n rootTraceConfiguration.configureMode('test');\n rootTraceConfiguration.configureRuntime('main');\n }\n\n const operations = mergeOperations(options.operations);\n const { router } = createTestRouter('', {\n container: options.container,\n controllersDir: options.controllersDir,\n middlewareDir: options.middlewareDir,\n });\n router.loadOperations(operations);\n await options.configureRouter?.(router);\n\n const { bridge, events } = createRouterBridge(router);\n const harnessBridge = Object.assign(bridge, {\n __rpcManifest: createRpcManifest(operations),\n });\n\n const globalObj = globalThis as MutableGlobal;\n const previousInternalBridge = globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n const previousWoodBridge = globalObj.wood;\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = harnessBridge;\n globalObj.wood = harnessBridge;\n\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n // The client container is a FRESH RENDERER ROOT, mirroring production\n // (renderer and main are separate processes with separate roots). A child\n // scope of the backend root would share singleton storage, so renderer\n // builtins (notably TraceProviderConfiguration with runtime 'renderer')\n // would collide with the backend's own singletons. Cross-root traffic\n // flows through the bridge globals, exactly as in production.\n const scopedContainer = options.clientContainer === 'extend'\n ? options.container.extend()\n : (createWoodRoot() as any);\n registerClientBuiltins(scopedContainer);\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(options.controllerClass);\n }\n const navigation = getNavigation();\n await options.prepareClient?.({\n bridge: harnessBridge,\n events,\n navigation,\n container: scopedContainer,\n backendContainer: options.container,\n });\n\n const controller = await scopedContainer.instance(options.controllerClass);\n // Controller interaction surfaces (input/events handlers) are plain data\n // properties holding arrow functions — they bypass the IoC context proxy,\n // so invoking them from a test would run context-less and singleton\n // collaborators (e.g. TraceProvider) would resolve root-scope state\n // instead of this harness scope's registrations. Bridge them so every\n // handler runs under the harness scope unless a context is already active.\n const bridgeHandlers = (surface: unknown): void => {\n if (!surface || typeof surface !== 'object') return;\n const record = surface as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n const handler = record[key];\n if (typeof handler !== 'function') continue;\n record[key] = (...args: unknown[]) =>\n ExecutionContext.current() !== undefined\n ? (handler as (...a: unknown[]) => unknown)(...args)\n : ExecutionContext.run(scopedContainer as any, () =>\n (handler as (...a: unknown[]) => unknown)(...args),\n );\n }\n };\n bridgeHandlers((controller as { input?: unknown }).input);\n bridgeHandlers((controller as { events?: unknown }).events);\n const trace = await scopedContainer.instance(TraceProvider);\n const traceRecorder = await scopedContainer.instance(TraceRecorder);\n if (options.initialize && typeof (controller as { initialize?: () => void }).initialize === 'function') {\n await Promise.resolve(\n (controller as { initialize: () => void | Promise<void> }).initialize(),\n );\n }\n\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.created',\n payload: {\n controllerClass: options.controllerClass.name,\n },\n });\n\n return {\n controller,\n bridge: harnessBridge,\n events,\n router,\n container: scopedContainer,\n backendContainer: options.container,\n navigation,\n traceRecorder,\n destroy: async () => {\n trace.info({\n source: 'wood.testing.controller-harness',\n type: 'harness.destroyed',\n });\n await disposeControllerLifecycle(controller);\n events.clear();\n clearClientContainerForTests();\n clearClientAppForTests();\n clearClientNavigationForTests();\n clearClientWindowControlsForTests();\n clearClientLoggersForTests();\n\n if (previousInternalBridge === undefined) {\n delete globalObj[WOOD_INTERNAL_BRIDGE_KEY];\n } else {\n globalObj[WOOD_INTERNAL_BRIDGE_KEY] = previousInternalBridge;\n }\n\n if (previousWoodBridge === undefined) {\n delete globalObj.wood;\n } else {\n globalObj.wood = previousWoodBridge;\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  import { WoodRouter } from '../router/wood_router.cjs';
2
2
  import '../types/operations.cjs';
3
+ import '../types/context.cjs';
3
4
 
4
5
  /**
5
6
  * An in-process event emitter that replaces Electron's
@@ -1,5 +1,6 @@
1
1
  import { WoodRouter } from '../router/wood_router.js';
2
2
  import '../types/operations.js';
3
+ import '../types/context.js';
3
4
 
4
5
  /**
5
6
  * An in-process event emitter that replaces Electron's
@@ -1,5 +1,6 @@
1
1
  import { WoodRouter } from '../router/wood_router.cjs';
2
2
  import '../types/operations.cjs';
3
+ import '../types/context.cjs';
3
4
 
4
5
  interface TestRouterResult {
5
6
  router: WoodRouter;
@@ -1,5 +1,6 @@
1
1
  import { WoodRouter } from '../router/wood_router.js';
2
2
  import '../types/operations.js';
3
+ import '../types/context.js';
3
4
 
4
5
  interface TestRouterResult {
5
6
  router: WoodRouter;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var geometry_exports = {};
20
+ __export(geometry_exports, {
21
+ axisEdges: () => axisEdges,
22
+ axisGap: () => axisGap,
23
+ evenSpacing: () => evenSpacing,
24
+ gaps: () => gaps,
25
+ rectsOverlap: () => rectsOverlap,
26
+ sharedEdges: () => sharedEdges,
27
+ snapshotRectsOverlap: () => snapshotRectsOverlap
28
+ });
29
+ module.exports = __toCommonJS(geometry_exports);
30
+ function axisEdges(rect, axis) {
31
+ return axis === "horizontal" ? { leading: rect.left, trailing: rect.right } : { leading: rect.top, trailing: rect.bottom };
32
+ }
33
+ function axisGap(first, second, axis) {
34
+ const a = axisEdges(first, axis);
35
+ const b = axisEdges(second, axis);
36
+ const [earlier, later] = a.leading <= b.leading ? [a, b] : [b, a];
37
+ return later.leading - earlier.trailing;
38
+ }
39
+ function rectsOverlap(a, b) {
40
+ return Boolean(
41
+ a && b && a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top
42
+ );
43
+ }
44
+ function snapshotRectsOverlap(a, b) {
45
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
46
+ }
47
+ function sharedEdges(rects, edge, options = {}) {
48
+ const tolerance = options.tolerance ?? 1;
49
+ if (rects.length < 2) {
50
+ return {
51
+ pass: false,
52
+ message: "sharedEdges requires at least two rects.",
53
+ details: { count: rects.length, edge, tolerance }
54
+ };
55
+ }
56
+ const expected = rects[0][edge];
57
+ const deltas = rects.slice(1).map((rect) => Math.abs(rect[edge] - expected));
58
+ const maximumDelta = Math.max(...deltas);
59
+ const pass = maximumDelta <= tolerance;
60
+ return {
61
+ pass,
62
+ message: pass ? `All rects share the ${edge} edge within ${tolerance}px.` : `Rects do not share the ${edge} edge within ${tolerance}px (maximum delta ${maximumDelta.toFixed(2)}px).`,
63
+ details: { edge, tolerance, expected, deltas, maximumDelta }
64
+ };
65
+ }
66
+ function gaps(rects, axis) {
67
+ const sorted = [...rects].sort(
68
+ (a, b) => axisEdges(a, axis).leading - axisEdges(b, axis).leading
69
+ );
70
+ return sorted.slice(1).map((rect, index) => axisGap(sorted[index], rect, axis));
71
+ }
72
+ function evenSpacing(rects, axis, options = {}) {
73
+ const tolerance = options.tolerance ?? 1;
74
+ if (rects.length < 3) {
75
+ return {
76
+ pass: false,
77
+ message: `evenSpacing requires at least three rects, got ${rects.length}.`,
78
+ details: { count: rects.length, axis, tolerance }
79
+ };
80
+ }
81
+ const measuredGaps = gaps(rects, axis);
82
+ const widest = Math.max(...measuredGaps);
83
+ const narrowest = Math.min(...measuredGaps);
84
+ const spread = widest - narrowest;
85
+ const pass = spread <= tolerance;
86
+ return {
87
+ pass,
88
+ message: pass ? `Rects are evenly spaced along ${axis} within ${tolerance}px.` : `Rects are not evenly spaced along ${axis}: gap spread ${spread.toFixed(2)}px exceeds ${tolerance}px.`,
89
+ details: {
90
+ axis,
91
+ tolerance,
92
+ gaps: measuredGaps,
93
+ widest,
94
+ narrowest,
95
+ spread
96
+ }
97
+ };
98
+ }
99
+ // Annotate the CommonJS export names for ESM import in node:
100
+ 0 && (module.exports = {
101
+ axisEdges,
102
+ axisGap,
103
+ evenSpacing,
104
+ gaps,
105
+ rectsOverlap,
106
+ sharedEdges,
107
+ snapshotRectsOverlap
108
+ });
109
+ //# sourceMappingURL=geometry.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/geometry.ts"],"sourcesContent":["export type RectAxis = 'horizontal' | 'vertical';\n\nexport interface RectBox {\n top: number;\n left: number;\n right: number;\n bottom: number;\n width: number;\n height: number;\n}\n\nexport interface SnapshotRect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface GeometryVerdict {\n pass: boolean;\n message: string;\n details?: Record<string, unknown>;\n}\n\nexport type RectEdge = 'left' | 'right' | 'top' | 'bottom';\n\nexport function axisEdges(\n rect: RectBox,\n axis: RectAxis,\n): { leading: number; trailing: number } {\n return axis === 'horizontal'\n ? { leading: rect.left, trailing: rect.right }\n : { leading: rect.top, trailing: rect.bottom };\n}\n\n/**\n * Edge-to-edge gap between two border boxes. The box with the smaller leading\n * edge is treated as the earlier box, so negative values mean overlap.\n */\nexport function axisGap(first: RectBox, second: RectBox, axis: RectAxis): number {\n const a = axisEdges(first, axis);\n const b = axisEdges(second, axis);\n const [earlier, later] = a.leading <= b.leading ? [a, b] : [b, a];\n return later.leading - earlier.trailing;\n}\n\nexport function rectsOverlap(\n a: RectBox | null | undefined,\n b: RectBox | null | undefined,\n): boolean {\n return Boolean(\n a && b\n && a.left < b.right && a.right > b.left\n && a.top < b.bottom && a.bottom > b.top,\n );\n}\n\n/** Two CDP snapshot `[x,y,w,h]` rects overlap on both axes. */\nexport function snapshotRectsOverlap(a: SnapshotRect, b: SnapshotRect): boolean {\n return a.x < b.x + b.w\n && a.x + a.w > b.x\n && a.y < b.y + b.h\n && a.y + a.h > b.y;\n}\n\n/** Assert that every rect shares one named edge with the first rect. */\nexport function sharedEdges(\n rects: readonly RectBox[],\n edge: RectEdge,\n options: { tolerance?: number } = {},\n): GeometryVerdict {\n const tolerance = options.tolerance ?? 1;\n if (rects.length < 2) {\n return {\n pass: false,\n message: 'sharedEdges requires at least two rects.',\n details: { count: rects.length, edge, tolerance },\n };\n }\n\n const expected = rects[0][edge];\n const deltas = rects.slice(1).map((rect) => Math.abs(rect[edge] - expected));\n const maximumDelta = Math.max(...deltas);\n const pass = maximumDelta <= tolerance;\n return {\n pass,\n message: pass\n ? `All rects share the ${edge} edge within ${tolerance}px.`\n : `Rects do not share the ${edge} edge within ${tolerance}px (maximum delta ${maximumDelta.toFixed(2)}px).`,\n details: { edge, tolerance, expected, deltas, maximumDelta },\n };\n}\n\n/** Consecutive edge-to-edge gaps, after sorting rects along the requested axis. */\nexport function gaps(rects: readonly RectBox[], axis: RectAxis): number[] {\n const sorted = [...rects].sort(\n (a, b) => axisEdges(a, axis).leading - axisEdges(b, axis).leading,\n );\n return sorted.slice(1).map((rect, index) => axisGap(sorted[index], rect, axis));\n}\n\n/** Assert that consecutive rect gaps have no more than the requested spread. */\nexport function evenSpacing(\n rects: readonly RectBox[],\n axis: RectAxis,\n options: { tolerance?: number } = {},\n): GeometryVerdict {\n const tolerance = options.tolerance ?? 1;\n if (rects.length < 3) {\n return {\n pass: false,\n message: `evenSpacing requires at least three rects, got ${rects.length}.`,\n details: { count: rects.length, axis, tolerance },\n };\n }\n\n const measuredGaps = gaps(rects, axis);\n const widest = Math.max(...measuredGaps);\n const narrowest = Math.min(...measuredGaps);\n const spread = widest - narrowest;\n const pass = spread <= tolerance;\n return {\n pass,\n message: pass\n ? `Rects are evenly spaced along ${axis} within ${tolerance}px.`\n : `Rects are not evenly spaced along ${axis}: gap spread ${spread.toFixed(2)}px exceeds ${tolerance}px.`,\n details: {\n axis,\n tolerance,\n gaps: measuredGaps,\n widest,\n narrowest,\n spread,\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,45 @@
1
+ type RectAxis = 'horizontal' | 'vertical';
2
+ interface RectBox {
3
+ top: number;
4
+ left: number;
5
+ right: number;
6
+ bottom: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+ interface SnapshotRect {
11
+ x: number;
12
+ y: number;
13
+ w: number;
14
+ h: number;
15
+ }
16
+ interface GeometryVerdict {
17
+ pass: boolean;
18
+ message: string;
19
+ details?: Record<string, unknown>;
20
+ }
21
+ type RectEdge = 'left' | 'right' | 'top' | 'bottom';
22
+ declare function axisEdges(rect: RectBox, axis: RectAxis): {
23
+ leading: number;
24
+ trailing: number;
25
+ };
26
+ /**
27
+ * Edge-to-edge gap between two border boxes. The box with the smaller leading
28
+ * edge is treated as the earlier box, so negative values mean overlap.
29
+ */
30
+ declare function axisGap(first: RectBox, second: RectBox, axis: RectAxis): number;
31
+ declare function rectsOverlap(a: RectBox | null | undefined, b: RectBox | null | undefined): boolean;
32
+ /** Two CDP snapshot `[x,y,w,h]` rects overlap on both axes. */
33
+ declare function snapshotRectsOverlap(a: SnapshotRect, b: SnapshotRect): boolean;
34
+ /** Assert that every rect shares one named edge with the first rect. */
35
+ declare function sharedEdges(rects: readonly RectBox[], edge: RectEdge, options?: {
36
+ tolerance?: number;
37
+ }): GeometryVerdict;
38
+ /** Consecutive edge-to-edge gaps, after sorting rects along the requested axis. */
39
+ declare function gaps(rects: readonly RectBox[], axis: RectAxis): number[];
40
+ /** Assert that consecutive rect gaps have no more than the requested spread. */
41
+ declare function evenSpacing(rects: readonly RectBox[], axis: RectAxis, options?: {
42
+ tolerance?: number;
43
+ }): GeometryVerdict;
44
+
45
+ export { type GeometryVerdict, type RectAxis, type RectBox, type RectEdge, type SnapshotRect, axisEdges, axisGap, evenSpacing, gaps, rectsOverlap, sharedEdges, snapshotRectsOverlap };
@@ -0,0 +1,45 @@
1
+ type RectAxis = 'horizontal' | 'vertical';
2
+ interface RectBox {
3
+ top: number;
4
+ left: number;
5
+ right: number;
6
+ bottom: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+ interface SnapshotRect {
11
+ x: number;
12
+ y: number;
13
+ w: number;
14
+ h: number;
15
+ }
16
+ interface GeometryVerdict {
17
+ pass: boolean;
18
+ message: string;
19
+ details?: Record<string, unknown>;
20
+ }
21
+ type RectEdge = 'left' | 'right' | 'top' | 'bottom';
22
+ declare function axisEdges(rect: RectBox, axis: RectAxis): {
23
+ leading: number;
24
+ trailing: number;
25
+ };
26
+ /**
27
+ * Edge-to-edge gap between two border boxes. The box with the smaller leading
28
+ * edge is treated as the earlier box, so negative values mean overlap.
29
+ */
30
+ declare function axisGap(first: RectBox, second: RectBox, axis: RectAxis): number;
31
+ declare function rectsOverlap(a: RectBox | null | undefined, b: RectBox | null | undefined): boolean;
32
+ /** Two CDP snapshot `[x,y,w,h]` rects overlap on both axes. */
33
+ declare function snapshotRectsOverlap(a: SnapshotRect, b: SnapshotRect): boolean;
34
+ /** Assert that every rect shares one named edge with the first rect. */
35
+ declare function sharedEdges(rects: readonly RectBox[], edge: RectEdge, options?: {
36
+ tolerance?: number;
37
+ }): GeometryVerdict;
38
+ /** Consecutive edge-to-edge gaps, after sorting rects along the requested axis. */
39
+ declare function gaps(rects: readonly RectBox[], axis: RectAxis): number[];
40
+ /** Assert that consecutive rect gaps have no more than the requested spread. */
41
+ declare function evenSpacing(rects: readonly RectBox[], axis: RectAxis, options?: {
42
+ tolerance?: number;
43
+ }): GeometryVerdict;
44
+
45
+ export { type GeometryVerdict, type RectAxis, type RectBox, type RectEdge, type SnapshotRect, axisEdges, axisGap, evenSpacing, gaps, rectsOverlap, sharedEdges, snapshotRectsOverlap };