@noego/wood 0.4.10 → 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 (382) hide show
  1. package/README.md +118 -42
  2. package/dist/bridge/backend_bridge.cjs.map +1 -1
  3. package/dist/bridge/backend_bridge.js.map +1 -1
  4. package/dist/client/index.cjs +78 -21
  5. package/dist/client/index.cjs.map +1 -1
  6. package/dist/client/index.d.cts +18 -3
  7. package/dist/client/index.d.ts +18 -3
  8. package/dist/client/index.js +80 -22
  9. package/dist/client/index.js.map +1 -1
  10. package/dist/client/renderer_log_transport.cjs +151 -0
  11. package/dist/client/renderer_log_transport.cjs.map +1 -0
  12. package/dist/client/renderer_log_transport.d.cts +54 -0
  13. package/dist/client/renderer_log_transport.d.ts +54 -0
  14. package/dist/client/renderer_log_transport.js +123 -0
  15. package/dist/client/renderer_log_transport.js.map +1 -0
  16. package/dist/codegen/controller_registration_generator.cjs +13 -2
  17. package/dist/codegen/controller_registration_generator.cjs.map +1 -1
  18. package/dist/codegen/controller_registration_generator.d.cts +12 -1
  19. package/dist/codegen/controller_registration_generator.d.ts +12 -1
  20. package/dist/codegen/controller_registration_generator.js +10 -1
  21. package/dist/codegen/controller_registration_generator.js.map +1 -1
  22. package/dist/codegen/index.d.cts +1 -0
  23. package/dist/codegen/index.d.ts +1 -0
  24. package/dist/codegen/preload_generator.cjs +13 -6
  25. package/dist/codegen/preload_generator.cjs.map +1 -1
  26. package/dist/codegen/preload_generator.d.cts +11 -2
  27. package/dist/codegen/preload_generator.d.ts +11 -2
  28. package/dist/codegen/preload_generator.js +13 -6
  29. package/dist/codegen/preload_generator.js.map +1 -1
  30. package/dist/codegen/renderer_app_generator.cjs +78 -42
  31. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  32. package/dist/codegen/renderer_app_generator.js +78 -42
  33. package/dist/codegen/renderer_app_generator.js.map +1 -1
  34. package/dist/codegen/type_generator.cjs +1 -0
  35. package/dist/codegen/type_generator.cjs.map +1 -1
  36. package/dist/codegen/type_generator.js +1 -0
  37. package/dist/codegen/type_generator.js.map +1 -1
  38. package/dist/config/config_parser.cjs +95 -4
  39. package/dist/config/config_parser.cjs.map +1 -1
  40. package/dist/config/config_parser.d.cts +10 -2
  41. package/dist/config/config_parser.d.ts +10 -2
  42. package/dist/config/config_parser.js +92 -3
  43. package/dist/config/config_parser.js.map +1 -1
  44. package/dist/config/index.cjs +6 -2
  45. package/dist/config/index.cjs.map +1 -1
  46. package/dist/config/index.d.cts +2 -2
  47. package/dist/config/index.d.ts +2 -2
  48. package/dist/config/index.js +8 -2
  49. package/dist/config/index.js.map +1 -1
  50. package/dist/controller/controller_resolver.cjs +51 -3
  51. package/dist/controller/controller_resolver.cjs.map +1 -1
  52. package/dist/controller/controller_resolver.d.cts +27 -4
  53. package/dist/controller/controller_resolver.d.ts +27 -4
  54. package/dist/controller/controller_resolver.js +48 -2
  55. package/dist/controller/controller_resolver.js.map +1 -1
  56. package/dist/controller/index.cjs +6 -2
  57. package/dist/controller/index.cjs.map +1 -1
  58. package/dist/controller/index.d.cts +1 -1
  59. package/dist/controller/index.d.ts +1 -1
  60. package/dist/controller/index.js +4 -2
  61. package/dist/controller/index.js.map +1 -1
  62. package/dist/dev/wood_dev_server.cjs +4 -1
  63. package/dist/dev/wood_dev_server.cjs.map +1 -1
  64. package/dist/dev/wood_dev_server.js +5 -2
  65. package/dist/dev/wood_dev_server.js.map +1 -1
  66. package/dist/dev/yaml_watcher.cjs +1 -1
  67. package/dist/dev/yaml_watcher.cjs.map +1 -1
  68. package/dist/dev/yaml_watcher.d.cts +4 -0
  69. package/dist/dev/yaml_watcher.d.ts +4 -0
  70. package/dist/dev/yaml_watcher.js +1 -1
  71. package/dist/dev/yaml_watcher.js.map +1 -1
  72. package/dist/frontend/frontend_lifecycle.cjs +74 -0
  73. package/dist/frontend/frontend_lifecycle.cjs.map +1 -0
  74. package/dist/frontend/frontend_lifecycle.d.cts +50 -0
  75. package/dist/frontend/frontend_lifecycle.d.ts +50 -0
  76. package/dist/frontend/frontend_lifecycle.js +50 -0
  77. package/dist/frontend/frontend_lifecycle.js.map +1 -0
  78. package/dist/frontend/frontend_page.cjs +122 -0
  79. package/dist/frontend/frontend_page.cjs.map +1 -0
  80. package/dist/frontend/frontend_page.d.cts +62 -0
  81. package/dist/frontend/frontend_page.d.ts +62 -0
  82. package/dist/frontend/frontend_page.js +98 -0
  83. package/dist/frontend/frontend_page.js.map +1 -0
  84. package/dist/frontend/frontend_surface.cjs +249 -0
  85. package/dist/frontend/frontend_surface.cjs.map +1 -0
  86. package/dist/frontend/frontend_surface.d.cts +64 -0
  87. package/dist/frontend/frontend_surface.d.ts +64 -0
  88. package/dist/frontend/frontend_surface.js +224 -0
  89. package/dist/frontend/frontend_surface.js.map +1 -0
  90. package/dist/frontend/index.cjs +52 -0
  91. package/dist/frontend/index.cjs.map +1 -0
  92. package/dist/frontend/index.d.cts +10 -0
  93. package/dist/frontend/index.d.ts +10 -0
  94. package/dist/frontend/index.js +19 -0
  95. package/dist/frontend/index.js.map +1 -0
  96. package/dist/frontend/page_catalog.cjs +135 -0
  97. package/dist/frontend/page_catalog.cjs.map +1 -0
  98. package/dist/frontend/page_catalog.d.cts +56 -0
  99. package/dist/frontend/page_catalog.d.ts +56 -0
  100. package/dist/frontend/page_catalog.js +108 -0
  101. package/dist/frontend/page_catalog.js.map +1 -0
  102. package/dist/frontend/tracked_transport.cjs +55 -0
  103. package/dist/frontend/tracked_transport.cjs.map +1 -0
  104. package/dist/frontend/tracked_transport.d.cts +36 -0
  105. package/dist/frontend/tracked_transport.d.ts +36 -0
  106. package/dist/frontend/tracked_transport.js +31 -0
  107. package/dist/frontend/tracked_transport.js.map +1 -0
  108. package/dist/frontend/work_registry.cjs +153 -0
  109. package/dist/frontend/work_registry.cjs.map +1 -0
  110. package/dist/frontend/work_registry.d.cts +70 -0
  111. package/dist/frontend/work_registry.d.ts +70 -0
  112. package/dist/frontend/work_registry.js +128 -0
  113. package/dist/frontend/work_registry.js.map +1 -0
  114. package/dist/index.cjs +24 -0
  115. package/dist/index.cjs.map +1 -1
  116. package/dist/index.d.cts +10 -7
  117. package/dist/index.d.ts +10 -7
  118. package/dist/index.js +27 -4
  119. package/dist/index.js.map +1 -1
  120. package/dist/main/index.cjs +10 -0
  121. package/dist/main/index.cjs.map +1 -1
  122. package/dist/main/index.d.cts +14 -2
  123. package/dist/main/index.d.ts +14 -2
  124. package/dist/main/index.js +16 -0
  125. package/dist/main/index.js.map +1 -1
  126. package/dist/middleware/middleware_resolver.cjs +22 -1
  127. package/dist/middleware/middleware_resolver.cjs.map +1 -1
  128. package/dist/middleware/middleware_resolver.d.cts +13 -1
  129. package/dist/middleware/middleware_resolver.d.ts +13 -1
  130. package/dist/middleware/middleware_resolver.js +22 -1
  131. package/dist/middleware/middleware_resolver.js.map +1 -1
  132. package/dist/navigation/index.cjs +3 -0
  133. package/dist/navigation/index.cjs.map +1 -1
  134. package/dist/navigation/index.d.cts +25 -1
  135. package/dist/navigation/index.d.ts +25 -1
  136. package/dist/navigation/index.js +2 -0
  137. package/dist/navigation/index.js.map +1 -1
  138. package/dist/{middleware/__fixtures__/middleware/auth.cjs → navigation/layout_preservation.cjs} +18 -20
  139. package/dist/navigation/layout_preservation.cjs.map +1 -0
  140. package/dist/navigation/layout_preservation.d.cts +2 -0
  141. package/dist/navigation/layout_preservation.d.ts +2 -0
  142. package/dist/navigation/layout_preservation.js +18 -0
  143. package/dist/navigation/layout_preservation.js.map +1 -0
  144. package/dist/parser/index.cjs +2 -0
  145. package/dist/parser/index.cjs.map +1 -1
  146. package/dist/parser/index.d.cts +1 -1
  147. package/dist/parser/index.d.ts +1 -1
  148. package/dist/parser/index.js +2 -1
  149. package/dist/parser/index.js.map +1 -1
  150. package/dist/parser/operations_parser.cjs +6 -0
  151. package/dist/parser/operations_parser.cjs.map +1 -1
  152. package/dist/parser/operations_parser.d.cts +9 -2
  153. package/dist/parser/operations_parser.d.ts +9 -2
  154. package/dist/parser/operations_parser.js +5 -0
  155. package/dist/parser/operations_parser.js.map +1 -1
  156. package/dist/parser/views_parser.cjs.map +1 -1
  157. package/dist/parser/views_parser.js.map +1 -1
  158. package/dist/router/index.d.cts +1 -0
  159. package/dist/router/index.d.ts +1 -0
  160. package/dist/router/wood_router.cjs +35 -3
  161. package/dist/router/wood_router.cjs.map +1 -1
  162. package/dist/router/wood_router.d.cts +15 -0
  163. package/dist/router/wood_router.d.ts +15 -0
  164. package/dist/router/wood_router.js +36 -4
  165. package/dist/router/wood_router.js.map +1 -1
  166. package/dist/runtime/get_container.cjs +9 -34
  167. package/dist/runtime/get_container.cjs.map +1 -1
  168. package/dist/runtime/get_container.d.cts +24 -4
  169. package/dist/runtime/get_container.d.ts +24 -4
  170. package/dist/runtime/get_container.js +8 -23
  171. package/dist/runtime/get_container.js.map +1 -1
  172. package/dist/runtime/index.cjs +9 -0
  173. package/dist/runtime/index.cjs.map +1 -1
  174. package/dist/runtime/index.d.cts +5 -2
  175. package/dist/runtime/index.d.ts +5 -2
  176. package/dist/runtime/index.js +10 -1
  177. package/dist/runtime/index.js.map +1 -1
  178. package/dist/runtime/renderer_config_extension.cjs +50 -0
  179. package/dist/runtime/renderer_config_extension.cjs.map +1 -0
  180. package/dist/runtime/renderer_config_extension.d.cts +19 -0
  181. package/dist/runtime/renderer_config_extension.d.ts +19 -0
  182. package/dist/runtime/renderer_config_extension.js +24 -0
  183. package/dist/runtime/renderer_config_extension.js.map +1 -0
  184. package/dist/runtime/renderer_log_batch_receiver.cjs +73 -0
  185. package/dist/runtime/renderer_log_batch_receiver.cjs.map +1 -0
  186. package/dist/runtime/renderer_log_batch_receiver.d.cts +29 -0
  187. package/dist/runtime/renderer_log_batch_receiver.d.ts +29 -0
  188. package/dist/runtime/renderer_log_batch_receiver.js +48 -0
  189. package/dist/runtime/renderer_log_batch_receiver.js.map +1 -0
  190. package/dist/runtime/renderer_trace_batch_receiver_registry.d.cts +1 -1
  191. package/dist/runtime/renderer_trace_batch_receiver_registry.d.ts +1 -1
  192. package/dist/runtime/runtime.cjs +14 -0
  193. package/dist/runtime/runtime.cjs.map +1 -1
  194. package/dist/runtime/runtime.d.cts +2 -1
  195. package/dist/runtime/runtime.d.ts +2 -1
  196. package/dist/runtime/runtime.js +14 -0
  197. package/dist/runtime/runtime.js.map +1 -1
  198. package/dist/stick/index.cjs.map +1 -1
  199. package/dist/stick/index.js.map +1 -1
  200. package/dist/testing/browser.cjs +104 -23
  201. package/dist/testing/browser.cjs.map +1 -1
  202. package/dist/testing/browser.d.cts +8 -3
  203. package/dist/testing/browser.d.ts +8 -3
  204. package/dist/testing/browser.js +102 -22
  205. package/dist/testing/browser.js.map +1 -1
  206. package/dist/testing/create_controller_harness.cjs +28 -8
  207. package/dist/testing/create_controller_harness.cjs.map +1 -1
  208. package/dist/testing/create_controller_harness.d.cts +17 -2
  209. package/dist/testing/create_controller_harness.d.ts +17 -2
  210. package/dist/testing/create_controller_harness.js +28 -8
  211. package/dist/testing/create_controller_harness.js.map +1 -1
  212. package/dist/testing/create_router_bridge.d.cts +1 -0
  213. package/dist/testing/create_router_bridge.d.ts +1 -0
  214. package/dist/testing/create_test_router.d.cts +1 -0
  215. package/dist/testing/create_test_router.d.ts +1 -0
  216. package/dist/testing/index.cjs +31 -0
  217. package/dist/testing/index.cjs.map +1 -1
  218. package/dist/testing/index.d.cts +20 -5
  219. package/dist/testing/index.d.ts +20 -5
  220. package/dist/testing/index.js +19 -1
  221. package/dist/testing/index.js.map +1 -1
  222. package/dist/{controller/__fixtures__/controllers/not_a_class.cjs → testing/test_wood.cjs} +15 -7
  223. package/dist/testing/test_wood.cjs.map +1 -0
  224. package/dist/testing/test_wood.d.cts +16 -0
  225. package/dist/testing/test_wood.d.ts +16 -0
  226. package/dist/testing/test_wood.js +12 -0
  227. package/dist/testing/test_wood.js.map +1 -0
  228. package/dist/testing/test_wood_config.cjs +272 -0
  229. package/dist/testing/test_wood_config.cjs.map +1 -0
  230. package/dist/testing/test_wood_config.d.cts +111 -0
  231. package/dist/testing/test_wood_config.d.ts +111 -0
  232. package/dist/testing/test_wood_config.js +247 -0
  233. package/dist/testing/test_wood_config.js.map +1 -0
  234. package/dist/testing/wood_config_source.cjs +146 -0
  235. package/dist/testing/wood_config_source.cjs.map +1 -0
  236. package/dist/testing/wood_config_source.d.cts +36 -0
  237. package/dist/testing/wood_config_source.d.ts +36 -0
  238. package/dist/testing/wood_config_source.js +112 -0
  239. package/dist/testing/wood_config_source.js.map +1 -0
  240. package/dist/testing/wood_env_renderer.cjs +210 -0
  241. package/dist/testing/wood_env_renderer.cjs.map +1 -0
  242. package/dist/testing/wood_env_renderer.d.cts +74 -0
  243. package/dist/testing/wood_env_renderer.d.ts +74 -0
  244. package/dist/testing/wood_env_renderer.js +188 -0
  245. package/dist/testing/wood_env_renderer.js.map +1 -0
  246. package/dist/testing/wood_test_selection.cjs +94 -0
  247. package/dist/testing/wood_test_selection.cjs.map +1 -0
  248. package/dist/testing/wood_test_selection.d.cts +39 -0
  249. package/dist/testing/wood_test_selection.d.ts +39 -0
  250. package/dist/testing/wood_test_selection.js +68 -0
  251. package/dist/testing/wood_test_selection.js.map +1 -0
  252. package/dist/trace-testing/index.d.cts +1 -2
  253. package/dist/trace-testing/index.d.ts +1 -2
  254. package/dist/trace-testing/trace_recorder.cjs +1 -1
  255. package/dist/trace-testing/trace_recorder.cjs.map +1 -1
  256. package/dist/trace-testing/trace_recorder.d.cts +1 -2
  257. package/dist/trace-testing/trace_recorder.d.ts +1 -2
  258. package/dist/trace-testing/trace_recorder.js +1 -1
  259. package/dist/trace-testing/trace_recorder.js.map +1 -1
  260. package/dist/trace-transport/index.d.cts +1 -1
  261. package/dist/trace-transport/index.d.ts +1 -1
  262. package/dist/{trace_hub-Cob54qRx.d.cts → trace_hub-Cpo_nVze.d.cts} +21 -1
  263. package/dist/{trace_hub-Cob54qRx.d.ts → trace_hub-Cpo_nVze.d.ts} +21 -1
  264. package/dist/tracing/index.cjs +9 -2
  265. package/dist/tracing/index.cjs.map +1 -1
  266. package/dist/tracing/index.d.cts +3 -2
  267. package/dist/tracing/index.d.ts +3 -2
  268. package/dist/tracing/index.js +7 -1
  269. package/dist/tracing/index.js.map +1 -1
  270. package/dist/tracing/renderer_trace_batch_host.d.cts +1 -1
  271. package/dist/tracing/renderer_trace_batch_host.d.ts +1 -1
  272. package/dist/tracing/renderer_trace_batch_transport.cjs +48 -6
  273. package/dist/tracing/renderer_trace_batch_transport.cjs.map +1 -1
  274. package/dist/tracing/renderer_trace_batch_transport.d.cts +17 -1
  275. package/dist/tracing/renderer_trace_batch_transport.d.ts +17 -1
  276. package/dist/tracing/renderer_trace_batch_transport.js +48 -6
  277. package/dist/tracing/renderer_trace_batch_transport.js.map +1 -1
  278. package/dist/tracing/trace_batch_adapter.cjs +110 -0
  279. package/dist/tracing/trace_batch_adapter.cjs.map +1 -0
  280. package/dist/tracing/trace_batch_adapter.d.cts +36 -0
  281. package/dist/tracing/trace_batch_adapter.d.ts +36 -0
  282. package/dist/tracing/trace_batch_adapter.js +86 -0
  283. package/dist/tracing/trace_batch_adapter.js.map +1 -0
  284. package/dist/tracing/trace_commit_writer.d.cts +1 -1
  285. package/dist/tracing/trace_commit_writer.d.ts +1 -1
  286. package/dist/tracing/trace_hub.cjs +87 -20
  287. package/dist/tracing/trace_hub.cjs.map +1 -1
  288. package/dist/tracing/trace_hub.d.cts +1 -1
  289. package/dist/tracing/trace_hub.d.ts +1 -1
  290. package/dist/tracing/trace_hub.js +85 -20
  291. package/dist/tracing/trace_hub.js.map +1 -1
  292. package/dist/tracing/trace_provider.cjs +21 -6
  293. package/dist/tracing/trace_provider.cjs.map +1 -1
  294. package/dist/tracing/trace_provider.d.cts +13 -4
  295. package/dist/tracing/trace_provider.d.ts +13 -4
  296. package/dist/tracing/trace_provider.js +22 -7
  297. package/dist/tracing/trace_provider.js.map +1 -1
  298. package/dist/tracing/types/wood_trace_batch.types.cjs.map +1 -1
  299. package/dist/tracing/types/wood_trace_batch.types.d.cts +15 -1
  300. package/dist/tracing/types/wood_trace_batch.types.d.ts +15 -1
  301. package/dist/tracing/types/wood_trace_sink.types.d.cts +1 -1
  302. package/dist/tracing/types/wood_trace_sink.types.d.ts +1 -1
  303. package/dist/tracing/wood_trace_batch_contract.cjs +2 -0
  304. package/dist/tracing/wood_trace_batch_contract.cjs.map +1 -1
  305. package/dist/tracing/wood_trace_batch_contract.d.cts +1 -1
  306. package/dist/tracing/wood_trace_batch_contract.d.ts +1 -1
  307. package/dist/tracing/wood_trace_batch_contract.js +2 -0
  308. package/dist/tracing/wood_trace_batch_contract.js.map +1 -1
  309. package/dist/transport/build_transport_bridge.cjs +4 -2
  310. package/dist/transport/build_transport_bridge.cjs.map +1 -1
  311. package/dist/transport/build_transport_bridge.d.cts +7 -1
  312. package/dist/transport/build_transport_bridge.d.ts +7 -1
  313. package/dist/transport/build_transport_bridge.js +2 -1
  314. package/dist/transport/build_transport_bridge.js.map +1 -1
  315. package/dist/transport/in_process_router_transport.d.cts +1 -0
  316. package/dist/transport/in_process_router_transport.d.ts +1 -0
  317. package/dist/transport/index.d.cts +1 -0
  318. package/dist/transport/index.d.ts +1 -0
  319. package/dist/types/config.cjs.map +1 -1
  320. package/dist/types/config.d.cts +47 -1
  321. package/dist/types/config.d.ts +47 -1
  322. package/dist/types/context.cjs +12 -0
  323. package/dist/types/context.cjs.map +1 -1
  324. package/dist/types/context.d.cts +27 -1
  325. package/dist/types/context.d.ts +27 -1
  326. package/dist/types/context.js +4 -0
  327. package/dist/types/context.js.map +1 -1
  328. package/dist/types/index.d.cts +2 -2
  329. package/dist/types/index.d.ts +2 -2
  330. package/dist/types/operations.cjs.map +1 -1
  331. package/dist/types/operations.d.cts +2 -0
  332. package/dist/types/operations.d.ts +2 -0
  333. package/docs/axe.md +706 -0
  334. package/package.json +13 -5
  335. package/src/components/NavigationShell.scopes.browser.test.ts +363 -0
  336. package/src/components/NavigationShell.svelte +126 -49
  337. package/dist/controller/__fixtures__/controllers/chat.cjs +0 -32
  338. package/dist/controller/__fixtures__/controllers/chat.cjs.map +0 -1
  339. package/dist/controller/__fixtures__/controllers/chat.d.cts +0 -11
  340. package/dist/controller/__fixtures__/controllers/chat.d.ts +0 -11
  341. package/dist/controller/__fixtures__/controllers/chat.js +0 -12
  342. package/dist/controller/__fixtures__/controllers/chat.js.map +0 -1
  343. package/dist/controller/__fixtures__/controllers/not_a_class.cjs.map +0 -1
  344. package/dist/controller/__fixtures__/controllers/not_a_class.d.cts +0 -3
  345. package/dist/controller/__fixtures__/controllers/not_a_class.d.ts +0 -3
  346. package/dist/controller/__fixtures__/controllers/not_a_class.js +0 -5
  347. package/dist/controller/__fixtures__/controllers/not_a_class.js.map +0 -1
  348. package/dist/controller/__fixtures__/controllers/settings.controller.cjs +0 -32
  349. package/dist/controller/__fixtures__/controllers/settings.controller.cjs.map +0 -1
  350. package/dist/controller/__fixtures__/controllers/settings.controller.d.cts +0 -12
  351. package/dist/controller/__fixtures__/controllers/settings.controller.d.ts +0 -12
  352. package/dist/controller/__fixtures__/controllers/settings.controller.js +0 -12
  353. package/dist/controller/__fixtures__/controllers/settings.controller.js.map +0 -1
  354. package/dist/controller/__fixtures__/controllers/test.cjs +0 -29
  355. package/dist/controller/__fixtures__/controllers/test.cjs.map +0 -1
  356. package/dist/controller/__fixtures__/controllers/test.d.cts +0 -7
  357. package/dist/controller/__fixtures__/controllers/test.d.ts +0 -7
  358. package/dist/controller/__fixtures__/controllers/test.js +0 -9
  359. package/dist/controller/__fixtures__/controllers/test.js.map +0 -1
  360. package/dist/loader/__fixtures__/layouts/app.load.cjs +0 -27
  361. package/dist/loader/__fixtures__/layouts/app.load.cjs.map +0 -1
  362. package/dist/loader/__fixtures__/layouts/app.load.d.cts +0 -5
  363. package/dist/loader/__fixtures__/layouts/app.load.d.ts +0 -5
  364. package/dist/loader/__fixtures__/layouts/app.load.js +0 -7
  365. package/dist/loader/__fixtures__/layouts/app.load.js.map +0 -1
  366. package/dist/loader/__fixtures__/views/test/page.load.cjs +0 -27
  367. package/dist/loader/__fixtures__/views/test/page.load.cjs.map +0 -1
  368. package/dist/loader/__fixtures__/views/test/page.load.d.cts +0 -6
  369. package/dist/loader/__fixtures__/views/test/page.load.d.ts +0 -6
  370. package/dist/loader/__fixtures__/views/test/page.load.js +0 -7
  371. package/dist/loader/__fixtures__/views/test/page.load.js.map +0 -1
  372. package/dist/middleware/__fixtures__/middleware/auth.cjs.map +0 -1
  373. package/dist/middleware/__fixtures__/middleware/auth.d.cts +0 -6
  374. package/dist/middleware/__fixtures__/middleware/auth.d.ts +0 -6
  375. package/dist/middleware/__fixtures__/middleware/auth.js +0 -19
  376. package/dist/middleware/__fixtures__/middleware/auth.js.map +0 -1
  377. package/dist/middleware/__fixtures__/middleware/sequence.cjs +0 -47
  378. package/dist/middleware/__fixtures__/middleware/sequence.cjs.map +0 -1
  379. package/dist/middleware/__fixtures__/middleware/sequence.d.cts +0 -7
  380. package/dist/middleware/__fixtures__/middleware/sequence.d.ts +0 -7
  381. package/dist/middleware/__fixtures__/middleware/sequence.js +0 -21
  382. package/dist/middleware/__fixtures__/middleware/sequence.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/runtime.ts"],"sourcesContent":["import { getLogger } from '@noego/logger';\nimport { WoodRouter } from '../router/wood_router.cjs';\nimport { getContainer } from './get_container.cjs';\nimport {\n RendererTraceBatchHost,\n TraceProviderConfiguration,\n dispatchRendererTracePayload,\n} from '../tracing/index.js';\nimport {\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n type TraceLevel,\n type TraceRuntimeContext,\n type WoodTraceBatchReceiver,\n} from '../tracing/index.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogDispatchResult,\n type RendererLogEntry,\n type RendererLogLevel,\n} from './renderer_log_dispatcher.js';\nimport { getRendererTraceBatchReceiver } from './renderer_trace_batch_receiver_registry.js';\nimport { resolveWoodAppEnv, type WoodAppEnv } from './app_env.js';\n\nconst logger = getLogger('wood:runtime');\n\ntype IpcMainLike = {\n handle: (channel: string, handler: (_event: unknown, data: unknown) => Promise<unknown> | unknown) => void;\n on: (channel: string, handler: (_event: unknown, ...args: unknown[]) => void) => void;\n removeHandler?: (channel: string) => void;\n removeAllListeners?: (channel: string) => void;\n};\n\nexport interface RegisterRuntimeRouter {\n registerController(name: string, cls: any): void;\n loadOperations(operations: Map<string, any>): void;\n}\n\nexport interface RuntimeOptions {\n ipcMain: IpcMainLike;\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n container?: any;\n controllersDir: string;\n middlewareDir: string;\n mode?: 'bundled' | 'dev';\n /** Override the resolved app environment tier reported to renderers via __woodConfig. */\n appEnv?: WoodAppEnv;\n enableRendererLogBridge?: boolean;\n enableRendererTraceBridge?: boolean;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n /** Resolves only with the acknowledgement produced by the downstream durable receiver. */\n rendererTraceBatchReceiver?: WoodTraceBatchReceiver;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onNavigate?: (data: unknown, event?: unknown) => Promise<unknown>;\n onWindow?: (data: unknown, event?: unknown) => Promise<unknown>;\n onDebug?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n resolveRendererTraceContext?: (event: unknown) => TraceRuntimeContext | undefined;\n}\n\nexport interface RuntimeResult {\n router: WoodRouter;\n}\n\ntype RendererLogPayload = {\n level: string;\n logger: string;\n message: string;\n context?: unknown;\n};\n\ntype IpcInvokeEventLike = {\n sender?: {\n id?: number;\n };\n};\n\ntype RuntimeReservedHandler = (\n data: unknown,\n event?: unknown,\n) => Promise<unknown>;\n\ntype RuntimeTraceSettings = Readonly<{\n enableRendererLogBridge: boolean;\n enableRendererTraceBridge: boolean;\n traceMinLevel: TraceLevel | undefined;\n rendererTraceEnabled: boolean;\n rendererTraceMinLevel: TraceLevel;\n rendererTraceBatchEnabled: boolean;\n}>;\n\nfunction normalizeRendererLogLevel(level: unknown): RendererLogLevel {\n if (typeof level === 'string') {\n const normalized = level.trim().toLowerCase();\n if (normalized.length > 0) {\n return normalized;\n }\n }\n return 'info';\n}\n\nfunction coerceRendererLogPayload(\n rawPayload: unknown,\n event: unknown,\n): RendererLogEntry {\n const payload = (rawPayload ?? {}) as Partial<RendererLogPayload>;\n const ipcEvent = event as IpcInvokeEventLike;\n\n return {\n level: normalizeRendererLogLevel(payload.level),\n logger: typeof payload.logger === 'string' && payload.logger.trim().length > 0\n ? payload.logger\n : 'renderer',\n message: typeof payload.message === 'string' ? payload.message : '',\n context: payload.context,\n timestamp: new Date().toISOString(),\n senderId: typeof ipcEvent?.sender?.id === 'number' ? ipcEvent.sender.id : null,\n };\n}\n\n/**\n * Boot a bundled-safe Wood runtime using pre-generated static runtime artifacts.\n */\nexport async function runtime(options: RuntimeOptions): Promise<RuntimeResult> {\n const {\n ipcMain,\n registerRuntime,\n controllersDir,\n middlewareDir,\n mode = 'bundled',\n enableRendererLogBridge = true,\n enableRendererTraceBridge = true,\n traceMinLevel,\n rendererTraceEnabled = false,\n rendererTraceMinLevel = 'debug',\n } = options;\n\n const container = options.container ?? getContainer();\n const rendererTraceBatchReceiver = options.rendererTraceBatchReceiver\n ?? getRendererTraceBatchReceiver();\n const rendererTraceBatchEnabled = rendererTraceBatchReceiver !== null;\n const rendererTraceBatchHost = new RendererTraceBatchHost(\n rendererTraceBatchReceiver,\n );\n\n logger.info('runtime start', {\n mode,\n hasProvidedContainer: !!options.container,\n controllersDir,\n middlewareDir,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel: traceMinLevel ?? 'debug',\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n });\n\n const traceSettings: RuntimeTraceSettings = {\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel,\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n };\n await configureRuntimeTracing(container, traceSettings);\n\n const router = new WoodRouter({\n container,\n controllersDir,\n middlewareDir,\n mode,\n });\n\n registerRuntime(router);\n logger.info('runtime registration complete');\n\n router.validateControllers();\n logger.info('runtime controller validation complete');\n\n router.precompileSchemas();\n logger.info('runtime schema pre-compilation complete');\n\n registerReservedRuntimeHandlers(router, options, traceSettings, rendererTraceBatchHost);\n registerRuntimeObservationBridges(ipcMain, traceSettings, options.resolveRendererTraceContext);\n\n router.registerIpcHandlers(ipcMain);\n logger.info('runtime ipc handlers registered', {\n channelCount: router.getChannels().length,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n });\n\n return { router };\n}\n\nasync function configureRuntimeTracing(\n container: any,\n settings: RuntimeTraceSettings,\n): Promise<void> {\n if (settings.traceMinLevel) {\n setMainTraceMinLevel(settings.traceMinLevel);\n }\n setRendererTraceMinLevel(settings.rendererTraceMinLevel);\n if (typeof container.instance !== 'function') {\n return;\n }\n\n const traceConfiguration = await container.instance(TraceProviderConfiguration);\n traceConfiguration.configureRuntime('main');\n if (settings.traceMinLevel) {\n traceConfiguration.configureMinLevel(settings.traceMinLevel);\n }\n}\n\nfunction registerReservedRuntimeHandlers(\n router: WoodRouter,\n options: RuntimeOptions,\n settings: RuntimeTraceSettings,\n rendererTraceBatchHost: RendererTraceBatchHost,\n): void {\n router.registerReservedHandler('__load', requireRuntimeHandler('__load', 'onLoad', options.onLoad));\n router.registerReservedHandler('__navigate', requireRuntimeHandler('__navigate', 'onNavigate', options.onNavigate));\n router.registerReservedHandler('__window', requireRuntimeHandler('__window', 'onWindow', options.onWindow));\n router.registerReservedHandler('__debug', requireRuntimeHandler('__debug', 'onDebug', options.onDebug));\n router.registerReservedHandler(\n '__contextMenu',\n requireRuntimeHandler('__contextMenu', 'onContextMenu', options.onContextMenu),\n );\n router.registerReservedHandler('__woodConfig', async () => ({\n rendererTraceEnabled: settings.rendererTraceEnabled,\n rendererTraceMinLevel: settings.rendererTraceMinLevel,\n rendererTraceBatchEnabled: settings.rendererTraceBatchEnabled,\n appEnv: options.appEnv ?? await resolveWoodAppEnv(),\n }));\n router.registerReservedHandler('__traceBatch', async (data, event) => {\n return rendererTraceBatchHost.receive(data, options.resolveRendererTraceContext?.(event) ?? {});\n });\n}\n\nfunction registerRuntimeObservationBridges(\n ipcMain: IpcMainLike,\n settings: RuntimeTraceSettings,\n resolveContext: RuntimeOptions['resolveRendererTraceContext'],\n): void {\n if (settings.enableRendererLogBridge) {\n registerRendererLogBridge(ipcMain);\n }\n ipcMain.removeAllListeners?.('__trace');\n if (settings.enableRendererTraceBridge && settings.rendererTraceEnabled) {\n registerRendererTraceBridge(ipcMain, resolveContext);\n }\n}\n\nfunction requireRuntimeHandler(\n channel: string,\n optionName: string,\n handler: RuntimeReservedHandler | undefined,\n): RuntimeReservedHandler {\n return async (data, event) => {\n if (!handler) {\n throw new Error(`Reserved channel \"${channel}\" not configured. Provide runtime({ ${optionName} }).`);\n }\n return handler(data, event);\n };\n}\n\nfunction registerRendererTraceBridge(\n ipcMain: IpcMainLike,\n resolveContext?: (event: unknown) => TraceRuntimeContext | undefined,\n): void {\n ipcMain.removeAllListeners?.('__trace');\n\n ipcMain.on('__trace', (event, rawPayload) => {\n dispatchRendererTracePayload(rawPayload, resolveContext?.(event));\n });\n\n logger.info('renderer trace bridge registered (fire-and-forget)');\n}\n\nfunction registerRendererLogBridge(ipcMain: IpcMainLike): void {\n const dispatcher = getRendererLogDispatcher();\n\n ipcMain.removeAllListeners?.('__log');\n\n ipcMain.on('__log', (_event, rawPayload) => {\n const entry = coerceRendererLogPayload(rawPayload, null);\n void dispatcher.dispatch(entry).then((result) => {\n const typedResult = result as RendererLogDispatchResult;\n if (!typedResult.ok) {\n logger.warn('renderer log dispatch failed', {\n logger: entry.logger,\n messageLength: entry.message.length,\n reason: typedResult.reason ?? null,\n errorMessage: typedResult.errorMessage ?? null,\n });\n }\n });\n });\n\n logger.info('renderer log bridge registered (fire-and-forget)');\n}\n"],"mappings":"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","names":["import_tracing"]}
1
+ {"version":3,"sources":["../../src/runtime/runtime.ts"],"sourcesContent":["import { getLogger } from '@noego/logger';\nimport { WoodRouter } from '../router/wood_router.cjs';\nimport { getContainer } from './get_container.cjs';\nimport {\n RendererTraceBatchHost,\n TraceProviderConfiguration,\n dispatchRendererTracePayload,\n} from '../tracing/index.js';\nimport {\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n type TraceLevel,\n type TraceRuntimeContext,\n type WoodTraceBatchReceiver,\n} from '../tracing/index.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogDispatchResult,\n type RendererLogEntry,\n type RendererLogLevel,\n} from './renderer_log_dispatcher.js';\nimport { dispatchRendererLogBatch } from './renderer_log_batch_receiver.js';\nimport { resolveRendererConfigExtension } from './renderer_config_extension.js';\nimport { getRendererTraceBatchReceiver } from './renderer_trace_batch_receiver_registry.js';\nimport { resolveWoodAppEnv, type WoodAppEnv } from './app_env.js';\n\nconst logger = getLogger('wood:runtime');\n\ntype IpcMainLike = {\n handle: (channel: string, handler: (_event: unknown, data: unknown) => Promise<unknown> | unknown) => void;\n on: (channel: string, handler: (_event: unknown, ...args: unknown[]) => void) => void;\n removeHandler?: (channel: string) => void;\n removeAllListeners?: (channel: string) => void;\n};\n\nexport interface RegisterRuntimeRouter {\n registerController(name: string, cls: any): void;\n loadOperations(operations: Map<string, any>): void;\n}\n\nexport interface RuntimeOptions {\n ipcMain: IpcMainLike;\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n container?: any;\n controllersDir: string;\n middlewareDir: string;\n mode?: 'bundled' | 'dev';\n /** Override the resolved app environment tier reported to renderers via __woodConfig. */\n appEnv?: WoodAppEnv;\n enableRendererLogBridge?: boolean;\n enableRendererTraceBridge?: boolean;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n /** Resolves only with the acknowledgement produced by the downstream durable receiver. */\n rendererTraceBatchReceiver?: WoodTraceBatchReceiver;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onNavigate?: (data: unknown, event?: unknown) => Promise<unknown>;\n onWindow?: (data: unknown, event?: unknown) => Promise<unknown>;\n onDebug?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n resolveRendererTraceContext?: (event: unknown) => TraceRuntimeContext | undefined;\n}\n\nexport interface RuntimeResult {\n router: WoodRouter;\n}\n\ntype RendererLogPayload = {\n level: string;\n logger: string;\n message: string;\n context?: unknown;\n};\n\ntype IpcInvokeEventLike = {\n sender?: {\n id?: number;\n };\n};\n\ntype RuntimeReservedHandler = (\n data: unknown,\n event?: unknown,\n) => Promise<unknown>;\n\ntype RuntimeTraceSettings = Readonly<{\n enableRendererLogBridge: boolean;\n enableRendererTraceBridge: boolean;\n traceMinLevel: TraceLevel | undefined;\n rendererTraceEnabled: boolean;\n rendererTraceMinLevel: TraceLevel;\n rendererTraceBatchEnabled: boolean;\n}>;\n\nfunction normalizeRendererLogLevel(level: unknown): RendererLogLevel {\n if (typeof level === 'string') {\n const normalized = level.trim().toLowerCase();\n if (normalized.length > 0) {\n return normalized;\n }\n }\n return 'info';\n}\n\nfunction coerceRendererLogPayload(\n rawPayload: unknown,\n event: unknown,\n): RendererLogEntry {\n const payload = (rawPayload ?? {}) as Partial<RendererLogPayload>;\n const ipcEvent = event as IpcInvokeEventLike;\n\n return {\n level: normalizeRendererLogLevel(payload.level),\n logger: typeof payload.logger === 'string' && payload.logger.trim().length > 0\n ? payload.logger\n : 'renderer',\n message: typeof payload.message === 'string' ? payload.message : '',\n context: payload.context,\n timestamp: new Date().toISOString(),\n senderId: typeof ipcEvent?.sender?.id === 'number' ? ipcEvent.sender.id : null,\n };\n}\n\n/**\n * Boot a bundled-safe Wood runtime using pre-generated static runtime artifacts.\n */\nexport async function runtime(options: RuntimeOptions): Promise<RuntimeResult> {\n const {\n ipcMain,\n registerRuntime,\n controllersDir,\n middlewareDir,\n mode = 'bundled',\n enableRendererLogBridge = true,\n enableRendererTraceBridge = true,\n traceMinLevel,\n rendererTraceEnabled = false,\n rendererTraceMinLevel = 'debug',\n } = options;\n\n const container = options.container ?? getContainer();\n const rendererTraceBatchReceiver = options.rendererTraceBatchReceiver\n ?? getRendererTraceBatchReceiver();\n const rendererTraceBatchEnabled = rendererTraceBatchReceiver !== null;\n const rendererTraceBatchHost = new RendererTraceBatchHost(\n rendererTraceBatchReceiver,\n );\n\n logger.info('runtime start', {\n mode,\n hasProvidedContainer: !!options.container,\n controllersDir,\n middlewareDir,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel: traceMinLevel ?? 'debug',\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n });\n\n const traceSettings: RuntimeTraceSettings = {\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel,\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n };\n await configureRuntimeTracing(container, traceSettings);\n\n const router = new WoodRouter({\n container,\n controllersDir,\n middlewareDir,\n mode,\n });\n\n registerRuntime(router);\n logger.info('runtime registration complete');\n\n router.validateControllers();\n logger.info('runtime controller validation complete');\n\n router.precompileSchemas();\n logger.info('runtime schema pre-compilation complete');\n\n registerReservedRuntimeHandlers(router, options, traceSettings, rendererTraceBatchHost);\n registerRuntimeObservationBridges(ipcMain, traceSettings, options.resolveRendererTraceContext);\n\n router.registerIpcHandlers(ipcMain);\n logger.info('runtime ipc handlers registered', {\n channelCount: router.getChannels().length,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n });\n\n return { router };\n}\n\nasync function configureRuntimeTracing(\n container: any,\n settings: RuntimeTraceSettings,\n): Promise<void> {\n if (settings.traceMinLevel) {\n setMainTraceMinLevel(settings.traceMinLevel);\n }\n setRendererTraceMinLevel(settings.rendererTraceMinLevel);\n if (typeof container.instance !== 'function') {\n return;\n }\n\n const traceConfiguration = await container.instance(TraceProviderConfiguration);\n traceConfiguration.configureRuntime('main');\n if (settings.traceMinLevel) {\n traceConfiguration.configureMinLevel(settings.traceMinLevel);\n }\n}\n\nfunction registerReservedRuntimeHandlers(\n router: WoodRouter,\n options: RuntimeOptions,\n settings: RuntimeTraceSettings,\n rendererTraceBatchHost: RendererTraceBatchHost,\n): void {\n router.registerReservedHandler('__load', requireRuntimeHandler('__load', 'onLoad', options.onLoad));\n router.registerReservedHandler('__navigate', requireRuntimeHandler('__navigate', 'onNavigate', options.onNavigate));\n router.registerReservedHandler('__window', requireRuntimeHandler('__window', 'onWindow', options.onWindow));\n router.registerReservedHandler('__debug', requireRuntimeHandler('__debug', 'onDebug', options.onDebug));\n router.registerReservedHandler(\n '__contextMenu',\n requireRuntimeHandler('__contextMenu', 'onContextMenu', options.onContextMenu),\n );\n router.registerReservedHandler('__woodConfig', async () => ({\n // App extension fields first so Wood's reserved keys always win.\n ...(await resolveRendererConfigExtension()),\n rendererTraceEnabled: settings.rendererTraceEnabled,\n rendererTraceMinLevel: settings.rendererTraceMinLevel,\n rendererTraceBatchEnabled: settings.rendererTraceBatchEnabled,\n appEnv: options.appEnv ?? await resolveWoodAppEnv(),\n }));\n router.registerReservedHandler('__traceBatch', async (data, event) => {\n return rendererTraceBatchHost.receive(data, options.resolveRendererTraceContext?.(event) ?? {});\n });\n}\n\nfunction registerRuntimeObservationBridges(\n ipcMain: IpcMainLike,\n settings: RuntimeTraceSettings,\n resolveContext: RuntimeOptions['resolveRendererTraceContext'],\n): void {\n if (settings.enableRendererLogBridge) {\n registerRendererLogBridge(ipcMain);\n }\n ipcMain.removeAllListeners?.('__trace');\n if (settings.enableRendererTraceBridge && settings.rendererTraceEnabled) {\n registerRendererTraceBridge(ipcMain, resolveContext);\n }\n}\n\nfunction requireRuntimeHandler(\n channel: string,\n optionName: string,\n handler: RuntimeReservedHandler | undefined,\n): RuntimeReservedHandler {\n return async (data, event) => {\n if (!handler) {\n throw new Error(`Reserved channel \"${channel}\" not configured. Provide runtime({ ${optionName} }).`);\n }\n return handler(data, event);\n };\n}\n\nfunction registerRendererTraceBridge(\n ipcMain: IpcMainLike,\n resolveContext?: (event: unknown) => TraceRuntimeContext | undefined,\n): void {\n ipcMain.removeAllListeners?.('__trace');\n\n ipcMain.on('__trace', (event, rawPayload) => {\n dispatchRendererTracePayload(rawPayload, resolveContext?.(event));\n });\n\n logger.info('renderer trace bridge registered (fire-and-forget)');\n}\n\nfunction registerRendererLogBridge(ipcMain: IpcMainLike): void {\n const dispatcher = getRendererLogDispatcher();\n\n ipcMain.removeAllListeners?.('__log');\n\n ipcMain.on('__log', (_event, rawPayload) => {\n const entry = coerceRendererLogPayload(rawPayload, null);\n void dispatcher.dispatch(entry).then((result) => {\n const typedResult = result as RendererLogDispatchResult;\n if (!typedResult.ok) {\n logger.warn('renderer log dispatch failed', {\n logger: entry.logger,\n messageLength: entry.message.length,\n reason: typedResult.reason ?? null,\n errorMessage: typedResult.errorMessage ?? null,\n });\n }\n });\n });\n\n ipcMain.removeAllListeners?.('__log_batch');\n\n ipcMain.on('__log_batch', (_event, rawPayload) => {\n const result = dispatchRendererLogBatch(rawPayload);\n if (!result.ok) {\n logger.warn('renderer log batch rejected', {\n reason: result.reason ?? null,\n errorMessage: result.errorMessage ?? null,\n });\n }\n });\n\n logger.info('renderer log bridge registered (fire-and-forget)');\n}\n"],"mappings":"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","names":["import_tracing"]}
@@ -1,8 +1,9 @@
1
1
  import { WoodRouter } from '../router/wood_router.cjs';
2
- import { c as TraceLevel, e as TraceRuntimeContext } from '../trace_hub-Cob54qRx.cjs';
2
+ import { c as TraceLevel, e as TraceRuntimeContext } from '../trace_hub-Cpo_nVze.cjs';
3
3
  import { WoodTraceBatchReceiver } from '../tracing/types/wood_trace_batch.types.cjs';
4
4
  import { WoodAppEnv } from './app_env.cjs';
5
5
  import '../types/operations.cjs';
6
+ import '../types/context.cjs';
6
7
  import 'rxjs';
7
8
  import '@noego/telemetry/core';
8
9
 
@@ -1,8 +1,9 @@
1
1
  import { WoodRouter } from '../router/wood_router.js';
2
- import { c as TraceLevel, e as TraceRuntimeContext } from '../trace_hub-Cob54qRx.js';
2
+ import { c as TraceLevel, e as TraceRuntimeContext } from '../trace_hub-Cpo_nVze.js';
3
3
  import { WoodTraceBatchReceiver } from '../tracing/types/wood_trace_batch.types.js';
4
4
  import { WoodAppEnv } from './app_env.js';
5
5
  import '../types/operations.js';
6
+ import '../types/context.js';
6
7
  import 'rxjs';
7
8
  import '@noego/telemetry/core';
8
9
 
@@ -13,6 +13,8 @@ import {
13
13
  import {
14
14
  getRendererLogDispatcher
15
15
  } from "./renderer_log_dispatcher.js";
16
+ import { dispatchRendererLogBatch } from "./renderer_log_batch_receiver.js";
17
+ import { resolveRendererConfigExtension } from "./renderer_config_extension.js";
16
18
  import { getRendererTraceBatchReceiver } from "./renderer_trace_batch_receiver_registry.js";
17
19
  import { resolveWoodAppEnv } from "./app_env.js";
18
20
  const logger = getLogger("wood:runtime");
@@ -123,6 +125,8 @@ function registerReservedRuntimeHandlers(router, options, settings, rendererTrac
123
125
  requireRuntimeHandler("__contextMenu", "onContextMenu", options.onContextMenu)
124
126
  );
125
127
  router.registerReservedHandler("__woodConfig", async () => ({
128
+ // App extension fields first so Wood's reserved keys always win.
129
+ ...await resolveRendererConfigExtension(),
126
130
  rendererTraceEnabled: settings.rendererTraceEnabled,
127
131
  rendererTraceMinLevel: settings.rendererTraceMinLevel,
128
132
  rendererTraceBatchEnabled: settings.rendererTraceBatchEnabled,
@@ -173,6 +177,16 @@ function registerRendererLogBridge(ipcMain) {
173
177
  }
174
178
  });
175
179
  });
180
+ ipcMain.removeAllListeners?.("__log_batch");
181
+ ipcMain.on("__log_batch", (_event, rawPayload) => {
182
+ const result = dispatchRendererLogBatch(rawPayload);
183
+ if (!result.ok) {
184
+ logger.warn("renderer log batch rejected", {
185
+ reason: result.reason ?? null,
186
+ errorMessage: result.errorMessage ?? null
187
+ });
188
+ }
189
+ });
176
190
  logger.info("renderer log bridge registered (fire-and-forget)");
177
191
  }
178
192
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/runtime.ts"],"sourcesContent":["import { getLogger } from '@noego/logger';\nimport { WoodRouter } from '../router/wood_router.js';\nimport { getContainer } from './get_container.js';\nimport {\n RendererTraceBatchHost,\n TraceProviderConfiguration,\n dispatchRendererTracePayload,\n} from '../tracing/index.js';\nimport {\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n type TraceLevel,\n type TraceRuntimeContext,\n type WoodTraceBatchReceiver,\n} from '../tracing/index.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogDispatchResult,\n type RendererLogEntry,\n type RendererLogLevel,\n} from './renderer_log_dispatcher.js';\nimport { getRendererTraceBatchReceiver } from './renderer_trace_batch_receiver_registry.js';\nimport { resolveWoodAppEnv, type WoodAppEnv } from './app_env.js';\n\nconst logger = getLogger('wood:runtime');\n\ntype IpcMainLike = {\n handle: (channel: string, handler: (_event: unknown, data: unknown) => Promise<unknown> | unknown) => void;\n on: (channel: string, handler: (_event: unknown, ...args: unknown[]) => void) => void;\n removeHandler?: (channel: string) => void;\n removeAllListeners?: (channel: string) => void;\n};\n\nexport interface RegisterRuntimeRouter {\n registerController(name: string, cls: any): void;\n loadOperations(operations: Map<string, any>): void;\n}\n\nexport interface RuntimeOptions {\n ipcMain: IpcMainLike;\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n container?: any;\n controllersDir: string;\n middlewareDir: string;\n mode?: 'bundled' | 'dev';\n /** Override the resolved app environment tier reported to renderers via __woodConfig. */\n appEnv?: WoodAppEnv;\n enableRendererLogBridge?: boolean;\n enableRendererTraceBridge?: boolean;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n /** Resolves only with the acknowledgement produced by the downstream durable receiver. */\n rendererTraceBatchReceiver?: WoodTraceBatchReceiver;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onNavigate?: (data: unknown, event?: unknown) => Promise<unknown>;\n onWindow?: (data: unknown, event?: unknown) => Promise<unknown>;\n onDebug?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n resolveRendererTraceContext?: (event: unknown) => TraceRuntimeContext | undefined;\n}\n\nexport interface RuntimeResult {\n router: WoodRouter;\n}\n\ntype RendererLogPayload = {\n level: string;\n logger: string;\n message: string;\n context?: unknown;\n};\n\ntype IpcInvokeEventLike = {\n sender?: {\n id?: number;\n };\n};\n\ntype RuntimeReservedHandler = (\n data: unknown,\n event?: unknown,\n) => Promise<unknown>;\n\ntype RuntimeTraceSettings = Readonly<{\n enableRendererLogBridge: boolean;\n enableRendererTraceBridge: boolean;\n traceMinLevel: TraceLevel | undefined;\n rendererTraceEnabled: boolean;\n rendererTraceMinLevel: TraceLevel;\n rendererTraceBatchEnabled: boolean;\n}>;\n\nfunction normalizeRendererLogLevel(level: unknown): RendererLogLevel {\n if (typeof level === 'string') {\n const normalized = level.trim().toLowerCase();\n if (normalized.length > 0) {\n return normalized;\n }\n }\n return 'info';\n}\n\nfunction coerceRendererLogPayload(\n rawPayload: unknown,\n event: unknown,\n): RendererLogEntry {\n const payload = (rawPayload ?? {}) as Partial<RendererLogPayload>;\n const ipcEvent = event as IpcInvokeEventLike;\n\n return {\n level: normalizeRendererLogLevel(payload.level),\n logger: typeof payload.logger === 'string' && payload.logger.trim().length > 0\n ? payload.logger\n : 'renderer',\n message: typeof payload.message === 'string' ? payload.message : '',\n context: payload.context,\n timestamp: new Date().toISOString(),\n senderId: typeof ipcEvent?.sender?.id === 'number' ? ipcEvent.sender.id : null,\n };\n}\n\n/**\n * Boot a bundled-safe Wood runtime using pre-generated static runtime artifacts.\n */\nexport async function runtime(options: RuntimeOptions): Promise<RuntimeResult> {\n const {\n ipcMain,\n registerRuntime,\n controllersDir,\n middlewareDir,\n mode = 'bundled',\n enableRendererLogBridge = true,\n enableRendererTraceBridge = true,\n traceMinLevel,\n rendererTraceEnabled = false,\n rendererTraceMinLevel = 'debug',\n } = options;\n\n const container = options.container ?? getContainer();\n const rendererTraceBatchReceiver = options.rendererTraceBatchReceiver\n ?? getRendererTraceBatchReceiver();\n const rendererTraceBatchEnabled = rendererTraceBatchReceiver !== null;\n const rendererTraceBatchHost = new RendererTraceBatchHost(\n rendererTraceBatchReceiver,\n );\n\n logger.info('runtime start', {\n mode,\n hasProvidedContainer: !!options.container,\n controllersDir,\n middlewareDir,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel: traceMinLevel ?? 'debug',\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n });\n\n const traceSettings: RuntimeTraceSettings = {\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel,\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n };\n await configureRuntimeTracing(container, traceSettings);\n\n const router = new WoodRouter({\n container,\n controllersDir,\n middlewareDir,\n mode,\n });\n\n registerRuntime(router);\n logger.info('runtime registration complete');\n\n router.validateControllers();\n logger.info('runtime controller validation complete');\n\n router.precompileSchemas();\n logger.info('runtime schema pre-compilation complete');\n\n registerReservedRuntimeHandlers(router, options, traceSettings, rendererTraceBatchHost);\n registerRuntimeObservationBridges(ipcMain, traceSettings, options.resolveRendererTraceContext);\n\n router.registerIpcHandlers(ipcMain);\n logger.info('runtime ipc handlers registered', {\n channelCount: router.getChannels().length,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n });\n\n return { router };\n}\n\nasync function configureRuntimeTracing(\n container: any,\n settings: RuntimeTraceSettings,\n): Promise<void> {\n if (settings.traceMinLevel) {\n setMainTraceMinLevel(settings.traceMinLevel);\n }\n setRendererTraceMinLevel(settings.rendererTraceMinLevel);\n if (typeof container.instance !== 'function') {\n return;\n }\n\n const traceConfiguration = await container.instance(TraceProviderConfiguration);\n traceConfiguration.configureRuntime('main');\n if (settings.traceMinLevel) {\n traceConfiguration.configureMinLevel(settings.traceMinLevel);\n }\n}\n\nfunction registerReservedRuntimeHandlers(\n router: WoodRouter,\n options: RuntimeOptions,\n settings: RuntimeTraceSettings,\n rendererTraceBatchHost: RendererTraceBatchHost,\n): void {\n router.registerReservedHandler('__load', requireRuntimeHandler('__load', 'onLoad', options.onLoad));\n router.registerReservedHandler('__navigate', requireRuntimeHandler('__navigate', 'onNavigate', options.onNavigate));\n router.registerReservedHandler('__window', requireRuntimeHandler('__window', 'onWindow', options.onWindow));\n router.registerReservedHandler('__debug', requireRuntimeHandler('__debug', 'onDebug', options.onDebug));\n router.registerReservedHandler(\n '__contextMenu',\n requireRuntimeHandler('__contextMenu', 'onContextMenu', options.onContextMenu),\n );\n router.registerReservedHandler('__woodConfig', async () => ({\n rendererTraceEnabled: settings.rendererTraceEnabled,\n rendererTraceMinLevel: settings.rendererTraceMinLevel,\n rendererTraceBatchEnabled: settings.rendererTraceBatchEnabled,\n appEnv: options.appEnv ?? await resolveWoodAppEnv(),\n }));\n router.registerReservedHandler('__traceBatch', async (data, event) => {\n return rendererTraceBatchHost.receive(data, options.resolveRendererTraceContext?.(event) ?? {});\n });\n}\n\nfunction registerRuntimeObservationBridges(\n ipcMain: IpcMainLike,\n settings: RuntimeTraceSettings,\n resolveContext: RuntimeOptions['resolveRendererTraceContext'],\n): void {\n if (settings.enableRendererLogBridge) {\n registerRendererLogBridge(ipcMain);\n }\n ipcMain.removeAllListeners?.('__trace');\n if (settings.enableRendererTraceBridge && settings.rendererTraceEnabled) {\n registerRendererTraceBridge(ipcMain, resolveContext);\n }\n}\n\nfunction requireRuntimeHandler(\n channel: string,\n optionName: string,\n handler: RuntimeReservedHandler | undefined,\n): RuntimeReservedHandler {\n return async (data, event) => {\n if (!handler) {\n throw new Error(`Reserved channel \"${channel}\" not configured. Provide runtime({ ${optionName} }).`);\n }\n return handler(data, event);\n };\n}\n\nfunction registerRendererTraceBridge(\n ipcMain: IpcMainLike,\n resolveContext?: (event: unknown) => TraceRuntimeContext | undefined,\n): void {\n ipcMain.removeAllListeners?.('__trace');\n\n ipcMain.on('__trace', (event, rawPayload) => {\n dispatchRendererTracePayload(rawPayload, resolveContext?.(event));\n });\n\n logger.info('renderer trace bridge registered (fire-and-forget)');\n}\n\nfunction registerRendererLogBridge(ipcMain: IpcMainLike): void {\n const dispatcher = getRendererLogDispatcher();\n\n ipcMain.removeAllListeners?.('__log');\n\n ipcMain.on('__log', (_event, rawPayload) => {\n const entry = coerceRendererLogPayload(rawPayload, null);\n void dispatcher.dispatch(entry).then((result) => {\n const typedResult = result as RendererLogDispatchResult;\n if (!typedResult.ok) {\n logger.warn('renderer log dispatch failed', {\n logger: entry.logger,\n messageLength: entry.message.length,\n reason: typedResult.reason ?? null,\n errorMessage: typedResult.errorMessage ?? null,\n });\n }\n });\n });\n\n logger.info('renderer log bridge registered (fire-and-forget)');\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/runtime/runtime.ts"],"sourcesContent":["import { getLogger } from '@noego/logger';\nimport { WoodRouter } from '../router/wood_router.js';\nimport { getContainer } from './get_container.js';\nimport {\n RendererTraceBatchHost,\n TraceProviderConfiguration,\n dispatchRendererTracePayload,\n} from '../tracing/index.js';\nimport {\n setMainTraceMinLevel,\n setRendererTraceMinLevel,\n type TraceLevel,\n type TraceRuntimeContext,\n type WoodTraceBatchReceiver,\n} from '../tracing/index.js';\nimport {\n getRendererLogDispatcher,\n type RendererLogDispatchResult,\n type RendererLogEntry,\n type RendererLogLevel,\n} from './renderer_log_dispatcher.js';\nimport { dispatchRendererLogBatch } from './renderer_log_batch_receiver.js';\nimport { resolveRendererConfigExtension } from './renderer_config_extension.js';\nimport { getRendererTraceBatchReceiver } from './renderer_trace_batch_receiver_registry.js';\nimport { resolveWoodAppEnv, type WoodAppEnv } from './app_env.js';\n\nconst logger = getLogger('wood:runtime');\n\ntype IpcMainLike = {\n handle: (channel: string, handler: (_event: unknown, data: unknown) => Promise<unknown> | unknown) => void;\n on: (channel: string, handler: (_event: unknown, ...args: unknown[]) => void) => void;\n removeHandler?: (channel: string) => void;\n removeAllListeners?: (channel: string) => void;\n};\n\nexport interface RegisterRuntimeRouter {\n registerController(name: string, cls: any): void;\n loadOperations(operations: Map<string, any>): void;\n}\n\nexport interface RuntimeOptions {\n ipcMain: IpcMainLike;\n registerRuntime: (router: RegisterRuntimeRouter) => void;\n container?: any;\n controllersDir: string;\n middlewareDir: string;\n mode?: 'bundled' | 'dev';\n /** Override the resolved app environment tier reported to renderers via __woodConfig. */\n appEnv?: WoodAppEnv;\n enableRendererLogBridge?: boolean;\n enableRendererTraceBridge?: boolean;\n traceMinLevel?: TraceLevel;\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: TraceLevel;\n /** Resolves only with the acknowledgement produced by the downstream durable receiver. */\n rendererTraceBatchReceiver?: WoodTraceBatchReceiver;\n onLoad?: (data: unknown, event?: unknown) => Promise<unknown>;\n onNavigate?: (data: unknown, event?: unknown) => Promise<unknown>;\n onWindow?: (data: unknown, event?: unknown) => Promise<unknown>;\n onDebug?: (data: unknown, event?: unknown) => Promise<unknown>;\n onContextMenu?: (data: unknown, event?: unknown) => Promise<unknown>;\n resolveRendererTraceContext?: (event: unknown) => TraceRuntimeContext | undefined;\n}\n\nexport interface RuntimeResult {\n router: WoodRouter;\n}\n\ntype RendererLogPayload = {\n level: string;\n logger: string;\n message: string;\n context?: unknown;\n};\n\ntype IpcInvokeEventLike = {\n sender?: {\n id?: number;\n };\n};\n\ntype RuntimeReservedHandler = (\n data: unknown,\n event?: unknown,\n) => Promise<unknown>;\n\ntype RuntimeTraceSettings = Readonly<{\n enableRendererLogBridge: boolean;\n enableRendererTraceBridge: boolean;\n traceMinLevel: TraceLevel | undefined;\n rendererTraceEnabled: boolean;\n rendererTraceMinLevel: TraceLevel;\n rendererTraceBatchEnabled: boolean;\n}>;\n\nfunction normalizeRendererLogLevel(level: unknown): RendererLogLevel {\n if (typeof level === 'string') {\n const normalized = level.trim().toLowerCase();\n if (normalized.length > 0) {\n return normalized;\n }\n }\n return 'info';\n}\n\nfunction coerceRendererLogPayload(\n rawPayload: unknown,\n event: unknown,\n): RendererLogEntry {\n const payload = (rawPayload ?? {}) as Partial<RendererLogPayload>;\n const ipcEvent = event as IpcInvokeEventLike;\n\n return {\n level: normalizeRendererLogLevel(payload.level),\n logger: typeof payload.logger === 'string' && payload.logger.trim().length > 0\n ? payload.logger\n : 'renderer',\n message: typeof payload.message === 'string' ? payload.message : '',\n context: payload.context,\n timestamp: new Date().toISOString(),\n senderId: typeof ipcEvent?.sender?.id === 'number' ? ipcEvent.sender.id : null,\n };\n}\n\n/**\n * Boot a bundled-safe Wood runtime using pre-generated static runtime artifacts.\n */\nexport async function runtime(options: RuntimeOptions): Promise<RuntimeResult> {\n const {\n ipcMain,\n registerRuntime,\n controllersDir,\n middlewareDir,\n mode = 'bundled',\n enableRendererLogBridge = true,\n enableRendererTraceBridge = true,\n traceMinLevel,\n rendererTraceEnabled = false,\n rendererTraceMinLevel = 'debug',\n } = options;\n\n const container = options.container ?? getContainer();\n const rendererTraceBatchReceiver = options.rendererTraceBatchReceiver\n ?? getRendererTraceBatchReceiver();\n const rendererTraceBatchEnabled = rendererTraceBatchReceiver !== null;\n const rendererTraceBatchHost = new RendererTraceBatchHost(\n rendererTraceBatchReceiver,\n );\n\n logger.info('runtime start', {\n mode,\n hasProvidedContainer: !!options.container,\n controllersDir,\n middlewareDir,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel: traceMinLevel ?? 'debug',\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n });\n\n const traceSettings: RuntimeTraceSettings = {\n enableRendererLogBridge,\n enableRendererTraceBridge,\n traceMinLevel,\n rendererTraceEnabled,\n rendererTraceMinLevel,\n rendererTraceBatchEnabled,\n };\n await configureRuntimeTracing(container, traceSettings);\n\n const router = new WoodRouter({\n container,\n controllersDir,\n middlewareDir,\n mode,\n });\n\n registerRuntime(router);\n logger.info('runtime registration complete');\n\n router.validateControllers();\n logger.info('runtime controller validation complete');\n\n router.precompileSchemas();\n logger.info('runtime schema pre-compilation complete');\n\n registerReservedRuntimeHandlers(router, options, traceSettings, rendererTraceBatchHost);\n registerRuntimeObservationBridges(ipcMain, traceSettings, options.resolveRendererTraceContext);\n\n router.registerIpcHandlers(ipcMain);\n logger.info('runtime ipc handlers registered', {\n channelCount: router.getChannels().length,\n enableRendererLogBridge,\n enableRendererTraceBridge,\n });\n\n return { router };\n}\n\nasync function configureRuntimeTracing(\n container: any,\n settings: RuntimeTraceSettings,\n): Promise<void> {\n if (settings.traceMinLevel) {\n setMainTraceMinLevel(settings.traceMinLevel);\n }\n setRendererTraceMinLevel(settings.rendererTraceMinLevel);\n if (typeof container.instance !== 'function') {\n return;\n }\n\n const traceConfiguration = await container.instance(TraceProviderConfiguration);\n traceConfiguration.configureRuntime('main');\n if (settings.traceMinLevel) {\n traceConfiguration.configureMinLevel(settings.traceMinLevel);\n }\n}\n\nfunction registerReservedRuntimeHandlers(\n router: WoodRouter,\n options: RuntimeOptions,\n settings: RuntimeTraceSettings,\n rendererTraceBatchHost: RendererTraceBatchHost,\n): void {\n router.registerReservedHandler('__load', requireRuntimeHandler('__load', 'onLoad', options.onLoad));\n router.registerReservedHandler('__navigate', requireRuntimeHandler('__navigate', 'onNavigate', options.onNavigate));\n router.registerReservedHandler('__window', requireRuntimeHandler('__window', 'onWindow', options.onWindow));\n router.registerReservedHandler('__debug', requireRuntimeHandler('__debug', 'onDebug', options.onDebug));\n router.registerReservedHandler(\n '__contextMenu',\n requireRuntimeHandler('__contextMenu', 'onContextMenu', options.onContextMenu),\n );\n router.registerReservedHandler('__woodConfig', async () => ({\n // App extension fields first so Wood's reserved keys always win.\n ...(await resolveRendererConfigExtension()),\n rendererTraceEnabled: settings.rendererTraceEnabled,\n rendererTraceMinLevel: settings.rendererTraceMinLevel,\n rendererTraceBatchEnabled: settings.rendererTraceBatchEnabled,\n appEnv: options.appEnv ?? await resolveWoodAppEnv(),\n }));\n router.registerReservedHandler('__traceBatch', async (data, event) => {\n return rendererTraceBatchHost.receive(data, options.resolveRendererTraceContext?.(event) ?? {});\n });\n}\n\nfunction registerRuntimeObservationBridges(\n ipcMain: IpcMainLike,\n settings: RuntimeTraceSettings,\n resolveContext: RuntimeOptions['resolveRendererTraceContext'],\n): void {\n if (settings.enableRendererLogBridge) {\n registerRendererLogBridge(ipcMain);\n }\n ipcMain.removeAllListeners?.('__trace');\n if (settings.enableRendererTraceBridge && settings.rendererTraceEnabled) {\n registerRendererTraceBridge(ipcMain, resolveContext);\n }\n}\n\nfunction requireRuntimeHandler(\n channel: string,\n optionName: string,\n handler: RuntimeReservedHandler | undefined,\n): RuntimeReservedHandler {\n return async (data, event) => {\n if (!handler) {\n throw new Error(`Reserved channel \"${channel}\" not configured. Provide runtime({ ${optionName} }).`);\n }\n return handler(data, event);\n };\n}\n\nfunction registerRendererTraceBridge(\n ipcMain: IpcMainLike,\n resolveContext?: (event: unknown) => TraceRuntimeContext | undefined,\n): void {\n ipcMain.removeAllListeners?.('__trace');\n\n ipcMain.on('__trace', (event, rawPayload) => {\n dispatchRendererTracePayload(rawPayload, resolveContext?.(event));\n });\n\n logger.info('renderer trace bridge registered (fire-and-forget)');\n}\n\nfunction registerRendererLogBridge(ipcMain: IpcMainLike): void {\n const dispatcher = getRendererLogDispatcher();\n\n ipcMain.removeAllListeners?.('__log');\n\n ipcMain.on('__log', (_event, rawPayload) => {\n const entry = coerceRendererLogPayload(rawPayload, null);\n void dispatcher.dispatch(entry).then((result) => {\n const typedResult = result as RendererLogDispatchResult;\n if (!typedResult.ok) {\n logger.warn('renderer log dispatch failed', {\n logger: entry.logger,\n messageLength: entry.message.length,\n reason: typedResult.reason ?? null,\n errorMessage: typedResult.errorMessage ?? null,\n });\n }\n });\n });\n\n ipcMain.removeAllListeners?.('__log_batch');\n\n ipcMain.on('__log_batch', (_event, rawPayload) => {\n const result = dispatchRendererLogBatch(rawPayload);\n if (!result.ok) {\n logger.warn('renderer log batch rejected', {\n reason: result.reason ?? null,\n errorMessage: result.errorMessage ?? null,\n });\n }\n });\n\n logger.info('renderer log bridge registered (fire-and-forget)');\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/stick/index.ts"],"sourcesContent":["import './allow_cli.cjs';\n// Type-only import — erased at runtime, won't trigger module loading\nimport type { WoodStaticRenderer } from './static_renderer.cjs';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { resolveImageRendererTemplate } from './template_resolver.cjs';\n\nexport type { WoodStaticRenderer };\nexport type {\n WoodStaticRendererConfig,\n WoodRenderOptions,\n WoodRenderToPageOptions,\n WoodRenderResult,\n} from './static_renderer';\n\n// ─────────────────────────────────────────────────────────────\n// Error Classes\n// ─────────────────────────────────────────────────────────────\n\nexport class PlaywrightNotInstalledError extends Error {\n name = 'PlaywrightNotInstalledError' as const;\n constructor() {\n super(\n 'ImageRenderer requires Playwright. Install it with:\\n' +\n ' npm install playwright\\n' +\n ' npx playwright install chromium'\n );\n }\n}\n\nexport class BrowserLaunchError extends Error {\n name = 'BrowserLaunchError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\nexport class RenderTimeoutError extends Error {\n name = 'RenderTimeoutError' as const;\n constructor(timeoutMs: number) {\n super(`Render timed out after ${timeoutMs}ms`);\n }\n}\n\nexport class RenderError extends Error {\n name = 'RenderError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────\n\nexport interface ImageRendererConfig {\n /** Output directory for screenshots */\n outputDir: string;\n /** Default width (default: 1920) */\n width?: number;\n /** Default height (default: 1080) */\n height?: number;\n /** Device scale factor for retina (default: 1) */\n deviceScaleFactor?: number;\n /** Image format (default: 'png') */\n format?: 'png' | 'jpeg';\n /** JPEG quality 0-100 (default: 80) */\n quality?: number;\n /** Wait condition (default: 'networkidle') */\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n /** Timeout in ms (default: 10000) */\n timeoutMs?: number;\n /** Custom HTML template with {{{HEAD}}}, {{{CSS}}}, {{{APP}}} placeholders */\n template?: string;\n\n // Static renderer config (one of these)\n /** Pre-created WoodStaticRenderer instance */\n staticRenderer?: WoodStaticRenderer;\n /** OR: Path to views.yaml */\n viewsConfig?: string;\n /**\n * Component directory (required if viewsConfig provided).\n * When template is omitted, stick auto-loads <componentDir>/index.html and\n * inlines <componentDir>/styles.css when present.\n */\n componentDir?: string;\n /** Asset path mappings (e.g., { '/images': ['public/images'] }) */\n assets?: Record<string, string[]>;\n}\n\nexport interface CaptureOptions {\n /** View component data */\n view?: any;\n /** Layout data (one object per layout) */\n layout?: any[];\n /** Wood bridge object passed as a prop to layouts and views */\n wood?: any;\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\nexport interface CaptureHtmlOptions {\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\ninterface CaptureResult {\n /** Raw image buffer */\n buffer: Buffer;\n /** Path where image was saved */\n path: string;\n}\n\n// ─────────────────────────────────────────────────────────────\n// Playwright Loader\n// ─────────────────────────────────────────────────────────────\n\ntype PlaywrightModule = typeof import('playwright');\n\nasync function loadPlaywright(): Promise<PlaywrightModule> {\n try {\n const playwright = await import('playwright');\n return playwright;\n } catch {\n throw new PlaywrightNotInstalledError();\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Content Type Helper\n// ─────────────────────────────────────────────────────────────\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n const types: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n };\n return types[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────\n// ImageRenderer Class\n// ─────────────────────────────────────────────────────────────\n\nexport class ImageRenderer {\n private browser: any;\n private staticRenderer: WoodStaticRenderer;\n private config: Required<Omit<ImageRendererConfig, 'staticRenderer' | 'viewsConfig' | 'componentDir' | 'template' | 'assets'>> & {\n outputDir: string;\n template?: string;\n assets?: Record<string, string[]>;\n };\n\n constructor(\n browser: any,\n staticRenderer: WoodStaticRenderer,\n config: ImageRendererConfig\n ) {\n this.browser = browser;\n this.staticRenderer = staticRenderer;\n this.config = {\n outputDir: config.outputDir,\n width: config.width ?? 1920,\n height: config.height ?? 1080,\n deviceScaleFactor: config.deviceScaleFactor ?? 1,\n format: config.format ?? 'png',\n quality: config.quality ?? 80,\n waitUntil: config.waitUntil ?? 'networkidle',\n timeoutMs: config.timeoutMs ?? 10000,\n template: config.template,\n assets: config.assets,\n };\n }\n\n /**\n * Capture a view by dot-path key and save to outputDir\n */\n async capture(\n name: string,\n viewKey: string,\n options?: CaptureOptions\n ): Promise<CaptureResult> {\n const { view, layout, wood } = options ?? {};\n const html = await this.staticRenderer.renderToPage({\n viewKey,\n data: { view, layout },\n wood,\n template: this.config.template,\n });\n\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Capture raw HTML and save to outputDir\n */\n async captureHtml(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Internal method to capture HTML to image\n */\n private async captureHtmlInternal(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n const width = options?.width ?? this.config.width;\n const height = options?.height ?? this.config.height;\n\n let context: any;\n let page: any;\n\n try {\n context = await this.browser.newContext({\n viewport: { width, height },\n deviceScaleFactor: this.config.deviceScaleFactor,\n });\n\n page = await context.newPage();\n\n // Route Interception Strategy:\n // We use page.goto() with a fake URL instead of page.setContent() because\n // setContent() has no base URL, so relative paths can't resolve.\n const FAKE_BASE_URL = 'http://stick-renderer/';\n\n await page.route(FAKE_BASE_URL, async (route: any) => {\n await route.fulfill({\n body: html,\n contentType: 'text/html',\n });\n });\n\n // Asset route interception\n if (this.config.assets) {\n for (const [urlPath, fsPaths] of Object.entries(this.config.assets)) {\n await page.route(`**${urlPath}/**`, async (route: any) => {\n const url = new URL(route.request().url());\n const relativePath = url.pathname.replace(urlPath, '');\n\n for (const fsPath of fsPaths) {\n const filePath = path.join(fsPath, relativePath);\n try {\n const body = await fs.promises.readFile(filePath);\n const contentType = getContentType(filePath);\n await route.fulfill({ body, contentType });\n return;\n } catch {\n // File not found in this path, try the next one\n }\n }\n\n await route.abort('filenotfound');\n });\n }\n }\n\n const waitUntilMapping = {\n 'load': 'load',\n 'domcontentloaded': 'domcontentloaded',\n 'networkidle': 'networkidle',\n } as const;\n\n try {\n await page.goto(FAKE_BASE_URL, {\n waitUntil: waitUntilMapping[this.config.waitUntil],\n timeout: this.config.timeoutMs,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('timeout')) {\n throw new RenderTimeoutError(this.config.timeoutMs);\n }\n throw new RenderError(\n 'Failed to render HTML content',\n error instanceof Error ? error : undefined\n );\n }\n\n if (options?.beforeCapture) {\n await options.beforeCapture(page);\n }\n\n const screenshotOptions: {\n type: 'png' | 'jpeg';\n quality?: number;\n fullPage: boolean;\n } = {\n type: this.config.format,\n fullPage: options?.fullPage ?? true,\n };\n\n if (this.config.format === 'jpeg') {\n screenshotOptions.quality = this.config.quality;\n }\n\n const screenshot = await page.screenshot(screenshotOptions);\n const buffer = Buffer.from(screenshot);\n\n const filename = `${name}@${width}x${height}.${this.config.format}`;\n const outputPath = path.join(this.config.outputDir, filename);\n\n await fs.promises.mkdir(this.config.outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, buffer);\n\n return { buffer, path: outputPath };\n\n } finally {\n if (page) {\n await page.close().catch(() => {});\n }\n if (context) {\n await context.close().catch(() => {});\n }\n }\n }\n\n /**\n * Close browser and clean up resources\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close().catch(() => {});\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Factory Function\n// ─────────────────────────────────────────────────────────────\n\nexport async function createImageRenderer(\n config: ImageRendererConfig\n): Promise<ImageRenderer> {\n const resolvedTemplate = resolveImageRendererTemplate({\n template: config.template,\n componentDir: config.componentDir,\n });\n const resolvedConfig: ImageRendererConfig = {\n ...config,\n template: resolvedTemplate,\n };\n\n const playwright = await loadPlaywright();\n\n let browser: any;\n try {\n browser = await playwright.chromium.launch({\n headless: true,\n });\n } catch (error) {\n throw new BrowserLaunchError(\n 'Failed to launch Chromium. Ensure it is installed with: npx playwright install chromium',\n error instanceof Error ? error : undefined\n );\n }\n\n let staticRenderer: WoodStaticRenderer;\n if (resolvedConfig.staticRenderer) {\n staticRenderer = resolvedConfig.staticRenderer;\n } else if (resolvedConfig.viewsConfig && resolvedConfig.componentDir) {\n const { createWoodStaticRenderer } = await import('./static_renderer.cjs');\n staticRenderer = createWoodStaticRenderer({\n viewsConfig: resolvedConfig.viewsConfig,\n componentDir: resolvedConfig.componentDir,\n });\n } else {\n throw new Error(\n 'ImageRendererConfig requires either staticRenderer or both viewsConfig and componentDir'\n );\n }\n\n return new ImageRenderer(browser, staticRenderer, resolvedConfig);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/stick/index.ts"],"sourcesContent":["import './allow_cli.cjs';\n// Type-only import — erased at runtime, won't trigger module loading\nimport type { WoodStaticRenderer } from './static_renderer.cjs';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { resolveImageRendererTemplate } from './template_resolver.cjs';\n\nexport type { WoodStaticRenderer };\nexport type {\n WoodStaticRendererConfig,\n WoodRenderOptions,\n WoodRenderToPageOptions,\n WoodRenderResult,\n} from './static_renderer.cjs';\n\n// ─────────────────────────────────────────────────────────────\n// Error Classes\n// ─────────────────────────────────────────────────────────────\n\nexport class PlaywrightNotInstalledError extends Error {\n name = 'PlaywrightNotInstalledError' as const;\n constructor() {\n super(\n 'ImageRenderer requires Playwright. Install it with:\\n' +\n ' npm install playwright\\n' +\n ' npx playwright install chromium'\n );\n }\n}\n\nexport class BrowserLaunchError extends Error {\n name = 'BrowserLaunchError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\nexport class RenderTimeoutError extends Error {\n name = 'RenderTimeoutError' as const;\n constructor(timeoutMs: number) {\n super(`Render timed out after ${timeoutMs}ms`);\n }\n}\n\nexport class RenderError extends Error {\n name = 'RenderError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────\n\nexport interface ImageRendererConfig {\n /** Output directory for screenshots */\n outputDir: string;\n /** Default width (default: 1920) */\n width?: number;\n /** Default height (default: 1080) */\n height?: number;\n /** Device scale factor for retina (default: 1) */\n deviceScaleFactor?: number;\n /** Image format (default: 'png') */\n format?: 'png' | 'jpeg';\n /** JPEG quality 0-100 (default: 80) */\n quality?: number;\n /** Wait condition (default: 'networkidle') */\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n /** Timeout in ms (default: 10000) */\n timeoutMs?: number;\n /** Custom HTML template with {{{HEAD}}}, {{{CSS}}}, {{{APP}}} placeholders */\n template?: string;\n\n // Static renderer config (one of these)\n /** Pre-created WoodStaticRenderer instance */\n staticRenderer?: WoodStaticRenderer;\n /** OR: Path to views.yaml */\n viewsConfig?: string;\n /**\n * Component directory (required if viewsConfig provided).\n * When template is omitted, stick auto-loads <componentDir>/index.html and\n * inlines <componentDir>/styles.css when present.\n */\n componentDir?: string;\n /** Asset path mappings (e.g., { '/images': ['public/images'] }) */\n assets?: Record<string, string[]>;\n}\n\nexport interface CaptureOptions {\n /** View component data */\n view?: any;\n /** Layout data (one object per layout) */\n layout?: any[];\n /** Wood bridge object passed as a prop to layouts and views */\n wood?: any;\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\nexport interface CaptureHtmlOptions {\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\ninterface CaptureResult {\n /** Raw image buffer */\n buffer: Buffer;\n /** Path where image was saved */\n path: string;\n}\n\n// ─────────────────────────────────────────────────────────────\n// Playwright Loader\n// ─────────────────────────────────────────────────────────────\n\ntype PlaywrightModule = typeof import('playwright');\n\nasync function loadPlaywright(): Promise<PlaywrightModule> {\n try {\n const playwright = await import('playwright');\n return playwright;\n } catch {\n throw new PlaywrightNotInstalledError();\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Content Type Helper\n// ─────────────────────────────────────────────────────────────\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n const types: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n };\n return types[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────\n// ImageRenderer Class\n// ─────────────────────────────────────────────────────────────\n\nexport class ImageRenderer {\n private browser: any;\n private staticRenderer: WoodStaticRenderer;\n private config: Required<Omit<ImageRendererConfig, 'staticRenderer' | 'viewsConfig' | 'componentDir' | 'template' | 'assets'>> & {\n outputDir: string;\n template?: string;\n assets?: Record<string, string[]>;\n };\n\n constructor(\n browser: any,\n staticRenderer: WoodStaticRenderer,\n config: ImageRendererConfig\n ) {\n this.browser = browser;\n this.staticRenderer = staticRenderer;\n this.config = {\n outputDir: config.outputDir,\n width: config.width ?? 1920,\n height: config.height ?? 1080,\n deviceScaleFactor: config.deviceScaleFactor ?? 1,\n format: config.format ?? 'png',\n quality: config.quality ?? 80,\n waitUntil: config.waitUntil ?? 'networkidle',\n timeoutMs: config.timeoutMs ?? 10000,\n template: config.template,\n assets: config.assets,\n };\n }\n\n /**\n * Capture a view by dot-path key and save to outputDir\n */\n async capture(\n name: string,\n viewKey: string,\n options?: CaptureOptions\n ): Promise<CaptureResult> {\n const { view, layout, wood } = options ?? {};\n const html = await this.staticRenderer.renderToPage({\n viewKey,\n data: { view, layout },\n wood,\n template: this.config.template,\n });\n\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Capture raw HTML and save to outputDir\n */\n async captureHtml(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Internal method to capture HTML to image\n */\n private async captureHtmlInternal(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n const width = options?.width ?? this.config.width;\n const height = options?.height ?? this.config.height;\n\n let context: any;\n let page: any;\n\n try {\n context = await this.browser.newContext({\n viewport: { width, height },\n deviceScaleFactor: this.config.deviceScaleFactor,\n });\n\n page = await context.newPage();\n\n // Route Interception Strategy:\n // We use page.goto() with a fake URL instead of page.setContent() because\n // setContent() has no base URL, so relative paths can't resolve.\n const FAKE_BASE_URL = 'http://stick-renderer/';\n\n await page.route(FAKE_BASE_URL, async (route: any) => {\n await route.fulfill({\n body: html,\n contentType: 'text/html',\n });\n });\n\n // Asset route interception\n if (this.config.assets) {\n for (const [urlPath, fsPaths] of Object.entries(this.config.assets)) {\n await page.route(`**${urlPath}/**`, async (route: any) => {\n const url = new URL(route.request().url());\n const relativePath = url.pathname.replace(urlPath, '');\n\n for (const fsPath of fsPaths) {\n const filePath = path.join(fsPath, relativePath);\n try {\n const body = await fs.promises.readFile(filePath);\n const contentType = getContentType(filePath);\n await route.fulfill({ body, contentType });\n return;\n } catch {\n // File not found in this path, try the next one\n }\n }\n\n await route.abort('filenotfound');\n });\n }\n }\n\n const waitUntilMapping = {\n 'load': 'load',\n 'domcontentloaded': 'domcontentloaded',\n 'networkidle': 'networkidle',\n } as const;\n\n try {\n await page.goto(FAKE_BASE_URL, {\n waitUntil: waitUntilMapping[this.config.waitUntil],\n timeout: this.config.timeoutMs,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('timeout')) {\n throw new RenderTimeoutError(this.config.timeoutMs);\n }\n throw new RenderError(\n 'Failed to render HTML content',\n error instanceof Error ? error : undefined\n );\n }\n\n if (options?.beforeCapture) {\n await options.beforeCapture(page);\n }\n\n const screenshotOptions: {\n type: 'png' | 'jpeg';\n quality?: number;\n fullPage: boolean;\n } = {\n type: this.config.format,\n fullPage: options?.fullPage ?? true,\n };\n\n if (this.config.format === 'jpeg') {\n screenshotOptions.quality = this.config.quality;\n }\n\n const screenshot = await page.screenshot(screenshotOptions);\n const buffer = Buffer.from(screenshot);\n\n const filename = `${name}@${width}x${height}.${this.config.format}`;\n const outputPath = path.join(this.config.outputDir, filename);\n\n await fs.promises.mkdir(this.config.outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, buffer);\n\n return { buffer, path: outputPath };\n\n } finally {\n if (page) {\n await page.close().catch(() => {});\n }\n if (context) {\n await context.close().catch(() => {});\n }\n }\n }\n\n /**\n * Close browser and clean up resources\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close().catch(() => {});\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Factory Function\n// ─────────────────────────────────────────────────────────────\n\nexport async function createImageRenderer(\n config: ImageRendererConfig\n): Promise<ImageRenderer> {\n const resolvedTemplate = resolveImageRendererTemplate({\n template: config.template,\n componentDir: config.componentDir,\n });\n const resolvedConfig: ImageRendererConfig = {\n ...config,\n template: resolvedTemplate,\n };\n\n const playwright = await loadPlaywright();\n\n let browser: any;\n try {\n browser = await playwright.chromium.launch({\n headless: true,\n });\n } catch (error) {\n throw new BrowserLaunchError(\n 'Failed to launch Chromium. Ensure it is installed with: npx playwright install chromium',\n error instanceof Error ? error : undefined\n );\n }\n\n let staticRenderer: WoodStaticRenderer;\n if (resolvedConfig.staticRenderer) {\n staticRenderer = resolvedConfig.staticRenderer;\n } else if (resolvedConfig.viewsConfig && resolvedConfig.componentDir) {\n const { createWoodStaticRenderer } = await import('./static_renderer.cjs');\n staticRenderer = createWoodStaticRenderer({\n viewsConfig: resolvedConfig.viewsConfig,\n componentDir: resolvedConfig.componentDir,\n });\n } else {\n throw new Error(\n 'ImageRendererConfig requires either staticRenderer or both viewsConfig and componentDir'\n );\n }\n\n return new ImageRenderer(browser, staticRenderer, resolvedConfig);\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/stick/index.ts"],"sourcesContent":["import './allow_cli.js';\n// Type-only import — erased at runtime, won't trigger module loading\nimport type { WoodStaticRenderer } from './static_renderer.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { resolveImageRendererTemplate } from './template_resolver.js';\n\nexport type { WoodStaticRenderer };\nexport type {\n WoodStaticRendererConfig,\n WoodRenderOptions,\n WoodRenderToPageOptions,\n WoodRenderResult,\n} from './static_renderer';\n\n// ─────────────────────────────────────────────────────────────\n// Error Classes\n// ─────────────────────────────────────────────────────────────\n\nexport class PlaywrightNotInstalledError extends Error {\n name = 'PlaywrightNotInstalledError' as const;\n constructor() {\n super(\n 'ImageRenderer requires Playwright. Install it with:\\n' +\n ' npm install playwright\\n' +\n ' npx playwright install chromium'\n );\n }\n}\n\nexport class BrowserLaunchError extends Error {\n name = 'BrowserLaunchError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\nexport class RenderTimeoutError extends Error {\n name = 'RenderTimeoutError' as const;\n constructor(timeoutMs: number) {\n super(`Render timed out after ${timeoutMs}ms`);\n }\n}\n\nexport class RenderError extends Error {\n name = 'RenderError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────\n\nexport interface ImageRendererConfig {\n /** Output directory for screenshots */\n outputDir: string;\n /** Default width (default: 1920) */\n width?: number;\n /** Default height (default: 1080) */\n height?: number;\n /** Device scale factor for retina (default: 1) */\n deviceScaleFactor?: number;\n /** Image format (default: 'png') */\n format?: 'png' | 'jpeg';\n /** JPEG quality 0-100 (default: 80) */\n quality?: number;\n /** Wait condition (default: 'networkidle') */\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n /** Timeout in ms (default: 10000) */\n timeoutMs?: number;\n /** Custom HTML template with {{{HEAD}}}, {{{CSS}}}, {{{APP}}} placeholders */\n template?: string;\n\n // Static renderer config (one of these)\n /** Pre-created WoodStaticRenderer instance */\n staticRenderer?: WoodStaticRenderer;\n /** OR: Path to views.yaml */\n viewsConfig?: string;\n /**\n * Component directory (required if viewsConfig provided).\n * When template is omitted, stick auto-loads <componentDir>/index.html and\n * inlines <componentDir>/styles.css when present.\n */\n componentDir?: string;\n /** Asset path mappings (e.g., { '/images': ['public/images'] }) */\n assets?: Record<string, string[]>;\n}\n\nexport interface CaptureOptions {\n /** View component data */\n view?: any;\n /** Layout data (one object per layout) */\n layout?: any[];\n /** Wood bridge object passed as a prop to layouts and views */\n wood?: any;\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\nexport interface CaptureHtmlOptions {\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\ninterface CaptureResult {\n /** Raw image buffer */\n buffer: Buffer;\n /** Path where image was saved */\n path: string;\n}\n\n// ─────────────────────────────────────────────────────────────\n// Playwright Loader\n// ─────────────────────────────────────────────────────────────\n\ntype PlaywrightModule = typeof import('playwright');\n\nasync function loadPlaywright(): Promise<PlaywrightModule> {\n try {\n const playwright = await import('playwright');\n return playwright;\n } catch {\n throw new PlaywrightNotInstalledError();\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Content Type Helper\n// ─────────────────────────────────────────────────────────────\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n const types: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n };\n return types[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────\n// ImageRenderer Class\n// ─────────────────────────────────────────────────────────────\n\nexport class ImageRenderer {\n private browser: any;\n private staticRenderer: WoodStaticRenderer;\n private config: Required<Omit<ImageRendererConfig, 'staticRenderer' | 'viewsConfig' | 'componentDir' | 'template' | 'assets'>> & {\n outputDir: string;\n template?: string;\n assets?: Record<string, string[]>;\n };\n\n constructor(\n browser: any,\n staticRenderer: WoodStaticRenderer,\n config: ImageRendererConfig\n ) {\n this.browser = browser;\n this.staticRenderer = staticRenderer;\n this.config = {\n outputDir: config.outputDir,\n width: config.width ?? 1920,\n height: config.height ?? 1080,\n deviceScaleFactor: config.deviceScaleFactor ?? 1,\n format: config.format ?? 'png',\n quality: config.quality ?? 80,\n waitUntil: config.waitUntil ?? 'networkidle',\n timeoutMs: config.timeoutMs ?? 10000,\n template: config.template,\n assets: config.assets,\n };\n }\n\n /**\n * Capture a view by dot-path key and save to outputDir\n */\n async capture(\n name: string,\n viewKey: string,\n options?: CaptureOptions\n ): Promise<CaptureResult> {\n const { view, layout, wood } = options ?? {};\n const html = await this.staticRenderer.renderToPage({\n viewKey,\n data: { view, layout },\n wood,\n template: this.config.template,\n });\n\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Capture raw HTML and save to outputDir\n */\n async captureHtml(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Internal method to capture HTML to image\n */\n private async captureHtmlInternal(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n const width = options?.width ?? this.config.width;\n const height = options?.height ?? this.config.height;\n\n let context: any;\n let page: any;\n\n try {\n context = await this.browser.newContext({\n viewport: { width, height },\n deviceScaleFactor: this.config.deviceScaleFactor,\n });\n\n page = await context.newPage();\n\n // Route Interception Strategy:\n // We use page.goto() with a fake URL instead of page.setContent() because\n // setContent() has no base URL, so relative paths can't resolve.\n const FAKE_BASE_URL = 'http://stick-renderer/';\n\n await page.route(FAKE_BASE_URL, async (route: any) => {\n await route.fulfill({\n body: html,\n contentType: 'text/html',\n });\n });\n\n // Asset route interception\n if (this.config.assets) {\n for (const [urlPath, fsPaths] of Object.entries(this.config.assets)) {\n await page.route(`**${urlPath}/**`, async (route: any) => {\n const url = new URL(route.request().url());\n const relativePath = url.pathname.replace(urlPath, '');\n\n for (const fsPath of fsPaths) {\n const filePath = path.join(fsPath, relativePath);\n try {\n const body = await fs.promises.readFile(filePath);\n const contentType = getContentType(filePath);\n await route.fulfill({ body, contentType });\n return;\n } catch {\n // File not found in this path, try the next one\n }\n }\n\n await route.abort('filenotfound');\n });\n }\n }\n\n const waitUntilMapping = {\n 'load': 'load',\n 'domcontentloaded': 'domcontentloaded',\n 'networkidle': 'networkidle',\n } as const;\n\n try {\n await page.goto(FAKE_BASE_URL, {\n waitUntil: waitUntilMapping[this.config.waitUntil],\n timeout: this.config.timeoutMs,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('timeout')) {\n throw new RenderTimeoutError(this.config.timeoutMs);\n }\n throw new RenderError(\n 'Failed to render HTML content',\n error instanceof Error ? error : undefined\n );\n }\n\n if (options?.beforeCapture) {\n await options.beforeCapture(page);\n }\n\n const screenshotOptions: {\n type: 'png' | 'jpeg';\n quality?: number;\n fullPage: boolean;\n } = {\n type: this.config.format,\n fullPage: options?.fullPage ?? true,\n };\n\n if (this.config.format === 'jpeg') {\n screenshotOptions.quality = this.config.quality;\n }\n\n const screenshot = await page.screenshot(screenshotOptions);\n const buffer = Buffer.from(screenshot);\n\n const filename = `${name}@${width}x${height}.${this.config.format}`;\n const outputPath = path.join(this.config.outputDir, filename);\n\n await fs.promises.mkdir(this.config.outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, buffer);\n\n return { buffer, path: outputPath };\n\n } finally {\n if (page) {\n await page.close().catch(() => {});\n }\n if (context) {\n await context.close().catch(() => {});\n }\n }\n }\n\n /**\n * Close browser and clean up resources\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close().catch(() => {});\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Factory Function\n// ─────────────────────────────────────────────────────────────\n\nexport async function createImageRenderer(\n config: ImageRendererConfig\n): Promise<ImageRenderer> {\n const resolvedTemplate = resolveImageRendererTemplate({\n template: config.template,\n componentDir: config.componentDir,\n });\n const resolvedConfig: ImageRendererConfig = {\n ...config,\n template: resolvedTemplate,\n };\n\n const playwright = await loadPlaywright();\n\n let browser: any;\n try {\n browser = await playwright.chromium.launch({\n headless: true,\n });\n } catch (error) {\n throw new BrowserLaunchError(\n 'Failed to launch Chromium. Ensure it is installed with: npx playwright install chromium',\n error instanceof Error ? error : undefined\n );\n }\n\n let staticRenderer: WoodStaticRenderer;\n if (resolvedConfig.staticRenderer) {\n staticRenderer = resolvedConfig.staticRenderer;\n } else if (resolvedConfig.viewsConfig && resolvedConfig.componentDir) {\n const { createWoodStaticRenderer } = await import('./static_renderer.js');\n staticRenderer = createWoodStaticRenderer({\n viewsConfig: resolvedConfig.viewsConfig,\n componentDir: resolvedConfig.componentDir,\n });\n } else {\n throw new Error(\n 'ImageRendererConfig requires either staticRenderer or both viewsConfig and componentDir'\n );\n }\n\n return new ImageRenderer(browser, staticRenderer, resolvedConfig);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/stick/index.ts"],"sourcesContent":["import './allow_cli.js';\n// Type-only import — erased at runtime, won't trigger module loading\nimport type { WoodStaticRenderer } from './static_renderer.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { resolveImageRendererTemplate } from './template_resolver.js';\n\nexport type { WoodStaticRenderer };\nexport type {\n WoodStaticRendererConfig,\n WoodRenderOptions,\n WoodRenderToPageOptions,\n WoodRenderResult,\n} from './static_renderer.js';\n\n// ─────────────────────────────────────────────────────────────\n// Error Classes\n// ─────────────────────────────────────────────────────────────\n\nexport class PlaywrightNotInstalledError extends Error {\n name = 'PlaywrightNotInstalledError' as const;\n constructor() {\n super(\n 'ImageRenderer requires Playwright. Install it with:\\n' +\n ' npm install playwright\\n' +\n ' npx playwright install chromium'\n );\n }\n}\n\nexport class BrowserLaunchError extends Error {\n name = 'BrowserLaunchError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\nexport class RenderTimeoutError extends Error {\n name = 'RenderTimeoutError' as const;\n constructor(timeoutMs: number) {\n super(`Render timed out after ${timeoutMs}ms`);\n }\n}\n\nexport class RenderError extends Error {\n name = 'RenderError' as const;\n constructor(message: string, public cause?: Error) {\n super(message);\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────\n\nexport interface ImageRendererConfig {\n /** Output directory for screenshots */\n outputDir: string;\n /** Default width (default: 1920) */\n width?: number;\n /** Default height (default: 1080) */\n height?: number;\n /** Device scale factor for retina (default: 1) */\n deviceScaleFactor?: number;\n /** Image format (default: 'png') */\n format?: 'png' | 'jpeg';\n /** JPEG quality 0-100 (default: 80) */\n quality?: number;\n /** Wait condition (default: 'networkidle') */\n waitUntil?: 'load' | 'domcontentloaded' | 'networkidle';\n /** Timeout in ms (default: 10000) */\n timeoutMs?: number;\n /** Custom HTML template with {{{HEAD}}}, {{{CSS}}}, {{{APP}}} placeholders */\n template?: string;\n\n // Static renderer config (one of these)\n /** Pre-created WoodStaticRenderer instance */\n staticRenderer?: WoodStaticRenderer;\n /** OR: Path to views.yaml */\n viewsConfig?: string;\n /**\n * Component directory (required if viewsConfig provided).\n * When template is omitted, stick auto-loads <componentDir>/index.html and\n * inlines <componentDir>/styles.css when present.\n */\n componentDir?: string;\n /** Asset path mappings (e.g., { '/images': ['public/images'] }) */\n assets?: Record<string, string[]>;\n}\n\nexport interface CaptureOptions {\n /** View component data */\n view?: any;\n /** Layout data (one object per layout) */\n layout?: any[];\n /** Wood bridge object passed as a prop to layouts and views */\n wood?: any;\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\nexport interface CaptureHtmlOptions {\n /** Override width */\n width?: number;\n /** Override height */\n height?: number;\n /** Capture full page (default: true) */\n fullPage?: boolean;\n /** Callback executed after page load, before screenshot. Receives the Playwright page object. */\n beforeCapture?: (page: any) => Promise<void>;\n}\n\ninterface CaptureResult {\n /** Raw image buffer */\n buffer: Buffer;\n /** Path where image was saved */\n path: string;\n}\n\n// ─────────────────────────────────────────────────────────────\n// Playwright Loader\n// ─────────────────────────────────────────────────────────────\n\ntype PlaywrightModule = typeof import('playwright');\n\nasync function loadPlaywright(): Promise<PlaywrightModule> {\n try {\n const playwright = await import('playwright');\n return playwright;\n } catch {\n throw new PlaywrightNotInstalledError();\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Content Type Helper\n// ─────────────────────────────────────────────────────────────\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n const types: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.svg': 'image/svg+xml',\n '.webp': 'image/webp',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.json': 'application/json',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n };\n return types[ext] || 'application/octet-stream';\n}\n\n// ─────────────────────────────────────────────────────────────\n// ImageRenderer Class\n// ─────────────────────────────────────────────────────────────\n\nexport class ImageRenderer {\n private browser: any;\n private staticRenderer: WoodStaticRenderer;\n private config: Required<Omit<ImageRendererConfig, 'staticRenderer' | 'viewsConfig' | 'componentDir' | 'template' | 'assets'>> & {\n outputDir: string;\n template?: string;\n assets?: Record<string, string[]>;\n };\n\n constructor(\n browser: any,\n staticRenderer: WoodStaticRenderer,\n config: ImageRendererConfig\n ) {\n this.browser = browser;\n this.staticRenderer = staticRenderer;\n this.config = {\n outputDir: config.outputDir,\n width: config.width ?? 1920,\n height: config.height ?? 1080,\n deviceScaleFactor: config.deviceScaleFactor ?? 1,\n format: config.format ?? 'png',\n quality: config.quality ?? 80,\n waitUntil: config.waitUntil ?? 'networkidle',\n timeoutMs: config.timeoutMs ?? 10000,\n template: config.template,\n assets: config.assets,\n };\n }\n\n /**\n * Capture a view by dot-path key and save to outputDir\n */\n async capture(\n name: string,\n viewKey: string,\n options?: CaptureOptions\n ): Promise<CaptureResult> {\n const { view, layout, wood } = options ?? {};\n const html = await this.staticRenderer.renderToPage({\n viewKey,\n data: { view, layout },\n wood,\n template: this.config.template,\n });\n\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Capture raw HTML and save to outputDir\n */\n async captureHtml(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n return this.captureHtmlInternal(name, html, options);\n }\n\n /**\n * Internal method to capture HTML to image\n */\n private async captureHtmlInternal(\n name: string,\n html: string,\n options?: CaptureHtmlOptions\n ): Promise<CaptureResult> {\n const width = options?.width ?? this.config.width;\n const height = options?.height ?? this.config.height;\n\n let context: any;\n let page: any;\n\n try {\n context = await this.browser.newContext({\n viewport: { width, height },\n deviceScaleFactor: this.config.deviceScaleFactor,\n });\n\n page = await context.newPage();\n\n // Route Interception Strategy:\n // We use page.goto() with a fake URL instead of page.setContent() because\n // setContent() has no base URL, so relative paths can't resolve.\n const FAKE_BASE_URL = 'http://stick-renderer/';\n\n await page.route(FAKE_BASE_URL, async (route: any) => {\n await route.fulfill({\n body: html,\n contentType: 'text/html',\n });\n });\n\n // Asset route interception\n if (this.config.assets) {\n for (const [urlPath, fsPaths] of Object.entries(this.config.assets)) {\n await page.route(`**${urlPath}/**`, async (route: any) => {\n const url = new URL(route.request().url());\n const relativePath = url.pathname.replace(urlPath, '');\n\n for (const fsPath of fsPaths) {\n const filePath = path.join(fsPath, relativePath);\n try {\n const body = await fs.promises.readFile(filePath);\n const contentType = getContentType(filePath);\n await route.fulfill({ body, contentType });\n return;\n } catch {\n // File not found in this path, try the next one\n }\n }\n\n await route.abort('filenotfound');\n });\n }\n }\n\n const waitUntilMapping = {\n 'load': 'load',\n 'domcontentloaded': 'domcontentloaded',\n 'networkidle': 'networkidle',\n } as const;\n\n try {\n await page.goto(FAKE_BASE_URL, {\n waitUntil: waitUntilMapping[this.config.waitUntil],\n timeout: this.config.timeoutMs,\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes('timeout')) {\n throw new RenderTimeoutError(this.config.timeoutMs);\n }\n throw new RenderError(\n 'Failed to render HTML content',\n error instanceof Error ? error : undefined\n );\n }\n\n if (options?.beforeCapture) {\n await options.beforeCapture(page);\n }\n\n const screenshotOptions: {\n type: 'png' | 'jpeg';\n quality?: number;\n fullPage: boolean;\n } = {\n type: this.config.format,\n fullPage: options?.fullPage ?? true,\n };\n\n if (this.config.format === 'jpeg') {\n screenshotOptions.quality = this.config.quality;\n }\n\n const screenshot = await page.screenshot(screenshotOptions);\n const buffer = Buffer.from(screenshot);\n\n const filename = `${name}@${width}x${height}.${this.config.format}`;\n const outputPath = path.join(this.config.outputDir, filename);\n\n await fs.promises.mkdir(this.config.outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, buffer);\n\n return { buffer, path: outputPath };\n\n } finally {\n if (page) {\n await page.close().catch(() => {});\n }\n if (context) {\n await context.close().catch(() => {});\n }\n }\n }\n\n /**\n * Close browser and clean up resources\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close().catch(() => {});\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────\n// Factory Function\n// ─────────────────────────────────────────────────────────────\n\nexport async function createImageRenderer(\n config: ImageRendererConfig\n): Promise<ImageRenderer> {\n const resolvedTemplate = resolveImageRendererTemplate({\n template: config.template,\n componentDir: config.componentDir,\n });\n const resolvedConfig: ImageRendererConfig = {\n ...config,\n template: resolvedTemplate,\n };\n\n const playwright = await loadPlaywright();\n\n let browser: any;\n try {\n browser = await playwright.chromium.launch({\n headless: true,\n });\n } catch (error) {\n throw new BrowserLaunchError(\n 'Failed to launch Chromium. Ensure it is installed with: npx playwright install chromium',\n error instanceof Error ? error : undefined\n );\n }\n\n let staticRenderer: WoodStaticRenderer;\n if (resolvedConfig.staticRenderer) {\n staticRenderer = resolvedConfig.staticRenderer;\n } else if (resolvedConfig.viewsConfig && resolvedConfig.componentDir) {\n const { createWoodStaticRenderer } = await import('./static_renderer.js');\n staticRenderer = createWoodStaticRenderer({\n viewsConfig: resolvedConfig.viewsConfig,\n componentDir: resolvedConfig.componentDir,\n });\n } else {\n throw new Error(\n 'ImageRendererConfig requires either staticRenderer or both viewsConfig and componentDir'\n );\n }\n\n return new ImageRenderer(browser, staticRenderer, resolvedConfig);\n}\n"],"mappings":"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","names":[]}
@@ -29,7 +29,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  var browser_exports = {};
30
30
  __export(browser_exports, {
31
31
  browser: () => browser,
32
- createBrowserBridgeFixture: () => createBrowserBridgeFixture
32
+ createBrowserBridgeFixture: () => createBrowserBridgeFixture,
33
+ startSharedBrowserViteServer: () => startSharedBrowserViteServer
33
34
  });
34
35
  module.exports = __toCommonJS(browser_exports);
35
36
  var import_node_fs = __toESM(require("node:fs"), 1);
@@ -49,6 +50,7 @@ const DEFAULT_RESOURCE_CLOSE_TIMEOUT_MS = 5e3;
49
50
  const WORKER_RUNTIME_IDLE_CLOSE_MS = 5e3;
50
51
  const browserWorkerRuntimes = /* @__PURE__ */ new Map();
51
52
  const browserWorkerCacheDirs = /* @__PURE__ */ new Set();
53
+ const browserConnectWarnings = /* @__PURE__ */ new Set();
52
54
  process.on("exit", () => {
53
55
  for (const cacheDir of browserWorkerCacheDirs) {
54
56
  try {
@@ -212,8 +214,9 @@ function buildBridgeInitScript(manifest, traceConfig, transportMode) {
212
214
  })();
213
215
  `;
214
216
  }
215
- async function loadPlaywright(browserName) {
216
- const playwright = await import("playwright");
217
+ async function loadPlaywright(browserName, rootDir) {
218
+ const playwrightPath = resolvePackageModule(rootDir, "playwright");
219
+ const playwright = playwrightPath ? await import((0, import_node_url.pathToFileURL)(playwrightPath).href) : await import("playwright");
217
220
  return playwright[browserName];
218
221
  }
219
222
  async function loadVite() {
@@ -314,7 +317,14 @@ async function createViteServer(rootDir, cacheDir) {
314
317
  resolve: {
315
318
  dedupe: ["svelte"]
316
319
  },
317
- server: { port: 0, fs: { allow: [rootDir, import_node_os.default.tmpdir()] } }
320
+ server: {
321
+ port: 0,
322
+ // Browser harnesses write independent HTML/entry files concurrently.
323
+ // HMR would broadcast another harness's temp-file changes to every page,
324
+ // causing unrelated mounts to reload; tests only need request-time transforms.
325
+ hmr: false,
326
+ fs: { allow: [rootDir, import_node_os.default.tmpdir()] }
327
+ }
318
328
  });
319
329
  await server.listen();
320
330
  return server;
@@ -323,21 +333,53 @@ function workerRuntimeKey(input) {
323
333
  return [
324
334
  input.rootDir,
325
335
  input.browserName,
326
- input.headless ? "headless" : "headed"
336
+ input.headless ? "headless" : "headed",
337
+ input.wsEndpoint ?? "local-browser",
338
+ input.viteUrl ?? "local-vite"
327
339
  ].join("::");
328
340
  }
329
341
  async function createBrowserWorkerRuntime(input) {
330
- const cacheRoot = import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "wood-browser-vite-"));
331
- browserWorkerCacheDirs.add(cacheRoot);
332
342
  let browser2 = null;
343
+ let cacheRoot = null;
344
+ let ownedServer = null;
333
345
  try {
334
- const playwrightBrowser = await loadPlaywright(input.browserName);
335
- browser2 = await playwrightBrowser.launch({ headless: input.headless });
336
- const server = await createViteServer(input.rootDir, import_node_path.default.join(cacheRoot, ".vite"));
346
+ const playwrightBrowser = await loadPlaywright(input.browserName, input.rootDir);
347
+ if (input.wsEndpoint) {
348
+ try {
349
+ browser2 = await playwrightBrowser.connect(input.wsEndpoint);
350
+ } catch (error) {
351
+ if (!browserConnectWarnings.has(input.wsEndpoint)) {
352
+ browserConnectWarnings.add(input.wsEndpoint);
353
+ console.warn(
354
+ `[wood browser] Could not connect to shared Chromium at "${input.wsEndpoint}"; falling back to a per-worker launch.`,
355
+ error
356
+ );
357
+ }
358
+ }
359
+ }
360
+ if (!browser2) {
361
+ browser2 = await playwrightBrowser.launch({ headless: input.headless });
362
+ }
363
+ let vite;
364
+ if (input.viteUrl) {
365
+ vite = {
366
+ baseUrl: input.viteUrl,
367
+ ownership: "external"
368
+ };
369
+ } else {
370
+ cacheRoot = import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "wood-browser-vite-"));
371
+ browserWorkerCacheDirs.add(cacheRoot);
372
+ ownedServer = await createViteServer(input.rootDir, import_node_path.default.join(cacheRoot, ".vite"));
373
+ vite = {
374
+ baseUrl: serverBaseUrl(ownedServer),
375
+ ownership: "worker",
376
+ server: ownedServer,
377
+ cacheDir: cacheRoot
378
+ };
379
+ }
337
380
  return {
338
381
  browser: browser2,
339
- server,
340
- cacheDir: cacheRoot,
382
+ vite,
341
383
  rootDir: input.rootDir,
342
384
  key: input.key,
343
385
  activeHarnesses: 0,
@@ -345,18 +387,25 @@ async function createBrowserWorkerRuntime(input) {
345
387
  idleCloseTimer: null
346
388
  };
347
389
  } catch (error) {
348
- await browser2?.close().catch(() => void 0);
349
- import_node_fs.default.rmSync(cacheRoot, { recursive: true, force: true });
350
- browserWorkerCacheDirs.delete(cacheRoot);
390
+ await Promise.all([
391
+ browser2?.close().catch(() => void 0),
392
+ ownedServer?.close().catch(() => void 0)
393
+ ]);
394
+ if (cacheRoot) {
395
+ import_node_fs.default.rmSync(cacheRoot, { recursive: true, force: true });
396
+ browserWorkerCacheDirs.delete(cacheRoot);
397
+ }
351
398
  throw error;
352
399
  }
353
400
  }
354
401
  async function acquireBrowserWorkerRuntime(input) {
355
- const key = workerRuntimeKey(input);
402
+ const wsEndpoint = input.browserName === "chromium" ? process.env.WOOD_BROWSER_WS_ENDPOINT : void 0;
403
+ const viteUrl = process.env.WOOD_BROWSER_VITE_URL;
404
+ const key = workerRuntimeKey({ ...input, wsEndpoint, viteUrl });
356
405
  let entry = browserWorkerRuntimes.get(key);
357
406
  if (!entry) {
358
407
  entry = {
359
- promise: createBrowserWorkerRuntime({ ...input, key })
408
+ promise: createBrowserWorkerRuntime({ ...input, wsEndpoint, viteUrl, key })
360
409
  };
361
410
  browserWorkerRuntimes.set(key, entry);
362
411
  }
@@ -379,12 +428,14 @@ async function closeBrowserWorkerRuntime(runtime) {
379
428
  runtime.closing = true;
380
429
  try {
381
430
  await Promise.all([
382
- waitForBoundedClose(runtime.server.close()),
431
+ ...runtime.vite.ownership === "worker" ? [waitForBoundedClose(runtime.vite.server.close())] : [],
383
432
  waitForBoundedClose(runtime.browser.close())
384
433
  ]);
385
434
  } finally {
386
- import_node_fs.default.rmSync(runtime.cacheDir, { recursive: true, force: true });
387
- browserWorkerCacheDirs.delete(runtime.cacheDir);
435
+ if (runtime.vite.ownership === "worker") {
436
+ import_node_fs.default.rmSync(runtime.vite.cacheDir, { recursive: true, force: true });
437
+ browserWorkerCacheDirs.delete(runtime.vite.cacheDir);
438
+ }
388
439
  const entry = browserWorkerRuntimes.get(runtime.key);
389
440
  const current = entry ? await entry.promise.catch(() => null) : null;
390
441
  if (current === runtime) {
@@ -411,6 +462,35 @@ function releaseBrowserWorkerRuntime(runtime) {
411
462
  function serverBaseUrl(server) {
412
463
  return server.resolvedUrls?.local?.[0] ?? "http://127.0.0.1:5173";
413
464
  }
465
+ async function startSharedBrowserViteServer(input) {
466
+ const rootDir = normalizeRoot(input.rootDir);
467
+ const cacheRoot = import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "wood-browser-vite-"));
468
+ browserWorkerCacheDirs.add(cacheRoot);
469
+ let server = null;
470
+ try {
471
+ server = await createViteServer(rootDir, import_node_path.default.join(cacheRoot, ".vite"));
472
+ } catch (error) {
473
+ import_node_fs.default.rmSync(cacheRoot, { recursive: true, force: true });
474
+ browserWorkerCacheDirs.delete(cacheRoot);
475
+ throw error;
476
+ }
477
+ let closed = false;
478
+ return {
479
+ url: serverBaseUrl(server),
480
+ async close() {
481
+ if (closed) {
482
+ return;
483
+ }
484
+ closed = true;
485
+ try {
486
+ await waitForBoundedClose(server.close());
487
+ } finally {
488
+ import_node_fs.default.rmSync(cacheRoot, { recursive: true, force: true });
489
+ browserWorkerCacheDirs.delete(cacheRoot);
490
+ }
491
+ }
492
+ };
493
+ }
414
494
  function assertFileExists(filePath, label) {
415
495
  if (!import_node_fs.default.existsSync(filePath)) {
416
496
  throw new Error(`${label} does not exist: ${filePath}`);
@@ -1326,7 +1406,7 @@ async function openMountPage(input) {
1326
1406
  ...input.context
1327
1407
  };
1328
1408
  await input.hooks?.beforeMount?.(context);
1329
- await page.goto(`${serverBaseUrl(input.runtime.server)}${viteImport(htmlPath)}`, {
1409
+ await page.goto(`${input.runtime.baseUrl}${viteImport(htmlPath)}`, {
1330
1410
  waitUntil: "domcontentloaded"
1331
1411
  });
1332
1412
  for (const event of input.bridge.state.initialEvents) {
@@ -1390,7 +1470,7 @@ async function createHarness(config = {}) {
1390
1470
  }
1391
1471
  const runtime = {
1392
1472
  context,
1393
- server: workerRuntime.server,
1473
+ baseUrl: workerRuntime.vite.baseUrl,
1394
1474
  page,
1395
1475
  tempDir,
1396
1476
  rootDir,
@@ -1560,6 +1640,7 @@ const browser = {
1560
1640
  // Annotate the CommonJS export names for ESM import in node:
1561
1641
  0 && (module.exports = {
1562
1642
  browser,
1563
- createBrowserBridgeFixture
1643
+ createBrowserBridgeFixture,
1644
+ startSharedBrowserViteServer
1564
1645
  });
1565
1646
  //# sourceMappingURL=browser.cjs.map