@noego/wood 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -42
- package/bin/axe.js +313 -98
- package/dist/{loader/__fixtures__/layouts/app.load.cjs → automation/actions.cjs} +3 -13
- package/dist/automation/actions.cjs.map +1 -0
- package/dist/automation/actions.d.cts +57 -0
- package/dist/automation/actions.d.ts +57 -0
- package/dist/automation/actions.js +1 -0
- package/dist/automation/actions.js.map +1 -0
- package/dist/automation/actions_executor.cjs +101 -0
- package/dist/automation/actions_executor.cjs.map +1 -0
- package/dist/automation/actions_executor.d.cts +16 -0
- package/dist/automation/actions_executor.d.ts +16 -0
- package/dist/automation/actions_executor.js +76 -0
- package/dist/automation/actions_executor.js.map +1 -0
- package/dist/automation/connection.cjs +86 -0
- package/dist/automation/connection.cjs.map +1 -0
- package/dist/automation/connection.d.cts +18 -0
- package/dist/automation/connection.d.ts +18 -0
- package/dist/automation/connection.js +51 -0
- package/dist/automation/connection.js.map +1 -0
- package/dist/{middleware/__fixtures__/middleware/sequence.cjs → automation/errors.cjs} +25 -22
- package/dist/automation/errors.cjs.map +1 -0
- package/dist/automation/errors.d.cts +31 -0
- package/dist/automation/errors.d.ts +31 -0
- package/dist/automation/errors.js +24 -0
- package/dist/automation/errors.js.map +1 -0
- package/dist/automation/index.cjs +49 -0
- package/dist/automation/index.cjs.map +1 -0
- package/dist/automation/index.d.cts +16 -0
- package/dist/automation/index.d.ts +16 -0
- package/dist/automation/index.js +15 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/introspection_reader.cjs +75 -0
- package/dist/automation/introspection_reader.cjs.map +1 -0
- package/dist/automation/introspection_reader.d.cts +20 -0
- package/dist/automation/introspection_reader.d.ts +20 -0
- package/dist/automation/introspection_reader.js +48 -0
- package/dist/automation/introspection_reader.js.map +1 -0
- package/dist/automation/locator.cjs +102 -0
- package/dist/automation/locator.cjs.map +1 -0
- package/dist/automation/locator.d.cts +51 -0
- package/dist/automation/locator.d.ts +51 -0
- package/dist/automation/locator.js +75 -0
- package/dist/automation/locator.js.map +1 -0
- package/dist/{loader/__fixtures__/views/test/page.load.cjs → automation/observation.cjs} +3 -13
- package/dist/automation/observation.cjs.map +1 -0
- package/dist/automation/observation.d.cts +44 -0
- package/dist/automation/observation.d.ts +44 -0
- package/dist/automation/observation.js +1 -0
- package/dist/automation/observation.js.map +1 -0
- package/dist/{controller/__fixtures__/controllers/test.cjs → automation/receipts.cjs} +3 -15
- package/dist/automation/receipts.cjs.map +1 -0
- package/dist/automation/receipts.d.cts +27 -0
- package/dist/automation/receipts.d.ts +27 -0
- package/dist/automation/receipts.js +1 -0
- package/dist/automation/receipts.js.map +1 -0
- package/dist/automation/scenario.cjs +696 -0
- package/dist/automation/scenario.cjs.map +1 -0
- package/dist/automation/scenario.d.cts +89 -0
- package/dist/automation/scenario.d.ts +89 -0
- package/dist/automation/scenario.js +660 -0
- package/dist/automation/scenario.js.map +1 -0
- package/dist/automation/scrolling.cjs +17 -0
- package/dist/automation/scrolling.cjs.map +1 -0
- package/dist/automation/scrolling.d.cts +51 -0
- package/dist/automation/scrolling.d.ts +51 -0
- package/dist/automation/scrolling.js +1 -0
- package/dist/automation/scrolling.js.map +1 -0
- package/dist/automation/scrolling_executor.cjs +109 -0
- package/dist/automation/scrolling_executor.cjs.map +1 -0
- package/dist/automation/scrolling_executor.d.cts +13 -0
- package/dist/automation/scrolling_executor.d.ts +13 -0
- package/dist/automation/scrolling_executor.js +84 -0
- package/dist/automation/scrolling_executor.js.map +1 -0
- package/dist/automation/target_identity.cjs +17 -0
- package/dist/automation/target_identity.cjs.map +1 -0
- package/dist/automation/target_identity.d.cts +28 -0
- package/dist/automation/target_identity.d.ts +28 -0
- package/dist/automation/target_identity.js +1 -0
- package/dist/automation/target_identity.js.map +1 -0
- package/dist/automation/target_registry.cjs +148 -0
- package/dist/automation/target_registry.cjs.map +1 -0
- package/dist/automation/target_registry.d.cts +32 -0
- package/dist/automation/target_registry.d.ts +32 -0
- package/dist/automation/target_registry.js +122 -0
- package/dist/automation/target_registry.js.map +1 -0
- package/dist/automation/waits.cjs +17 -0
- package/dist/automation/waits.cjs.map +1 -0
- package/dist/automation/waits.d.cts +44 -0
- package/dist/automation/waits.d.ts +44 -0
- package/dist/automation/waits.js +1 -0
- package/dist/automation/waits.js.map +1 -0
- package/dist/automation/waits_executor.cjs +104 -0
- package/dist/automation/waits_executor.cjs.map +1 -0
- package/dist/automation/waits_executor.d.cts +14 -0
- package/dist/automation/waits_executor.d.ts +14 -0
- package/dist/automation/waits_executor.js +79 -0
- package/dist/automation/waits_executor.js.map +1 -0
- package/dist/bridge/backend_bridge.cjs.map +1 -1
- package/dist/bridge/backend_bridge.js.map +1 -1
- package/dist/client/index.cjs +78 -21
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +19 -3
- package/dist/client/index.d.ts +19 -3
- package/dist/client/index.js +80 -22
- package/dist/client/index.js.map +1 -1
- package/dist/client/renderer_log_transport.cjs +151 -0
- package/dist/client/renderer_log_transport.cjs.map +1 -0
- package/dist/client/renderer_log_transport.d.cts +54 -0
- package/dist/client/renderer_log_transport.d.ts +54 -0
- package/dist/client/renderer_log_transport.js +123 -0
- package/dist/client/renderer_log_transport.js.map +1 -0
- package/dist/codegen/controller_registration_generator.cjs +13 -2
- package/dist/codegen/controller_registration_generator.cjs.map +1 -1
- package/dist/codegen/controller_registration_generator.d.cts +12 -1
- package/dist/codegen/controller_registration_generator.d.ts +12 -1
- package/dist/codegen/controller_registration_generator.js +10 -1
- package/dist/codegen/controller_registration_generator.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -0
- package/dist/codegen/index.d.ts +1 -0
- package/dist/codegen/preload_generator.cjs +13 -6
- package/dist/codegen/preload_generator.cjs.map +1 -1
- package/dist/codegen/preload_generator.d.cts +11 -2
- package/dist/codegen/preload_generator.d.ts +11 -2
- package/dist/codegen/preload_generator.js +13 -6
- package/dist/codegen/preload_generator.js.map +1 -1
- package/dist/codegen/renderer_app_generator.cjs +95 -44
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +95 -44
- package/dist/codegen/renderer_app_generator.js.map +1 -1
- package/dist/codegen/type_generator.cjs +1 -0
- package/dist/codegen/type_generator.cjs.map +1 -1
- package/dist/codegen/type_generator.js +1 -0
- package/dist/codegen/type_generator.js.map +1 -1
- package/dist/config/config_parser.cjs +95 -4
- package/dist/config/config_parser.cjs.map +1 -1
- package/dist/config/config_parser.d.cts +10 -2
- package/dist/config/config_parser.d.ts +10 -2
- package/dist/config/config_parser.js +92 -3
- package/dist/config/config_parser.js.map +1 -1
- package/dist/config/index.cjs +6 -2
- package/dist/config/index.cjs.map +1 -1
- package/dist/config/index.d.cts +2 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +8 -2
- package/dist/config/index.js.map +1 -1
- package/dist/controller/controller_resolver.cjs +51 -3
- package/dist/controller/controller_resolver.cjs.map +1 -1
- package/dist/controller/controller_resolver.d.cts +27 -4
- package/dist/controller/controller_resolver.d.ts +27 -4
- package/dist/controller/controller_resolver.js +48 -2
- package/dist/controller/controller_resolver.js.map +1 -1
- package/dist/controller/index.cjs +6 -2
- package/dist/controller/index.cjs.map +1 -1
- package/dist/controller/index.d.cts +1 -1
- package/dist/controller/index.d.ts +1 -1
- package/dist/controller/index.js +4 -2
- package/dist/controller/index.js.map +1 -1
- package/dist/dev/wood_dev_server.cjs +4 -1
- package/dist/dev/wood_dev_server.cjs.map +1 -1
- package/dist/dev/wood_dev_server.js +5 -2
- package/dist/dev/wood_dev_server.js.map +1 -1
- package/dist/dev/yaml_watcher.cjs +1 -1
- package/dist/dev/yaml_watcher.cjs.map +1 -1
- package/dist/dev/yaml_watcher.d.cts +4 -0
- package/dist/dev/yaml_watcher.d.ts +4 -0
- package/dist/dev/yaml_watcher.js +1 -1
- package/dist/dev/yaml_watcher.js.map +1 -1
- package/dist/frontend/frontend_lifecycle.cjs +74 -0
- package/dist/frontend/frontend_lifecycle.cjs.map +1 -0
- package/dist/frontend/frontend_lifecycle.d.cts +50 -0
- package/dist/frontend/frontend_lifecycle.d.ts +50 -0
- package/dist/frontend/frontend_lifecycle.js +50 -0
- package/dist/frontend/frontend_lifecycle.js.map +1 -0
- package/dist/frontend/frontend_page.cjs +122 -0
- package/dist/frontend/frontend_page.cjs.map +1 -0
- package/dist/frontend/frontend_page.d.cts +62 -0
- package/dist/frontend/frontend_page.d.ts +62 -0
- package/dist/frontend/frontend_page.js +98 -0
- package/dist/frontend/frontend_page.js.map +1 -0
- package/dist/frontend/frontend_surface.cjs +249 -0
- package/dist/frontend/frontend_surface.cjs.map +1 -0
- package/dist/frontend/frontend_surface.d.cts +64 -0
- package/dist/frontend/frontend_surface.d.ts +64 -0
- package/dist/frontend/frontend_surface.js +224 -0
- package/dist/frontend/frontend_surface.js.map +1 -0
- package/dist/frontend/index.cjs +52 -0
- package/dist/frontend/index.cjs.map +1 -0
- package/dist/frontend/index.d.cts +10 -0
- package/dist/frontend/index.d.ts +10 -0
- package/dist/frontend/index.js +19 -0
- package/dist/frontend/index.js.map +1 -0
- package/dist/frontend/page_catalog.cjs +135 -0
- package/dist/frontend/page_catalog.cjs.map +1 -0
- package/dist/frontend/page_catalog.d.cts +56 -0
- package/dist/frontend/page_catalog.d.ts +56 -0
- package/dist/frontend/page_catalog.js +108 -0
- package/dist/frontend/page_catalog.js.map +1 -0
- package/dist/frontend/tracked_transport.cjs +55 -0
- package/dist/frontend/tracked_transport.cjs.map +1 -0
- package/dist/frontend/tracked_transport.d.cts +36 -0
- package/dist/frontend/tracked_transport.d.ts +36 -0
- package/dist/frontend/tracked_transport.js +31 -0
- package/dist/frontend/tracked_transport.js.map +1 -0
- package/dist/frontend/work_registry.cjs +153 -0
- package/dist/frontend/work_registry.cjs.map +1 -0
- package/dist/frontend/work_registry.d.cts +70 -0
- package/dist/frontend/work_registry.d.ts +70 -0
- package/dist/frontend/work_registry.js +128 -0
- package/dist/frontend/work_registry.js.map +1 -0
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -7
- package/dist/index.d.ts +11 -7
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/main/index.cjs +10 -0
- package/dist/main/index.cjs.map +1 -1
- package/dist/main/index.d.cts +14 -2
- package/dist/main/index.d.ts +14 -2
- package/dist/main/index.js +16 -0
- package/dist/main/index.js.map +1 -1
- package/dist/middleware/middleware_resolver.cjs +22 -1
- package/dist/middleware/middleware_resolver.cjs.map +1 -1
- package/dist/middleware/middleware_resolver.d.cts +13 -1
- package/dist/middleware/middleware_resolver.d.ts +13 -1
- package/dist/middleware/middleware_resolver.js +22 -1
- package/dist/middleware/middleware_resolver.js.map +1 -1
- package/dist/navigation/automation_introspection.cjs +110 -0
- package/dist/navigation/automation_introspection.cjs.map +1 -0
- package/dist/navigation/automation_introspection.d.cts +60 -0
- package/dist/navigation/automation_introspection.d.ts +60 -0
- package/dist/navigation/automation_introspection.js +85 -0
- package/dist/navigation/automation_introspection.js.map +1 -0
- package/dist/navigation/index.cjs +10 -2
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +27 -1
- package/dist/navigation/index.d.ts +27 -1
- package/dist/navigation/index.js +6 -1
- package/dist/navigation/index.js.map +1 -1
- package/dist/{middleware/__fixtures__/middleware/auth.cjs → navigation/layout_preservation.cjs} +18 -20
- package/dist/navigation/layout_preservation.cjs.map +1 -0
- package/dist/navigation/layout_preservation.d.cts +2 -0
- package/dist/navigation/layout_preservation.d.ts +2 -0
- package/dist/navigation/layout_preservation.js +18 -0
- package/dist/navigation/layout_preservation.js.map +1 -0
- package/dist/parser/index.cjs +2 -0
- package/dist/parser/index.cjs.map +1 -1
- package/dist/parser/index.d.cts +1 -1
- package/dist/parser/index.d.ts +1 -1
- package/dist/parser/index.js +2 -1
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/operations_parser.cjs +6 -0
- package/dist/parser/operations_parser.cjs.map +1 -1
- package/dist/parser/operations_parser.d.cts +9 -2
- package/dist/parser/operations_parser.d.ts +9 -2
- package/dist/parser/operations_parser.js +5 -0
- package/dist/parser/operations_parser.js.map +1 -1
- package/dist/parser/views_parser.cjs.map +1 -1
- package/dist/parser/views_parser.js.map +1 -1
- package/dist/router/index.d.cts +1 -0
- package/dist/router/index.d.ts +1 -0
- package/dist/router/wood_router.cjs +35 -3
- package/dist/router/wood_router.cjs.map +1 -1
- package/dist/router/wood_router.d.cts +15 -0
- package/dist/router/wood_router.d.ts +15 -0
- package/dist/router/wood_router.js +36 -4
- package/dist/router/wood_router.js.map +1 -1
- package/dist/runtime/get_container.cjs +9 -34
- package/dist/runtime/get_container.cjs.map +1 -1
- package/dist/runtime/get_container.d.cts +24 -4
- package/dist/runtime/get_container.d.ts +24 -4
- package/dist/runtime/get_container.js +8 -23
- package/dist/runtime/get_container.js.map +1 -1
- package/dist/runtime/index.cjs +9 -0
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.cts +5 -2
- package/dist/runtime/index.d.ts +5 -2
- package/dist/runtime/index.js +10 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/renderer_config_extension.cjs +50 -0
- package/dist/runtime/renderer_config_extension.cjs.map +1 -0
- package/dist/runtime/renderer_config_extension.d.cts +19 -0
- package/dist/runtime/renderer_config_extension.d.ts +19 -0
- package/dist/runtime/renderer_config_extension.js +24 -0
- package/dist/runtime/renderer_config_extension.js.map +1 -0
- package/dist/runtime/renderer_log_batch_receiver.cjs +73 -0
- package/dist/runtime/renderer_log_batch_receiver.cjs.map +1 -0
- package/dist/runtime/renderer_log_batch_receiver.d.cts +29 -0
- package/dist/runtime/renderer_log_batch_receiver.d.ts +29 -0
- package/dist/runtime/renderer_log_batch_receiver.js +48 -0
- package/dist/runtime/renderer_log_batch_receiver.js.map +1 -0
- package/dist/runtime/renderer_trace_batch_receiver_registry.d.cts +1 -1
- package/dist/runtime/renderer_trace_batch_receiver_registry.d.ts +1 -1
- package/dist/runtime/runtime.cjs +14 -0
- package/dist/runtime/runtime.cjs.map +1 -1
- package/dist/runtime/runtime.d.cts +2 -1
- package/dist/runtime/runtime.d.ts +2 -1
- package/dist/runtime/runtime.js +14 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/stick/index.cjs.map +1 -1
- package/dist/stick/index.js.map +1 -1
- package/dist/testing/automation_assertions.cjs +417 -0
- package/dist/testing/automation_assertions.cjs.map +1 -0
- package/dist/testing/automation_assertions.d.cts +44 -0
- package/dist/testing/automation_assertions.d.ts +44 -0
- package/dist/testing/automation_assertions.js +385 -0
- package/dist/testing/automation_assertions.js.map +1 -0
- package/dist/testing/browser.cjs +130 -57
- package/dist/testing/browser.cjs.map +1 -1
- package/dist/testing/browser.d.cts +10 -4
- package/dist/testing/browser.d.ts +10 -4
- package/dist/testing/browser.js +132 -53
- package/dist/testing/browser.js.map +1 -1
- package/dist/testing/create_controller_harness.cjs +28 -8
- package/dist/testing/create_controller_harness.cjs.map +1 -1
- package/dist/testing/create_controller_harness.d.cts +18 -2
- package/dist/testing/create_controller_harness.d.ts +18 -2
- package/dist/testing/create_controller_harness.js +28 -8
- package/dist/testing/create_controller_harness.js.map +1 -1
- package/dist/testing/create_router_bridge.d.cts +1 -0
- package/dist/testing/create_router_bridge.d.ts +1 -0
- package/dist/testing/create_test_router.d.cts +1 -0
- package/dist/testing/create_test_router.d.ts +1 -0
- package/dist/testing/geometry.cjs +109 -0
- package/dist/testing/geometry.cjs.map +1 -0
- package/dist/testing/geometry.d.cts +45 -0
- package/dist/testing/geometry.d.ts +45 -0
- package/dist/testing/geometry.js +79 -0
- package/dist/testing/geometry.js.map +1 -0
- package/dist/testing/index.cjs +62 -2
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +23 -4
- package/dist/testing/index.d.ts +23 -4
- package/dist/testing/index.js +50 -2
- package/dist/testing/index.js.map +1 -1
- package/dist/{controller/__fixtures__/controllers/not_a_class.cjs → testing/test_wood.cjs} +15 -7
- package/dist/testing/test_wood.cjs.map +1 -0
- package/dist/testing/test_wood.d.cts +16 -0
- package/dist/testing/test_wood.d.ts +16 -0
- package/dist/testing/test_wood.js +12 -0
- package/dist/testing/test_wood.js.map +1 -0
- package/dist/testing/test_wood_config.cjs +272 -0
- package/dist/testing/test_wood_config.cjs.map +1 -0
- package/dist/testing/test_wood_config.d.cts +111 -0
- package/dist/testing/test_wood_config.d.ts +111 -0
- package/dist/testing/test_wood_config.js +247 -0
- package/dist/testing/test_wood_config.js.map +1 -0
- package/dist/testing/wood_config_source.cjs +146 -0
- package/dist/testing/wood_config_source.cjs.map +1 -0
- package/dist/testing/wood_config_source.d.cts +36 -0
- package/dist/testing/wood_config_source.d.ts +36 -0
- package/dist/testing/wood_config_source.js +112 -0
- package/dist/testing/wood_config_source.js.map +1 -0
- package/dist/testing/wood_env_renderer.cjs +210 -0
- package/dist/testing/wood_env_renderer.cjs.map +1 -0
- package/dist/testing/wood_env_renderer.d.cts +74 -0
- package/dist/testing/wood_env_renderer.d.ts +74 -0
- package/dist/testing/wood_env_renderer.js +188 -0
- package/dist/testing/wood_env_renderer.js.map +1 -0
- package/dist/testing/wood_test_selection.cjs +94 -0
- package/dist/testing/wood_test_selection.cjs.map +1 -0
- package/dist/testing/wood_test_selection.d.cts +39 -0
- package/dist/testing/wood_test_selection.d.ts +39 -0
- package/dist/testing/wood_test_selection.js +68 -0
- package/dist/testing/wood_test_selection.js.map +1 -0
- package/dist/trace-testing/index.d.cts +1 -2
- package/dist/trace-testing/index.d.ts +1 -2
- package/dist/trace-testing/trace_recorder.cjs +1 -1
- package/dist/trace-testing/trace_recorder.cjs.map +1 -1
- package/dist/trace-testing/trace_recorder.d.cts +1 -2
- package/dist/trace-testing/trace_recorder.d.ts +1 -2
- package/dist/trace-testing/trace_recorder.js +1 -1
- package/dist/trace-testing/trace_recorder.js.map +1 -1
- package/dist/trace-transport/index.d.cts +1 -1
- package/dist/trace-transport/index.d.ts +1 -1
- package/dist/{trace_hub-Cob54qRx.d.cts → trace_hub-Cpo_nVze.d.cts} +21 -1
- package/dist/{trace_hub-Cob54qRx.d.ts → trace_hub-Cpo_nVze.d.ts} +21 -1
- package/dist/tracing/index.cjs +9 -2
- package/dist/tracing/index.cjs.map +1 -1
- package/dist/tracing/index.d.cts +3 -2
- package/dist/tracing/index.d.ts +3 -2
- package/dist/tracing/index.js +7 -1
- package/dist/tracing/index.js.map +1 -1
- package/dist/tracing/renderer_trace_batch_host.d.cts +1 -1
- package/dist/tracing/renderer_trace_batch_host.d.ts +1 -1
- package/dist/tracing/renderer_trace_batch_transport.cjs +48 -6
- package/dist/tracing/renderer_trace_batch_transport.cjs.map +1 -1
- package/dist/tracing/renderer_trace_batch_transport.d.cts +17 -1
- package/dist/tracing/renderer_trace_batch_transport.d.ts +17 -1
- package/dist/tracing/renderer_trace_batch_transport.js +48 -6
- package/dist/tracing/renderer_trace_batch_transport.js.map +1 -1
- package/dist/tracing/trace_batch_adapter.cjs +110 -0
- package/dist/tracing/trace_batch_adapter.cjs.map +1 -0
- package/dist/tracing/trace_batch_adapter.d.cts +36 -0
- package/dist/tracing/trace_batch_adapter.d.ts +36 -0
- package/dist/tracing/trace_batch_adapter.js +86 -0
- package/dist/tracing/trace_batch_adapter.js.map +1 -0
- package/dist/tracing/trace_commit_writer.d.cts +1 -1
- package/dist/tracing/trace_commit_writer.d.ts +1 -1
- package/dist/tracing/trace_hub.cjs +87 -20
- package/dist/tracing/trace_hub.cjs.map +1 -1
- package/dist/tracing/trace_hub.d.cts +1 -1
- package/dist/tracing/trace_hub.d.ts +1 -1
- package/dist/tracing/trace_hub.js +85 -20
- package/dist/tracing/trace_hub.js.map +1 -1
- package/dist/tracing/trace_provider.cjs +21 -6
- package/dist/tracing/trace_provider.cjs.map +1 -1
- package/dist/tracing/trace_provider.d.cts +13 -4
- package/dist/tracing/trace_provider.d.ts +13 -4
- package/dist/tracing/trace_provider.js +22 -7
- package/dist/tracing/trace_provider.js.map +1 -1
- package/dist/tracing/types/wood_trace_batch.types.cjs.map +1 -1
- package/dist/tracing/types/wood_trace_batch.types.d.cts +15 -1
- package/dist/tracing/types/wood_trace_batch.types.d.ts +15 -1
- package/dist/tracing/types/wood_trace_sink.types.d.cts +1 -1
- package/dist/tracing/types/wood_trace_sink.types.d.ts +1 -1
- package/dist/tracing/wood_trace_batch_contract.cjs +2 -0
- package/dist/tracing/wood_trace_batch_contract.cjs.map +1 -1
- package/dist/tracing/wood_trace_batch_contract.d.cts +1 -1
- package/dist/tracing/wood_trace_batch_contract.d.ts +1 -1
- package/dist/tracing/wood_trace_batch_contract.js +2 -0
- package/dist/tracing/wood_trace_batch_contract.js.map +1 -1
- package/dist/transport/build_transport_bridge.cjs +4 -2
- package/dist/transport/build_transport_bridge.cjs.map +1 -1
- package/dist/transport/build_transport_bridge.d.cts +7 -1
- package/dist/transport/build_transport_bridge.d.ts +7 -1
- package/dist/transport/build_transport_bridge.js +2 -1
- package/dist/transport/build_transport_bridge.js.map +1 -1
- package/dist/transport/in_process_router_transport.d.cts +1 -0
- package/dist/transport/in_process_router_transport.d.ts +1 -0
- package/dist/transport/index.d.cts +1 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/types/config.cjs.map +1 -1
- package/dist/types/config.d.cts +47 -1
- package/dist/types/config.d.ts +47 -1
- package/dist/types/context.cjs +12 -0
- package/dist/types/context.cjs.map +1 -1
- package/dist/types/context.d.cts +27 -1
- package/dist/types/context.d.ts +27 -1
- package/dist/types/context.js +4 -0
- package/dist/types/context.js.map +1 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/operations.cjs.map +1 -1
- package/dist/types/operations.d.cts +2 -0
- package/dist/types/operations.d.ts +2 -0
- package/docs/axe.md +706 -0
- package/package.json +19 -5
- package/src/components/NavigationShell.scopes.browser.test.ts +363 -0
- package/src/components/NavigationShell.svelte +126 -49
- package/dist/controller/__fixtures__/controllers/chat.cjs +0 -32
- package/dist/controller/__fixtures__/controllers/chat.cjs.map +0 -1
- package/dist/controller/__fixtures__/controllers/chat.d.cts +0 -11
- package/dist/controller/__fixtures__/controllers/chat.d.ts +0 -11
- package/dist/controller/__fixtures__/controllers/chat.js +0 -12
- package/dist/controller/__fixtures__/controllers/chat.js.map +0 -1
- package/dist/controller/__fixtures__/controllers/not_a_class.cjs.map +0 -1
- package/dist/controller/__fixtures__/controllers/not_a_class.d.cts +0 -3
- package/dist/controller/__fixtures__/controllers/not_a_class.d.ts +0 -3
- package/dist/controller/__fixtures__/controllers/not_a_class.js +0 -5
- package/dist/controller/__fixtures__/controllers/not_a_class.js.map +0 -1
- package/dist/controller/__fixtures__/controllers/settings.controller.cjs +0 -32
- package/dist/controller/__fixtures__/controllers/settings.controller.cjs.map +0 -1
- package/dist/controller/__fixtures__/controllers/settings.controller.d.cts +0 -12
- package/dist/controller/__fixtures__/controllers/settings.controller.d.ts +0 -12
- package/dist/controller/__fixtures__/controllers/settings.controller.js +0 -12
- package/dist/controller/__fixtures__/controllers/settings.controller.js.map +0 -1
- package/dist/controller/__fixtures__/controllers/test.cjs.map +0 -1
- package/dist/controller/__fixtures__/controllers/test.d.cts +0 -7
- package/dist/controller/__fixtures__/controllers/test.d.ts +0 -7
- package/dist/controller/__fixtures__/controllers/test.js +0 -9
- package/dist/controller/__fixtures__/controllers/test.js.map +0 -1
- package/dist/loader/__fixtures__/layouts/app.load.cjs.map +0 -1
- package/dist/loader/__fixtures__/layouts/app.load.d.cts +0 -5
- package/dist/loader/__fixtures__/layouts/app.load.d.ts +0 -5
- package/dist/loader/__fixtures__/layouts/app.load.js +0 -7
- package/dist/loader/__fixtures__/layouts/app.load.js.map +0 -1
- package/dist/loader/__fixtures__/views/test/page.load.cjs.map +0 -1
- package/dist/loader/__fixtures__/views/test/page.load.d.cts +0 -6
- package/dist/loader/__fixtures__/views/test/page.load.d.ts +0 -6
- package/dist/loader/__fixtures__/views/test/page.load.js +0 -7
- package/dist/loader/__fixtures__/views/test/page.load.js.map +0 -1
- package/dist/middleware/__fixtures__/middleware/auth.cjs.map +0 -1
- package/dist/middleware/__fixtures__/middleware/auth.d.cts +0 -6
- package/dist/middleware/__fixtures__/middleware/auth.d.ts +0 -6
- package/dist/middleware/__fixtures__/middleware/auth.js +0 -19
- package/dist/middleware/__fixtures__/middleware/auth.js.map +0 -1
- package/dist/middleware/__fixtures__/middleware/sequence.cjs.map +0 -1
- package/dist/middleware/__fixtures__/middleware/sequence.d.cts +0 -7
- package/dist/middleware/__fixtures__/middleware/sequence.d.ts +0 -7
- package/dist/middleware/__fixtures__/middleware/sequence.js +0 -21
- package/dist/middleware/__fixtures__/middleware/sequence.js.map +0 -1
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { ExecutionContext } from "@noego/ioc";
|
|
3
|
+
import { testIoc, ENV_REGISTRY } from "@noego/testing";
|
|
4
|
+
import { WoodRouter } from "../router/wood_router.js";
|
|
5
|
+
import { TestWoodError } from "./test_wood.js";
|
|
6
|
+
import {
|
|
7
|
+
defaultImporter,
|
|
8
|
+
importDefaultExport,
|
|
9
|
+
loadWoodConfigSource
|
|
10
|
+
} from "./wood_config_source.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveSelection,
|
|
13
|
+
selectCatalog,
|
|
14
|
+
selectOperations
|
|
15
|
+
} from "./wood_test_selection.js";
|
|
16
|
+
import {
|
|
17
|
+
buildRendererTier
|
|
18
|
+
} from "./wood_env_renderer.js";
|
|
19
|
+
const CONTAINER_REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for("@noego/wood.container.registry");
|
|
20
|
+
function globalRegistrySize() {
|
|
21
|
+
const registry = globalThis[CONTAINER_REGISTRY_SYMBOL];
|
|
22
|
+
return registry instanceof Map ? registry.size : 0;
|
|
23
|
+
}
|
|
24
|
+
function tierMissing(tier, member) {
|
|
25
|
+
throw new TestWoodError(
|
|
26
|
+
`the ${member} is not available: this environment was built without { ${tier} } \u2014 add it to select()`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
function buildRouter(root, source, operations, importer) {
|
|
30
|
+
const router = new WoodRouter({
|
|
31
|
+
container: root,
|
|
32
|
+
controllersDir: source.mainControllersDir,
|
|
33
|
+
middlewareDir: source.mainMiddlewareDir,
|
|
34
|
+
mode: "dev"
|
|
35
|
+
});
|
|
36
|
+
const bound = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const operation of operations.values()) {
|
|
38
|
+
if (bound.has(operation.controller)) continue;
|
|
39
|
+
bound.add(operation.controller);
|
|
40
|
+
const file = source.mainControllerFile(operation.controller);
|
|
41
|
+
router.registerControllerLoader(operation.controller, () => importer(file));
|
|
42
|
+
}
|
|
43
|
+
router.loadOperations(operations);
|
|
44
|
+
router.validateControllers();
|
|
45
|
+
return router;
|
|
46
|
+
}
|
|
47
|
+
async function runCompose(root, source, context) {
|
|
48
|
+
const composePath = source.hookModule(source.config.main.testing?.compose);
|
|
49
|
+
if (!composePath) return null;
|
|
50
|
+
const compose = await importDefaultExport(context.importer, composePath, "main.testing.compose");
|
|
51
|
+
if (typeof compose !== "function") {
|
|
52
|
+
throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);
|
|
53
|
+
}
|
|
54
|
+
return await ExecutionContext.run(root, () => compose(root, context)) ?? null;
|
|
55
|
+
}
|
|
56
|
+
async function disposeRoot(env) {
|
|
57
|
+
try {
|
|
58
|
+
await ExecutionContext.run(env.root, () => env.dispose());
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const failures = error.failures;
|
|
61
|
+
if (!Array.isArray(failures)) throw error;
|
|
62
|
+
const detail = failures.map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`).join("\n ");
|
|
63
|
+
throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):
|
|
64
|
+
${detail}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function assertNoGlobalContainerLeak(before, source) {
|
|
68
|
+
if (!process.env.VITEST) return;
|
|
69
|
+
const after = globalRegistrySize();
|
|
70
|
+
if (after > before) {
|
|
71
|
+
throw new TestWoodError(
|
|
72
|
+
`${source.config.main.testing?.compose ?? "the build"} registered ${after - before} process-global Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function assembleEnvironment(state, selection) {
|
|
77
|
+
const source = loadWoodConfigSource(state.configPath);
|
|
78
|
+
const operations = selectOperations(source.operations, selection.main);
|
|
79
|
+
const catalog = selection.pages ? selectCatalog(source.views(), selection.pages) : null;
|
|
80
|
+
const registryBefore = globalRegistrySize();
|
|
81
|
+
let composed = null;
|
|
82
|
+
let renderer = null;
|
|
83
|
+
const controllerScopes = [];
|
|
84
|
+
let router = null;
|
|
85
|
+
const context = {
|
|
86
|
+
configPath: source.configPath,
|
|
87
|
+
configDir: source.configDir,
|
|
88
|
+
config: source.config,
|
|
89
|
+
selection,
|
|
90
|
+
operations,
|
|
91
|
+
importer: state.importer,
|
|
92
|
+
call: async (operation, input) => {
|
|
93
|
+
if (!router) throw new TestWoodError("compose context call() is available from start() onward, not during compose");
|
|
94
|
+
const live = router;
|
|
95
|
+
return await ExecutionContext.run(env.root, () => live.dispatch(operation, input));
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const env = await state.ioc.build({
|
|
99
|
+
prepare: async (root2) => {
|
|
100
|
+
composed = await runCompose(root2, source, context);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const root = env.root;
|
|
104
|
+
const dispose = async () => {
|
|
105
|
+
if (renderer) await renderer.dispose();
|
|
106
|
+
for (const scope of controllerScopes.reverse()) await scope.dispose();
|
|
107
|
+
await composed?.drain?.();
|
|
108
|
+
try {
|
|
109
|
+
await disposeRoot(env);
|
|
110
|
+
} finally {
|
|
111
|
+
await composed?.dispose?.();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
router = buildRouter(root, source, operations, state.importer);
|
|
116
|
+
await ExecutionContext.run(root, () => composed?.start?.());
|
|
117
|
+
assertNoGlobalContainerLeak(registryBefore, source);
|
|
118
|
+
if (catalog) {
|
|
119
|
+
renderer = await buildRendererTier({
|
|
120
|
+
source,
|
|
121
|
+
router,
|
|
122
|
+
catalog,
|
|
123
|
+
operations,
|
|
124
|
+
importer: state.importer,
|
|
125
|
+
hooks: composed,
|
|
126
|
+
dom: selection.dom
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return environmentSurface({ root, env, router, operations, renderer, controllerScopes, dispose });
|
|
130
|
+
} catch (error) {
|
|
131
|
+
await dispose().catch(() => {
|
|
132
|
+
});
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function environmentSurface(parts) {
|
|
137
|
+
const { root, env, router, operations, renderer, controllerScopes, dispose } = parts;
|
|
138
|
+
const dom = () => renderer?.dom ?? tierMissing("dom", "dom driver");
|
|
139
|
+
const pages = () => renderer ?? tierMissing("pages", "renderer");
|
|
140
|
+
return {
|
|
141
|
+
root,
|
|
142
|
+
router,
|
|
143
|
+
operations,
|
|
144
|
+
call: async (operation, input) => {
|
|
145
|
+
if (!operations.has(operation)) {
|
|
146
|
+
throw new TestWoodError(
|
|
147
|
+
`operation "${operation}" is not in the selected aperture; available operations:
|
|
148
|
+
${[...operations.keys()].join("\n ")}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return await ExecutionContext.run(root, () => router.dispatch(operation, input));
|
|
152
|
+
},
|
|
153
|
+
controller: async (controller) => {
|
|
154
|
+
const scope = root.extend();
|
|
155
|
+
controllerScopes.push(scope);
|
|
156
|
+
if (!scope.isRegistered(controller)) scope.registerClass(controller);
|
|
157
|
+
return await scope.get(controller);
|
|
158
|
+
},
|
|
159
|
+
get: async (token) => await root.get(token),
|
|
160
|
+
get frontend() {
|
|
161
|
+
return pages().frontend;
|
|
162
|
+
},
|
|
163
|
+
get events() {
|
|
164
|
+
return pages().events;
|
|
165
|
+
},
|
|
166
|
+
get rendererRoot() {
|
|
167
|
+
return pages().rendererRoot;
|
|
168
|
+
},
|
|
169
|
+
open: async (page, params, query) => pages().open(page, params, query),
|
|
170
|
+
navigate: async (page, params, query) => pages().navigate(page, params, query),
|
|
171
|
+
current: () => pages().current(),
|
|
172
|
+
settle: async () => pages().settle(),
|
|
173
|
+
get target() {
|
|
174
|
+
return dom().target;
|
|
175
|
+
},
|
|
176
|
+
click: async (selector) => dom().click(selector),
|
|
177
|
+
type: async (selector, text) => dom().type(selector, text),
|
|
178
|
+
submit: async (selector) => dom().submit(selector),
|
|
179
|
+
text: async (selector) => dom().text(selector),
|
|
180
|
+
exists: async (selector) => dom().exists(selector),
|
|
181
|
+
// Method expectations only: `settle()` is the completion contract for
|
|
182
|
+
// owned renderer work, and a test may legitimately verify while a
|
|
183
|
+
// deliberately deferred stream is still in flight.
|
|
184
|
+
verify: () => env.verify(),
|
|
185
|
+
dispose,
|
|
186
|
+
[ENV_REGISTRY]: env[ENV_REGISTRY]
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
class TestWoodBuilder {
|
|
190
|
+
constructor(state) {
|
|
191
|
+
this.state = state;
|
|
192
|
+
}
|
|
193
|
+
static create(configPath) {
|
|
194
|
+
if (typeof configPath !== "string" || !configPath.length) {
|
|
195
|
+
throw new TestWoodError("testWood(configPath) requires the path to a wood.config.yml");
|
|
196
|
+
}
|
|
197
|
+
return new TestWoodBuilder({
|
|
198
|
+
configPath: path.resolve(configPath),
|
|
199
|
+
selection: null,
|
|
200
|
+
ioc: testIoc(),
|
|
201
|
+
importer: defaultImporter
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/** Declare which tiers exist: `main`, `pages`, `dom`. Replaces any earlier selection. */
|
|
205
|
+
select(selection) {
|
|
206
|
+
resolveSelection(selection);
|
|
207
|
+
return this.derive({ selection });
|
|
208
|
+
}
|
|
209
|
+
/** Route hook/controller imports through a caller-owned importer (escape hatch). */
|
|
210
|
+
importer(importer) {
|
|
211
|
+
if (typeof importer !== "function") throw new TestWoodError("importer() expects a function");
|
|
212
|
+
return this.derive({ importer });
|
|
213
|
+
}
|
|
214
|
+
/* ---- Composition: exact @noego/testing semantics on the main root ---- */
|
|
215
|
+
use(preset) {
|
|
216
|
+
return this.derive({ ioc: this.state.ioc.use(preset) });
|
|
217
|
+
}
|
|
218
|
+
classes(config) {
|
|
219
|
+
return this.derive({ ioc: this.state.ioc.classes(config) });
|
|
220
|
+
}
|
|
221
|
+
functions(config) {
|
|
222
|
+
return this.derive({ ioc: this.state.ioc.functions(config) });
|
|
223
|
+
}
|
|
224
|
+
values(config) {
|
|
225
|
+
return this.derive({ ioc: this.state.ioc.values(config) });
|
|
226
|
+
}
|
|
227
|
+
methods(config) {
|
|
228
|
+
return this.derive({ ioc: this.state.ioc.methods(config) });
|
|
229
|
+
}
|
|
230
|
+
async build() {
|
|
231
|
+
if (!this.state.selection) {
|
|
232
|
+
throw new TestWoodError("select() a tier before build(): { main }, { pages }, or both");
|
|
233
|
+
}
|
|
234
|
+
return assembleEnvironment(this.state, resolveSelection(this.state.selection));
|
|
235
|
+
}
|
|
236
|
+
derive(patch) {
|
|
237
|
+
return new TestWoodBuilder({ ...this.state, ...patch });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function testWood(configPath) {
|
|
241
|
+
return TestWoodBuilder.create(configPath);
|
|
242
|
+
}
|
|
243
|
+
export {
|
|
244
|
+
TestWoodBuilder,
|
|
245
|
+
testWood
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=test_wood_config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/test_wood_config.ts"],"sourcesContent":["/**\n * `testWood(configPath)` — the canonical Wood testing entry point.\n *\n * One persistent immutable builder over the production `wood.config.yml`:\n *\n * const app = await testWood('wood.config.yml')\n * .select({ main: { module: 'backend_service' }, pages: ['settings.backend-service'] })\n * .classes([[BackendServiceLifecycleCoordinator, LifecycleStub]])\n * .methods([[LifecycleStub, { getStatus: test.once(test.returns(status)) }]])\n * .build();\n *\n * `select` decides what exists (main router, renderer pages, DOM); the\n * `@noego/testing` vocabulary (`use/classes/functions/values/methods`, exact\n * tokens) decides what is replaced — controllers included, since the router\n * resolves them through the same root. Controllers, middleware and views are\n * auto-bound from the config; the app composes its backend through the\n * optional `main.testing.compose` hook and mounts pages through\n * `renderer.testing.host`.\n *\n * Every build creates fresh `@noego/ioc` roots (main, and renderer when\n * pages are selected) — nothing global is touched, so environments coexist\n * in one process.\n */\nimport path from 'node:path';\nimport type { IContainer } from '@noego/ioc';\nimport { ExecutionContext } from '@noego/ioc';\nimport { testIoc, TestIocBuilder, ENV_REGISTRY } from '@noego/testing';\nimport type { ConfigMap, MethodsConfig, TestEnvironment, UseInput } from '@noego/testing';\nimport { WoodRouter } from '../router/wood_router.js';\nimport type { OperationDef } from '../types/index.js';\nimport type { WoodConfig } from '../types/config.js';\nimport type { FrontendApplicationSurface } from '../frontend/frontend_surface.js';\nimport type { FrontendPage } from '../frontend/frontend_page.js';\nimport type { RouterBridgeEvents } from './create_router_bridge.js';\nimport { TestWoodError } from './test_wood.js';\nimport {\n defaultImporter,\n importDefaultExport,\n loadWoodConfigSource,\n type ModuleImporter,\n type WoodConfigSource,\n} from './wood_config_source.js';\nimport {\n resolveSelection,\n selectCatalog,\n selectOperations,\n type ResolvedWoodSelection,\n type WoodTestSelection,\n} from './wood_test_selection.js';\nimport {\n buildRendererTier,\n type RendererTier,\n type WoodDomDriver,\n type WoodRendererHooks,\n} from './wood_env_renderer.js';\n\n/** What the app's `main.testing.compose` default export receives. */\nexport interface WoodComposeContext {\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n readonly selection: ResolvedWoodSelection;\n /** The selected operations (the router will serve exactly these). */\n readonly operations: ReadonlyMap<string, OperationDef>;\n readonly importer: ModuleImporter;\n /**\n * Dispatch through the environment's router. Available from `start()`\n * onward (the router is built after compose returns); calling it during\n * compose itself fails.\n */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n}\n\n/** What the compose hook may hand back; every member is optional. */\nexport interface WoodComposeResult extends WoodRendererHooks {\n /**\n * Runs AFTER the test's substitutions are installed on the root (compose\n * itself runs before them, so production registrations never shadow a\n * seam). Seed world state and warm caches here — through the seamed graph,\n * exactly as the test will observe it.\n */\n start?(): void | Promise<void>;\n /**\n * Quiesce BEFORE the main root disposes its instances: silence push\n * delivery, drain detached work — everything that still needs the live\n * graph (database, tracer) the services' own dispose hooks run against.\n */\n drain?(): void | Promise<void>;\n /** Release resources AFTER the roots are disposed (close files, remove dirs). */\n dispose?(): void | Promise<void>;\n}\n\n/**\n * The app's backend composition on the root the builder hands it. Register\n * lazily: nothing expensive should run until a test resolves it.\n */\nexport type WoodComposeMain = (\n root: IContainer,\n context: WoodComposeContext,\n) => WoodComposeResult | void | Promise<WoodComposeResult | void>;\n\ntype Params = Record<string, string>;\n\nexport interface TestWoodEnvironment {\n /** The main-process IoC root of this environment. */\n readonly root: IContainer;\n readonly router: WoodRouter;\n /** The selected operations. */\n readonly operations: ReadonlyMap<string, OperationDef>;\n /** Execute a selected operation through the real pipeline. */\n call<T = unknown>(operation: string, input?: unknown): Promise<T>;\n /** Resolve a controller class through a fresh main scope (root seams apply). */\n controller<T>(controller: new (...args: never[]) => T): Promise<T>;\n /** Resolve any dependency from the main root. */\n get<T>(token: unknown): Promise<T>;\n\n /** Renderer surface — present only when `pages` was selected. */\n readonly frontend: FrontendApplicationSurface;\n /** Backend push emitter feeding renderer subscriptions (pages only). */\n readonly events: RouterBridgeEvents;\n readonly rendererRoot: IContainer;\n open(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n navigate(page: string, params?: Params, query?: Params): Promise<FrontendPage>;\n current(): FrontendPage | null;\n settle(): Promise<void>;\n\n /** DOM driver — present only when `dom` was selected. */\n readonly target: Element;\n click(selector: string): Promise<void>;\n type(selector: string, text: string): Promise<void>;\n submit(selector: string): Promise<void>;\n text(selector: string): Promise<string>;\n exists(selector: string): Promise<boolean>;\n\n /** Finalize `test.*` expectations; fails on undrained renderer work. */\n verify(): Promise<void>;\n dispose(): Promise<void>;\n readonly [ENV_REGISTRY]: unknown;\n}\n\ninterface BuilderState {\n readonly configPath: string;\n readonly selection: WoodTestSelection | null;\n readonly ioc: TestIocBuilder;\n readonly importer: ModuleImporter;\n}\n\nconst CONTAINER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.container.registry');\n\nfunction globalRegistrySize(): number {\n const registry = (globalThis as Record<symbol, unknown>)[CONTAINER_REGISTRY_SYMBOL];\n return registry instanceof Map ? registry.size : 0;\n}\n\nfunction tierMissing(tier: 'pages' | 'dom', member: string): never {\n throw new TestWoodError(\n `the ${member} is not available: this environment was built without { ${tier} } — add it to select()`,\n );\n}\n\n/** A router over the selected aperture with every controller auto-bound from the config. */\nfunction buildRouter(\n root: IContainer,\n source: WoodConfigSource,\n operations: Map<string, OperationDef>,\n importer: ModuleImporter,\n): WoodRouter {\n const router = new WoodRouter({\n container: root,\n controllersDir: source.mainControllersDir,\n middlewareDir: source.mainMiddlewareDir,\n mode: 'dev',\n });\n const bound = new Set<string>();\n for (const operation of operations.values()) {\n if (bound.has(operation.controller)) continue;\n bound.add(operation.controller);\n const file = source.mainControllerFile(operation.controller);\n router.registerControllerLoader(operation.controller, () => importer(file));\n }\n router.loadOperations(operations);\n router.validateControllers();\n return router;\n}\n\nasync function runCompose(\n root: IContainer,\n source: WoodConfigSource,\n context: WoodComposeContext,\n): Promise<WoodComposeResult | null> {\n const composePath = source.hookModule(source.config.main.testing?.compose);\n if (!composePath) return null;\n const compose = await importDefaultExport<WoodComposeMain>(context.importer, composePath, 'main.testing.compose');\n if (typeof compose !== 'function') {\n throw new TestWoodError(`main.testing.compose default export must be a function (${composePath})`);\n }\n return (await ExecutionContext.run(root as never, () => compose(root, context))) ?? null;\n}\n\n/**\n * Dispose the main root under its own execution context (service teardown\n * hooks resolve collaborators such as tracers exactly like any other call),\n * naming every instance whose dispose hook failed.\n */\nasync function disposeRoot(env: TestEnvironment): Promise<void> {\n try {\n await ExecutionContext.run(env.root as never, () => env.dispose());\n } catch (error) {\n const failures = (error as { failures?: Array<{ token: string; error: unknown }> }).failures;\n if (!Array.isArray(failures)) throw error;\n const detail = failures\n .map((failure) => `${failure.token}: ${failure.error instanceof Error ? failure.error.message : String(failure.error)}`)\n .join('\\n ');\n throw new TestWoodError(`main root disposal failed for ${failures.length} instance(s):\\n ${detail}`);\n }\n}\n\nfunction assertNoGlobalContainerLeak(before: number, source: WoodConfigSource): void {\n if (!process.env.VITEST) return;\n const after = globalRegistrySize();\n if (after > before) {\n throw new TestWoodError(\n `${source.config.main.testing?.compose ?? 'the build'} registered ${after - before} process-global ` +\n 'Wood container(s) (getContainer). Compose on the root the builder hands you so environments stay isolated.',\n );\n }\n}\n\n/** Build the environment from an already-resolved selection (split from the builder for budget). */\nasync function assembleEnvironment(\n state: BuilderState,\n selection: ResolvedWoodSelection,\n): Promise<TestWoodEnvironment> {\n const source = loadWoodConfigSource(state.configPath);\n const operations = selectOperations(source.operations, selection.main);\n const catalog = selection.pages ? selectCatalog(source.views(), selection.pages) : null;\n\n const registryBefore = globalRegistrySize();\n let composed: WoodComposeResult | null = null;\n let renderer: RendererTier | null = null;\n const controllerScopes: Array<{ dispose(): Promise<void> }> = [];\n let router: WoodRouter | null = null;\n const context: WoodComposeContext = {\n configPath: source.configPath,\n configDir: source.configDir,\n config: source.config,\n selection,\n operations,\n importer: state.importer,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!router) throw new TestWoodError('compose context call() is available from start() onward, not during compose');\n const live = router;\n return (await ExecutionContext.run(env.root as never, () => live.dispatch(operation, input))) as T;\n },\n };\n\n // Production composition lands on the fresh root FIRST; the test's\n // substitutions are applied on top of it by the ioc builder, so a seam\n // always shadows what the app registered for the same token.\n const env: TestEnvironment = await state.ioc.build({\n prepare: async (root) => {\n composed = await runCompose(root, source, context);\n },\n });\n const root = env.root;\n\n const dispose = async (): Promise<void> => {\n if (renderer) await renderer.dispose();\n for (const scope of controllerScopes.reverse()) await scope.dispose();\n await composed?.drain?.();\n try {\n await disposeRoot(env);\n } finally {\n await composed?.dispose?.();\n }\n };\n\n try {\n router = buildRouter(root, source, operations, state.importer);\n await ExecutionContext.run(root as never, () => composed?.start?.());\n assertNoGlobalContainerLeak(registryBefore, source);\n if (catalog) {\n renderer = await buildRendererTier({\n source,\n router,\n catalog,\n operations,\n importer: state.importer,\n hooks: composed,\n dom: selection.dom,\n });\n }\n return environmentSurface({ root, env, router, operations, renderer, controllerScopes, dispose });\n } catch (error) {\n await dispose().catch(() => {});\n throw error;\n }\n}\n\ninterface SurfaceParts {\n root: IContainer;\n env: TestEnvironment;\n router: WoodRouter;\n operations: Map<string, OperationDef>;\n renderer: RendererTier | null;\n controllerScopes: Array<{ dispose(): Promise<void> }>;\n dispose(): Promise<void>;\n}\n\nfunction environmentSurface(parts: SurfaceParts): TestWoodEnvironment {\n const { root, env, router, operations, renderer, controllerScopes, dispose } = parts;\n const dom = (): WoodDomDriver => renderer?.dom ?? tierMissing('dom', 'dom driver');\n const pages = (): RendererTier => renderer ?? tierMissing('pages', 'renderer');\n\n return {\n root,\n router,\n operations,\n call: async <T>(operation: string, input?: unknown): Promise<T> => {\n if (!operations.has(operation)) {\n throw new TestWoodError(\n `operation \"${operation}\" is not in the selected aperture; available operations:\\n ${[...operations.keys()].join('\\n ')}`,\n );\n }\n return (await ExecutionContext.run(root as never, () => router.dispatch(operation, input))) as T;\n },\n controller: async <T>(controller: new (...args: never[]) => T): Promise<T> => {\n const scope = root.extend();\n controllerScopes.push(scope as unknown as { dispose(): Promise<void> });\n if (!scope.isRegistered(controller)) scope.registerClass(controller);\n return (await scope.get(controller)) as T;\n },\n get: async <T>(token: unknown): Promise<T> => (await root.get(token)) as T,\n\n get frontend() { return pages().frontend; },\n get events() { return pages().events; },\n get rendererRoot() { return pages().rendererRoot; },\n open: async (page, params, query) => pages().open(page, params, query),\n navigate: async (page, params, query) => pages().navigate(page, params, query),\n current: () => pages().current(),\n settle: async () => pages().settle(),\n\n get target() { return dom().target; },\n click: async (selector) => dom().click(selector),\n type: async (selector, text) => dom().type(selector, text),\n submit: async (selector) => dom().submit(selector),\n text: async (selector) => dom().text(selector),\n exists: async (selector) => dom().exists(selector),\n\n // Method expectations only: `settle()` is the completion contract for\n // owned renderer work, and a test may legitimately verify while a\n // deliberately deferred stream is still in flight.\n verify: () => env.verify(),\n dispose,\n [ENV_REGISTRY]: (env as unknown as Record<symbol, unknown>)[ENV_REGISTRY],\n };\n}\n\nexport class TestWoodBuilder {\n private constructor(private readonly state: BuilderState) {}\n\n static create(configPath: string): TestWoodBuilder {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n return new TestWoodBuilder({\n configPath: path.resolve(configPath),\n selection: null,\n ioc: testIoc(),\n importer: defaultImporter,\n });\n }\n\n /** Declare which tiers exist: `main`, `pages`, `dom`. Replaces any earlier selection. */\n select(selection: WoodTestSelection): TestWoodBuilder {\n resolveSelection(selection);\n return this.derive({ selection });\n }\n\n /** Route hook/controller imports through a caller-owned importer (escape hatch). */\n importer(importer: ModuleImporter): TestWoodBuilder {\n if (typeof importer !== 'function') throw new TestWoodError('importer() expects a function');\n return this.derive({ importer });\n }\n\n /* ---- Composition: exact @noego/testing semantics on the main root ---- */\n\n use(preset: UseInput): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.use(preset) });\n }\n\n classes(config: ConfigMap<new (...args: any[]) => unknown>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.classes(config) });\n }\n\n functions(config: ConfigMap<(...args: any[]) => any>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.functions(config) });\n }\n\n values(config: ConfigMap<unknown>): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.values(config) });\n }\n\n methods(config: MethodsConfig): TestWoodBuilder {\n return this.derive({ ioc: this.state.ioc.methods(config) });\n }\n\n async build(): Promise<TestWoodEnvironment> {\n if (!this.state.selection) {\n throw new TestWoodError('select() a tier before build(): { main }, { pages }, or both');\n }\n return assembleEnvironment(this.state, resolveSelection(this.state.selection));\n }\n\n private derive(patch: Partial<BuilderState>): TestWoodBuilder {\n return new TestWoodBuilder({ ...this.state, ...patch });\n }\n}\n\nexport function testWood(configPath: string): TestWoodBuilder {\n return TestWoodBuilder.create(configPath);\n}\n"],"mappings":"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","names":["root"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var wood_config_source_exports = {};
|
|
30
|
+
__export(wood_config_source_exports, {
|
|
31
|
+
defaultImporter: () => defaultImporter,
|
|
32
|
+
expandOpenapiFiles: () => expandOpenapiFiles,
|
|
33
|
+
importDefaultExport: () => importDefaultExport,
|
|
34
|
+
loadWoodConfigSource: () => loadWoodConfigSource
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(wood_config_source_exports);
|
|
37
|
+
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
38
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
39
|
+
var import_node_url = require("node:url");
|
|
40
|
+
var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
41
|
+
var import_glob = require("glob");
|
|
42
|
+
var import_config_parser = require("../config/config_parser.cjs");
|
|
43
|
+
var import_operations_parser = require("../parser/operations_parser.cjs");
|
|
44
|
+
var import_views_parser = require("../parser/views_parser.cjs");
|
|
45
|
+
var import_controller_registration_generator = require("../codegen/controller_registration_generator.cjs");
|
|
46
|
+
var import_test_wood = require("./test_wood.cjs");
|
|
47
|
+
const defaultImporter = (absolutePath) => import(
|
|
48
|
+
/* @vite-ignore */
|
|
49
|
+
(0, import_node_url.pathToFileURL)(absolutePath).href
|
|
50
|
+
);
|
|
51
|
+
const GLOB_MAGIC = /[*?[\]{}]/;
|
|
52
|
+
function expandOpenapiFiles(absolutePath) {
|
|
53
|
+
const doc = import_js_yaml.default.load(import_node_fs.default.readFileSync(absolutePath, "utf-8"));
|
|
54
|
+
if (!doc || typeof doc !== "object" || !("stitch" in doc)) return [absolutePath];
|
|
55
|
+
if (!Array.isArray(doc.stitch)) {
|
|
56
|
+
throw new import_test_wood.TestWoodError(`${absolutePath}: "stitch" must be a list of YAML files or globs`);
|
|
57
|
+
}
|
|
58
|
+
const manifestDir = import_node_path.default.dirname(absolutePath);
|
|
59
|
+
const files = [];
|
|
60
|
+
for (const entry of doc.stitch) {
|
|
61
|
+
if (typeof entry !== "string" || !entry.length) {
|
|
62
|
+
throw new import_test_wood.TestWoodError(`${absolutePath}: every "stitch" entry must be a non-empty string`);
|
|
63
|
+
}
|
|
64
|
+
files.push(...expandStitchEntry(entry, manifestDir));
|
|
65
|
+
}
|
|
66
|
+
return files;
|
|
67
|
+
}
|
|
68
|
+
function expandStitchEntry(entry, manifestDir) {
|
|
69
|
+
if (!GLOB_MAGIC.test(entry)) return [import_node_path.default.resolve(manifestDir, entry)];
|
|
70
|
+
return (0, import_glob.globSync)(entry, { cwd: manifestDir, absolute: true, nodir: true }).sort();
|
|
71
|
+
}
|
|
72
|
+
function loadOperations(files) {
|
|
73
|
+
const merged = /* @__PURE__ */ new Map();
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
for (const [channel, operation] of (0, import_operations_parser.parseOperations)(import_node_fs.default.readFileSync(file, "utf-8"))) {
|
|
76
|
+
if (!merged.has(channel)) merged.set(channel, operation);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return merged;
|
|
80
|
+
}
|
|
81
|
+
function loadViews(files) {
|
|
82
|
+
const views = [];
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const content = import_node_fs.default.readFileSync(file, "utf-8");
|
|
85
|
+
if (!/^view:/m.test(content)) continue;
|
|
86
|
+
views.push(...(0, import_views_parser.parseViews)(content));
|
|
87
|
+
}
|
|
88
|
+
return views;
|
|
89
|
+
}
|
|
90
|
+
function loadWoodConfigSource(configPath) {
|
|
91
|
+
if (typeof configPath !== "string" || !configPath.length) {
|
|
92
|
+
throw new import_test_wood.TestWoodError("testWood(configPath) requires the path to a wood.config.yml");
|
|
93
|
+
}
|
|
94
|
+
const resolvedConfigPath = import_node_path.default.resolve(configPath);
|
|
95
|
+
if (!import_node_fs.default.existsSync(resolvedConfigPath)) {
|
|
96
|
+
throw new import_test_wood.TestWoodError(`testWood(configPath): config file not found: ${resolvedConfigPath}`);
|
|
97
|
+
}
|
|
98
|
+
const config = (0, import_config_parser.parseConfig)(resolvedConfigPath);
|
|
99
|
+
const configDir = import_node_path.default.dirname(resolvedConfigPath);
|
|
100
|
+
const fromConfig = (relative) => import_node_path.default.resolve(configDir, relative);
|
|
101
|
+
const mainControllersDir = fromConfig(config.main.controllers);
|
|
102
|
+
const rendererComponentDir = fromConfig(config.renderer.componentDir);
|
|
103
|
+
let views = null;
|
|
104
|
+
return {
|
|
105
|
+
configPath: resolvedConfigPath,
|
|
106
|
+
configDir,
|
|
107
|
+
config,
|
|
108
|
+
operations: loadOperations(expandOpenapiFiles(fromConfig(config.main.openapi))),
|
|
109
|
+
mainControllersDir,
|
|
110
|
+
mainMiddlewareDir: fromConfig(config.main.middleware),
|
|
111
|
+
rendererComponentDir,
|
|
112
|
+
views: () => {
|
|
113
|
+
views ?? (views = loadViews(expandOpenapiFiles(fromConfig(config.renderer.openapi))));
|
|
114
|
+
return views;
|
|
115
|
+
},
|
|
116
|
+
mainControllerFile: (identity) => {
|
|
117
|
+
const file = (0, import_controller_registration_generator.resolveControllerFile)(identity, mainControllersDir);
|
|
118
|
+
if (file) return file;
|
|
119
|
+
throw new import_test_wood.TestWoodError(
|
|
120
|
+
`no controller file for identity "${identity}" under ${mainControllersDir}; looked for:
|
|
121
|
+
` + (0, import_controller_registration_generator.controllerFileCandidates)(identity, mainControllersDir).join("\n ")
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
rendererControllerFile: (reference) => {
|
|
125
|
+
const file = import_node_path.default.resolve(rendererComponentDir, reference);
|
|
126
|
+
if (import_node_fs.default.existsSync(file)) return file;
|
|
127
|
+
throw new import_test_wood.TestWoodError(`renderer controller "${reference}" not found at ${file}`);
|
|
128
|
+
},
|
|
129
|
+
hookModule: (relative) => relative ? fromConfig(relative) : null
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function importDefaultExport(importer, absolutePath, what) {
|
|
133
|
+
const mod = await importer(absolutePath);
|
|
134
|
+
if (mod.default === void 0) {
|
|
135
|
+
throw new import_test_wood.TestWoodError(`${what} module ${absolutePath} has no default export`);
|
|
136
|
+
}
|
|
137
|
+
return mod.default;
|
|
138
|
+
}
|
|
139
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
140
|
+
0 && (module.exports = {
|
|
141
|
+
defaultImporter,
|
|
142
|
+
expandOpenapiFiles,
|
|
143
|
+
importDefaultExport,
|
|
144
|
+
loadWoodConfigSource
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=wood_config_source.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/wood_config_source.ts"],"sourcesContent":["/**\n * Config-driven test source for `testWood(configPath)`.\n *\n * Everything a test environment binds is read from the production\n * `wood.config.yml`: operations from `main.openapi` (a single document or a\n * stitch manifest), views from `renderer.openapi`, controller files from\n * `main.controllers` (same candidate rules as codegen) and renderer\n * controllers relative to `renderer.componentDir`. Tests name nothing by\n * hand — a config path is the whole source.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport yaml from 'js-yaml';\nimport { globSync } from 'glob';\nimport { parseConfig } from '../config/config_parser.cjs';\nimport type { WoodConfig } from '../types/config.cjs';\nimport { parseOperations } from '../parser/operations_parser.cjs';\nimport { parseViews } from '../parser/views_parser.cjs';\nimport type { OperationDef, ViewDef } from '../types/index.cjs';\nimport {\n controllerFileCandidates,\n resolveControllerFile,\n} from '../codegen/controller_registration_generator.cjs';\nimport { TestWoodError } from './test_wood.cjs';\n\n/** Imports a module by absolute path; the default keeps the caller's module registry. */\nexport type ModuleImporter = (absolutePath: string) => Promise<unknown>;\n\nexport const defaultImporter: ModuleImporter = (absolutePath) =>\n import(/* @vite-ignore */ pathToFileURL(absolutePath).href);\n\nexport interface WoodConfigSource {\n readonly configPath: string;\n readonly configDir: string;\n readonly config: WoodConfig;\n /** Every production operation, stitch order, first declaration wins. */\n readonly operations: Map<string, OperationDef>;\n readonly mainControllersDir: string;\n readonly mainMiddlewareDir: string;\n readonly rendererComponentDir: string;\n /** Every production view (parsed on first use — main-only builds never read them). */\n views(): ViewDef[];\n /** Absolute file for a main controller identity, or a build error naming the candidates. */\n mainControllerFile(identity: string): string;\n /** Absolute file for a renderer controller reference (views `controller:`). */\n rendererControllerFile(reference: string): string;\n /** Absolute path of an optional config-relative hook module, if declared. */\n hookModule(relative: string | undefined): string | null;\n}\n\ninterface StitchManifest {\n stitch?: unknown;\n}\n\nconst GLOB_MAGIC = /[*?[\\]{}]/;\n\n/**\n * Expand an openapi reference into the ordered list of YAML files it names:\n * a stitch manifest (`stitch: [...]`, entries relative to the manifest, globs\n * allowed) or a single document.\n */\nexport function expandOpenapiFiles(absolutePath: string): string[] {\n const doc = yaml.load(fs.readFileSync(absolutePath, 'utf-8')) as StitchManifest | null;\n if (!doc || typeof doc !== 'object' || !('stitch' in doc)) return [absolutePath];\n if (!Array.isArray(doc.stitch)) {\n throw new TestWoodError(`${absolutePath}: \"stitch\" must be a list of YAML files or globs`);\n }\n const manifestDir = path.dirname(absolutePath);\n const files: string[] = [];\n for (const entry of doc.stitch) {\n if (typeof entry !== 'string' || !entry.length) {\n throw new TestWoodError(`${absolutePath}: every \"stitch\" entry must be a non-empty string`);\n }\n files.push(...expandStitchEntry(entry, manifestDir));\n }\n return files;\n}\n\nfunction expandStitchEntry(entry: string, manifestDir: string): string[] {\n if (!GLOB_MAGIC.test(entry)) return [path.resolve(manifestDir, entry)];\n return globSync(entry, { cwd: manifestDir, absolute: true, nodir: true }).sort();\n}\n\nfunction loadOperations(files: string[]): Map<string, OperationDef> {\n const merged = new Map<string, OperationDef>();\n for (const file of files) {\n for (const [channel, operation] of parseOperations(fs.readFileSync(file, 'utf-8'))) {\n if (!merged.has(channel)) merged.set(channel, operation);\n }\n }\n return merged;\n}\n\n/** Views documents only; a stitched directory may hold other YAML kinds. */\nfunction loadViews(files: string[]): ViewDef[] {\n const views: ViewDef[] = [];\n for (const file of files) {\n const content = fs.readFileSync(file, 'utf-8');\n if (!/^view:/m.test(content)) continue;\n views.push(...parseViews(content));\n }\n return views;\n}\n\nexport function loadWoodConfigSource(configPath: string): WoodConfigSource {\n if (typeof configPath !== 'string' || !configPath.length) {\n throw new TestWoodError('testWood(configPath) requires the path to a wood.config.yml');\n }\n const resolvedConfigPath = path.resolve(configPath);\n if (!fs.existsSync(resolvedConfigPath)) {\n throw new TestWoodError(`testWood(configPath): config file not found: ${resolvedConfigPath}`);\n }\n const config = parseConfig(resolvedConfigPath);\n const configDir = path.dirname(resolvedConfigPath);\n const fromConfig = (relative: string): string => path.resolve(configDir, relative);\n\n const mainControllersDir = fromConfig(config.main.controllers);\n const rendererComponentDir = fromConfig(config.renderer.componentDir);\n let views: ViewDef[] | null = null;\n\n return {\n configPath: resolvedConfigPath,\n configDir,\n config,\n operations: loadOperations(expandOpenapiFiles(fromConfig(config.main.openapi))),\n mainControllersDir,\n mainMiddlewareDir: fromConfig(config.main.middleware),\n rendererComponentDir,\n views: () => {\n views ??= loadViews(expandOpenapiFiles(fromConfig(config.renderer.openapi)));\n return views;\n },\n mainControllerFile: (identity) => {\n const file = resolveControllerFile(identity, mainControllersDir);\n if (file) return file;\n throw new TestWoodError(\n `no controller file for identity \"${identity}\" under ${mainControllersDir}; looked for:\\n ` +\n controllerFileCandidates(identity, mainControllersDir).join('\\n '),\n );\n },\n rendererControllerFile: (reference) => {\n const file = path.resolve(rendererComponentDir, reference);\n if (fs.existsSync(file)) return file;\n throw new TestWoodError(`renderer controller \"${reference}\" not found at ${file}`);\n },\n hookModule: (relative) => (relative ? fromConfig(relative) : null),\n };\n}\n\n/** Import a hook module declared in the config and return its default export. */\nexport async function importDefaultExport<T>(\n importer: ModuleImporter,\n absolutePath: string,\n what: string,\n): Promise<T> {\n const mod = (await importer(absolutePath)) as { default?: unknown };\n if (mod.default === undefined) {\n throw new TestWoodError(`${what} module ${absolutePath} has no default export`);\n }\n return mod.default as T;\n}\n"],"mappings":"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","names":["yaml","fs","path"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { WoodConfig } from '../types/config.cjs';
|
|
2
|
+
import { OperationDef } from '../types/operations.cjs';
|
|
3
|
+
import { ViewDef } from '../types/views.cjs';
|
|
4
|
+
|
|
5
|
+
/** Imports a module by absolute path; the default keeps the caller's module registry. */
|
|
6
|
+
type ModuleImporter = (absolutePath: string) => Promise<unknown>;
|
|
7
|
+
declare const defaultImporter: ModuleImporter;
|
|
8
|
+
interface WoodConfigSource {
|
|
9
|
+
readonly configPath: string;
|
|
10
|
+
readonly configDir: string;
|
|
11
|
+
readonly config: WoodConfig;
|
|
12
|
+
/** Every production operation, stitch order, first declaration wins. */
|
|
13
|
+
readonly operations: Map<string, OperationDef>;
|
|
14
|
+
readonly mainControllersDir: string;
|
|
15
|
+
readonly mainMiddlewareDir: string;
|
|
16
|
+
readonly rendererComponentDir: string;
|
|
17
|
+
/** Every production view (parsed on first use — main-only builds never read them). */
|
|
18
|
+
views(): ViewDef[];
|
|
19
|
+
/** Absolute file for a main controller identity, or a build error naming the candidates. */
|
|
20
|
+
mainControllerFile(identity: string): string;
|
|
21
|
+
/** Absolute file for a renderer controller reference (views `controller:`). */
|
|
22
|
+
rendererControllerFile(reference: string): string;
|
|
23
|
+
/** Absolute path of an optional config-relative hook module, if declared. */
|
|
24
|
+
hookModule(relative: string | undefined): string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Expand an openapi reference into the ordered list of YAML files it names:
|
|
28
|
+
* a stitch manifest (`stitch: [...]`, entries relative to the manifest, globs
|
|
29
|
+
* allowed) or a single document.
|
|
30
|
+
*/
|
|
31
|
+
declare function expandOpenapiFiles(absolutePath: string): string[];
|
|
32
|
+
declare function loadWoodConfigSource(configPath: string): WoodConfigSource;
|
|
33
|
+
/** Import a hook module declared in the config and return its default export. */
|
|
34
|
+
declare function importDefaultExport<T>(importer: ModuleImporter, absolutePath: string, what: string): Promise<T>;
|
|
35
|
+
|
|
36
|
+
export { type ModuleImporter, type WoodConfigSource, defaultImporter, expandOpenapiFiles, importDefaultExport, loadWoodConfigSource };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { WoodConfig } from '../types/config.js';
|
|
2
|
+
import { OperationDef } from '../types/operations.js';
|
|
3
|
+
import { ViewDef } from '../types/views.js';
|
|
4
|
+
|
|
5
|
+
/** Imports a module by absolute path; the default keeps the caller's module registry. */
|
|
6
|
+
type ModuleImporter = (absolutePath: string) => Promise<unknown>;
|
|
7
|
+
declare const defaultImporter: ModuleImporter;
|
|
8
|
+
interface WoodConfigSource {
|
|
9
|
+
readonly configPath: string;
|
|
10
|
+
readonly configDir: string;
|
|
11
|
+
readonly config: WoodConfig;
|
|
12
|
+
/** Every production operation, stitch order, first declaration wins. */
|
|
13
|
+
readonly operations: Map<string, OperationDef>;
|
|
14
|
+
readonly mainControllersDir: string;
|
|
15
|
+
readonly mainMiddlewareDir: string;
|
|
16
|
+
readonly rendererComponentDir: string;
|
|
17
|
+
/** Every production view (parsed on first use — main-only builds never read them). */
|
|
18
|
+
views(): ViewDef[];
|
|
19
|
+
/** Absolute file for a main controller identity, or a build error naming the candidates. */
|
|
20
|
+
mainControllerFile(identity: string): string;
|
|
21
|
+
/** Absolute file for a renderer controller reference (views `controller:`). */
|
|
22
|
+
rendererControllerFile(reference: string): string;
|
|
23
|
+
/** Absolute path of an optional config-relative hook module, if declared. */
|
|
24
|
+
hookModule(relative: string | undefined): string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Expand an openapi reference into the ordered list of YAML files it names:
|
|
28
|
+
* a stitch manifest (`stitch: [...]`, entries relative to the manifest, globs
|
|
29
|
+
* allowed) or a single document.
|
|
30
|
+
*/
|
|
31
|
+
declare function expandOpenapiFiles(absolutePath: string): string[];
|
|
32
|
+
declare function loadWoodConfigSource(configPath: string): WoodConfigSource;
|
|
33
|
+
/** Import a hook module declared in the config and return its default export. */
|
|
34
|
+
declare function importDefaultExport<T>(importer: ModuleImporter, absolutePath: string, what: string): Promise<T>;
|
|
35
|
+
|
|
36
|
+
export { type ModuleImporter, type WoodConfigSource, defaultImporter, expandOpenapiFiles, importDefaultExport, loadWoodConfigSource };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
import { globSync } from "glob";
|
|
6
|
+
import { parseConfig } from "../config/config_parser.js";
|
|
7
|
+
import { parseOperations } from "../parser/operations_parser.js";
|
|
8
|
+
import { parseViews } from "../parser/views_parser.js";
|
|
9
|
+
import {
|
|
10
|
+
controllerFileCandidates,
|
|
11
|
+
resolveControllerFile
|
|
12
|
+
} from "../codegen/controller_registration_generator.js";
|
|
13
|
+
import { TestWoodError } from "./test_wood.js";
|
|
14
|
+
const defaultImporter = (absolutePath) => import(
|
|
15
|
+
/* @vite-ignore */
|
|
16
|
+
pathToFileURL(absolutePath).href
|
|
17
|
+
);
|
|
18
|
+
const GLOB_MAGIC = /[*?[\]{}]/;
|
|
19
|
+
function expandOpenapiFiles(absolutePath) {
|
|
20
|
+
const doc = yaml.load(fs.readFileSync(absolutePath, "utf-8"));
|
|
21
|
+
if (!doc || typeof doc !== "object" || !("stitch" in doc)) return [absolutePath];
|
|
22
|
+
if (!Array.isArray(doc.stitch)) {
|
|
23
|
+
throw new TestWoodError(`${absolutePath}: "stitch" must be a list of YAML files or globs`);
|
|
24
|
+
}
|
|
25
|
+
const manifestDir = path.dirname(absolutePath);
|
|
26
|
+
const files = [];
|
|
27
|
+
for (const entry of doc.stitch) {
|
|
28
|
+
if (typeof entry !== "string" || !entry.length) {
|
|
29
|
+
throw new TestWoodError(`${absolutePath}: every "stitch" entry must be a non-empty string`);
|
|
30
|
+
}
|
|
31
|
+
files.push(...expandStitchEntry(entry, manifestDir));
|
|
32
|
+
}
|
|
33
|
+
return files;
|
|
34
|
+
}
|
|
35
|
+
function expandStitchEntry(entry, manifestDir) {
|
|
36
|
+
if (!GLOB_MAGIC.test(entry)) return [path.resolve(manifestDir, entry)];
|
|
37
|
+
return globSync(entry, { cwd: manifestDir, absolute: true, nodir: true }).sort();
|
|
38
|
+
}
|
|
39
|
+
function loadOperations(files) {
|
|
40
|
+
const merged = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
for (const [channel, operation] of parseOperations(fs.readFileSync(file, "utf-8"))) {
|
|
43
|
+
if (!merged.has(channel)) merged.set(channel, operation);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
function loadViews(files) {
|
|
49
|
+
const views = [];
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
52
|
+
if (!/^view:/m.test(content)) continue;
|
|
53
|
+
views.push(...parseViews(content));
|
|
54
|
+
}
|
|
55
|
+
return views;
|
|
56
|
+
}
|
|
57
|
+
function loadWoodConfigSource(configPath) {
|
|
58
|
+
if (typeof configPath !== "string" || !configPath.length) {
|
|
59
|
+
throw new TestWoodError("testWood(configPath) requires the path to a wood.config.yml");
|
|
60
|
+
}
|
|
61
|
+
const resolvedConfigPath = path.resolve(configPath);
|
|
62
|
+
if (!fs.existsSync(resolvedConfigPath)) {
|
|
63
|
+
throw new TestWoodError(`testWood(configPath): config file not found: ${resolvedConfigPath}`);
|
|
64
|
+
}
|
|
65
|
+
const config = parseConfig(resolvedConfigPath);
|
|
66
|
+
const configDir = path.dirname(resolvedConfigPath);
|
|
67
|
+
const fromConfig = (relative) => path.resolve(configDir, relative);
|
|
68
|
+
const mainControllersDir = fromConfig(config.main.controllers);
|
|
69
|
+
const rendererComponentDir = fromConfig(config.renderer.componentDir);
|
|
70
|
+
let views = null;
|
|
71
|
+
return {
|
|
72
|
+
configPath: resolvedConfigPath,
|
|
73
|
+
configDir,
|
|
74
|
+
config,
|
|
75
|
+
operations: loadOperations(expandOpenapiFiles(fromConfig(config.main.openapi))),
|
|
76
|
+
mainControllersDir,
|
|
77
|
+
mainMiddlewareDir: fromConfig(config.main.middleware),
|
|
78
|
+
rendererComponentDir,
|
|
79
|
+
views: () => {
|
|
80
|
+
views ?? (views = loadViews(expandOpenapiFiles(fromConfig(config.renderer.openapi))));
|
|
81
|
+
return views;
|
|
82
|
+
},
|
|
83
|
+
mainControllerFile: (identity) => {
|
|
84
|
+
const file = resolveControllerFile(identity, mainControllersDir);
|
|
85
|
+
if (file) return file;
|
|
86
|
+
throw new TestWoodError(
|
|
87
|
+
`no controller file for identity "${identity}" under ${mainControllersDir}; looked for:
|
|
88
|
+
` + controllerFileCandidates(identity, mainControllersDir).join("\n ")
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
rendererControllerFile: (reference) => {
|
|
92
|
+
const file = path.resolve(rendererComponentDir, reference);
|
|
93
|
+
if (fs.existsSync(file)) return file;
|
|
94
|
+
throw new TestWoodError(`renderer controller "${reference}" not found at ${file}`);
|
|
95
|
+
},
|
|
96
|
+
hookModule: (relative) => relative ? fromConfig(relative) : null
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function importDefaultExport(importer, absolutePath, what) {
|
|
100
|
+
const mod = await importer(absolutePath);
|
|
101
|
+
if (mod.default === void 0) {
|
|
102
|
+
throw new TestWoodError(`${what} module ${absolutePath} has no default export`);
|
|
103
|
+
}
|
|
104
|
+
return mod.default;
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
defaultImporter,
|
|
108
|
+
expandOpenapiFiles,
|
|
109
|
+
importDefaultExport,
|
|
110
|
+
loadWoodConfigSource
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=wood_config_source.js.map
|