@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.
- package/README.md +118 -42
- 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 +18 -3
- package/dist/client/index.d.ts +18 -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 +78 -42
- package/dist/codegen/renderer_app_generator.cjs.map +1 -1
- package/dist/codegen/renderer_app_generator.js +78 -42
- 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 +10 -7
- package/dist/index.d.ts +10 -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/index.cjs +3 -0
- package/dist/navigation/index.cjs.map +1 -1
- package/dist/navigation/index.d.cts +25 -1
- package/dist/navigation/index.d.ts +25 -1
- package/dist/navigation/index.js +2 -0
- 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/browser.cjs +104 -23
- package/dist/testing/browser.cjs.map +1 -1
- package/dist/testing/browser.d.cts +8 -3
- package/dist/testing/browser.d.ts +8 -3
- package/dist/testing/browser.js +102 -22
- 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 +17 -2
- package/dist/testing/create_controller_harness.d.ts +17 -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/index.cjs +31 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +20 -5
- package/dist/testing/index.d.ts +20 -5
- package/dist/testing/index.js +19 -1
- 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 +13 -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 +0 -29
- 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 +0 -27
- 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 +0 -27
- 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 +0 -47
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n options: NavigationOptions;\n}\n\nexport interface NavigationOptions {\n preserveLayouts?: boolean;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n options: NavigationOptions,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n options: { ...options },\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query, options);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n\n// Dev/test-only automation introspection surface (Axe AXE-WP-02).\n// Benign import cycle: automation_introspection uses getNavigation lazily.\n// NOTE: keep these re-exports single-line — the tsup extension-rewrite plugin\n// only rewrites single-line import/export specifiers.\nexport { installAutomationIntrospection, WOOD_AUTOMATION_GLOBAL_KEY } from './automation_introspection.js';\nexport type { WoodAutomationSnapshot, WoodAutomationEvent, WoodAutomationIntrospection, InstallAutomationIntrospectionOptions } from './automation_introspection.js';\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["import { emitRendererTrace } from '../tracing/index.js';\n\nconst NAVIGATION_SYMBOL = Symbol.for('@noego/wood.navigation');\nconst HISTORY_STATE_MARKER = '__wood_navigation_state__';\n\nexport interface NavigationEntry {\n page: string;\n params: Record<string, string>;\n query: Record<string, string>;\n options: NavigationOptions;\n}\n\nexport interface NavigationOptions {\n preserveLayouts?: boolean;\n}\n\ntype NavigationHistoryState = NavigationEntry & {\n [HISTORY_STATE_MARKER]: true;\n};\n\ntype NavigationListener = (entry: NavigationEntry | null) => void;\n\nexport class Navigation {\n private entries: NavigationEntry[] = [];\n private index = -1;\n private listeners = new Set<NavigationListener>();\n\n getCurrent(): NavigationEntry | null {\n if (this.index < 0 || this.index >= this.entries.length) {\n return null;\n }\n return this.cloneEntry(this.entries[this.index]);\n }\n\n getPages(): NavigationEntry[] {\n return this.entries.map((entry) => this.cloneEntry(entry));\n }\n\n getParams(): Record<string, string> {\n const current = this.getCurrent();\n if (!current) {\n return {};\n }\n return { ...current.params };\n }\n\n replace(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n\n if (this.index < 0) {\n this.entries.push(entry);\n this.index = 0;\n } else {\n this.entries[this.index] = entry;\n }\n\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.replace',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n ): NavigationEntry {\n const entry = this.createEntry(page, params, query, options);\n if (this.index < this.entries.length - 1) {\n this.entries = this.entries.slice(0, this.index + 1);\n }\n this.entries.push(entry);\n this.index = this.entries.length - 1;\n\n this.writeWindowHistory('pushState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.go',\n payload: {\n page: entry.page,\n params: entry.params,\n query: entry.query,\n options: entry.options,\n },\n });\n return this.cloneEntry(entry);\n }\n\n goto(delta: number): NavigationEntry | null {\n return this.moveCursor(delta);\n }\n\n goBack(): NavigationEntry | null {\n return this.goto(-1);\n }\n\n goForward(): NavigationEntry | null {\n return this.goto(1);\n }\n\n subscribe(listener: NavigationListener): () => void {\n this.listeners.add(listener);\n listener(this.getCurrent());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private moveCursor(delta: number): NavigationEntry | null {\n if (delta === 0) {\n return this.getCurrent();\n }\n\n const nextIndex = this.index + delta;\n if (nextIndex < 0 || nextIndex >= this.entries.length) {\n return this.getCurrent();\n }\n\n this.index = nextIndex;\n const entry = this.entries[this.index];\n this.writeWindowHistory('replaceState', entry);\n this.notify();\n emitRendererTrace('info', {\n source: 'wood.navigation',\n type: 'navigation.goto',\n payload: {\n delta,\n page: entry.page,\n },\n });\n return this.cloneEntry(entry);\n }\n\n private notify(): void {\n const current = this.getCurrent();\n for (const listener of this.listeners) {\n listener(current);\n }\n }\n\n private createEntry(\n page: string,\n params: Record<string, string>,\n query: Record<string, string>,\n options: NavigationOptions,\n ): NavigationEntry {\n return {\n page,\n params: this.normalizeRecord(params),\n query: this.normalizeRecord(query),\n options: { ...options },\n };\n }\n\n private cloneEntry(entry: NavigationEntry): NavigationEntry {\n return {\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n }\n\n private normalizeRecord(input: Record<string, string>): Record<string, string> {\n const output: Record<string, string> = {};\n for (const [key, value] of Object.entries(input ?? {})) {\n output[String(key)] = String(value);\n }\n return output;\n }\n\n private writeWindowHistory(\n mode: 'pushState' | 'replaceState',\n entry: NavigationEntry,\n ): void {\n if (typeof window === 'undefined') {\n return;\n }\n\n const state: NavigationHistoryState = {\n [HISTORY_STATE_MARKER]: true,\n page: entry.page,\n params: { ...entry.params },\n query: { ...entry.query },\n options: { ...entry.options },\n };\n\n window.history[mode](state, '');\n }\n}\n\ntype GlobalWithNavigation = typeof globalThis & {\n [NAVIGATION_SYMBOL]?: Navigation;\n};\n\nfunction createNavigationInstance(): Navigation {\n return new Navigation();\n}\n\nexport function getNavigation(): Navigation {\n const globalObj = globalThis as GlobalWithNavigation;\n if (!globalObj[NAVIGATION_SYMBOL]) {\n globalObj[NAVIGATION_SYMBOL] = createNavigationInstance();\n }\n return globalObj[NAVIGATION_SYMBOL];\n}\n\nexport function goto(\n delta: number,\n): NavigationEntry | null {\n return getNavigation().goto(delta);\n}\n\nexport function go(\n page: string,\n params: Record<string, string> = {},\n query: Record<string, string> = {},\n options: NavigationOptions = {},\n): NavigationEntry {\n return getNavigation().go(page, params, query, options);\n}\n\nexport function goback(): NavigationEntry | null {\n return goto(-1);\n}\n\n/** Test-only helper to clear the navigation singleton. */\nexport function clearNavigationForTests(): void {\n const globalObj = globalThis as GlobalWithNavigation;\n delete globalObj[NAVIGATION_SYMBOL];\n}\n\n// Dev/test-only automation introspection surface (Axe AXE-WP-02).\n// Benign import cycle: automation_introspection uses getNavigation lazily.\n// NOTE: keep these re-exports single-line — the tsup extension-rewrite plugin\n// only rewrites single-line import/export specifiers.\nexport { installAutomationIntrospection, WOOD_AUTOMATION_GLOBAL_KEY } from './automation_introspection.js';\nexport type { WoodAutomationSnapshot, WoodAutomationEvent, WoodAutomationIntrospection, InstallAutomationIntrospectionOptions } from './automation_introspection.js';\nexport { computePreservedLayoutDepth } from './layout_preservation.js';\nexport type { LayoutStack } from './layout_preservation.js';\n"],"mappings":"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","names":[]}
|
package/dist/{middleware/__fixtures__/middleware/auth.cjs → navigation/layout_preservation.cjs}
RENAMED
|
@@ -16,29 +16,27 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
|
|
22
|
-
is_authenticated: () => is_authenticated
|
|
19
|
+
var layout_preservation_exports = {};
|
|
20
|
+
__export(layout_preservation_exports, {
|
|
21
|
+
computePreservedLayoutDepth: () => computePreservedLayoutDepth
|
|
23
22
|
});
|
|
24
|
-
module.exports = __toCommonJS(
|
|
25
|
-
function
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
module.exports = __toCommonJS(layout_preservation_exports);
|
|
24
|
+
function computePreservedLayoutDepth(from, to, options = {}) {
|
|
25
|
+
const maxDepth = Math.min(from.layoutPaths.length, to.layoutPaths.length);
|
|
26
|
+
let depth = 0;
|
|
27
|
+
for (let index = 0; index < maxDepth; index += 1) {
|
|
28
|
+
if (from.layoutPaths[index] !== to.layoutPaths[index]) break;
|
|
29
|
+
if (from.layoutControllers?.[index] !== to.layoutControllers?.[index]) break;
|
|
30
|
+
if (options.preserveLayouts === false) break;
|
|
31
|
+
if (options.preserveLayouts !== true && (from.layoutPreserve?.[index] !== true || to.layoutPreserve?.[index] !== true)) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
depth += 1;
|
|
29
35
|
}
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
function is_authenticated(ctx) {
|
|
33
|
-
const session = ctx.getSession();
|
|
34
|
-
if (!session?.user) {
|
|
35
|
-
throw new Error("Unauthorized: authentication required");
|
|
36
|
-
}
|
|
37
|
-
ctx.set("user", session.user);
|
|
36
|
+
return depth;
|
|
38
37
|
}
|
|
39
38
|
// Annotate the CommonJS export names for ESM import in node:
|
|
40
39
|
0 && (module.exports = {
|
|
41
|
-
|
|
42
|
-
is_authenticated
|
|
40
|
+
computePreservedLayoutDepth
|
|
43
41
|
});
|
|
44
|
-
//# sourceMappingURL=
|
|
42
|
+
//# sourceMappingURL=layout_preservation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/layout_preservation.ts"],"sourcesContent":["/**\n * Layout preservation depth — the single production rule for how many\n * leading layouts a navigation transition keeps mounted.\n *\n * One implementation, two callers: the renderer's NavigationShell (real\n * component transitions) and the Node frontend application slice\n * (`createFrontendSurface`). They must agree — a slice test that preserved\n * a layout the renderer rebuilds, or vice versa, would be evidence about a\n * rule that does not exist in production.\n *\n * The rule: walk the layout stacks in parallel and keep a layout only while\n * BOTH sides declare the same layout path, the same layout controller, and\n * both mark it `preserve: true`. `NavigationOptions.preserveLayouts`\n * overrides the declaration in both directions.\n */\nimport type { NavigationOptions } from './index.js';\n\n/** The layout stack of one view, in the shape both callers can supply. */\nexport interface LayoutStack {\n layoutPaths: readonly string[];\n layoutControllers?: ReadonlyArray<string | undefined>;\n layoutPreserve?: readonly boolean[];\n}\n\nexport function computePreservedLayoutDepth(\n from: LayoutStack,\n to: LayoutStack,\n options: NavigationOptions = {},\n): number {\n const maxDepth = Math.min(from.layoutPaths.length, to.layoutPaths.length);\n let depth = 0;\n for (let index = 0; index < maxDepth; index += 1) {\n if (from.layoutPaths[index] !== to.layoutPaths[index]) break;\n if (from.layoutControllers?.[index] !== to.layoutControllers?.[index]) break;\n if (options.preserveLayouts === false) break;\n if (\n options.preserveLayouts !== true &&\n (from.layoutPreserve?.[index] !== true || to.layoutPreserve?.[index] !== true)\n ) {\n break;\n }\n depth += 1;\n }\n return depth;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBO,SAAS,4BACd,MACA,IACA,UAA6B,CAAC,GACtB;AACR,QAAM,WAAW,KAAK,IAAI,KAAK,YAAY,QAAQ,GAAG,YAAY,MAAM;AACxE,MAAI,QAAQ;AACZ,WAAS,QAAQ,GAAG,QAAQ,UAAU,SAAS,GAAG;AAChD,QAAI,KAAK,YAAY,KAAK,MAAM,GAAG,YAAY,KAAK,EAAG;AACvD,QAAI,KAAK,oBAAoB,KAAK,MAAM,GAAG,oBAAoB,KAAK,EAAG;AACvE,QAAI,QAAQ,oBAAoB,MAAO;AACvC,QACE,QAAQ,oBAAoB,SAC3B,KAAK,iBAAiB,KAAK,MAAM,QAAQ,GAAG,iBAAiB,KAAK,MAAM,OACzE;AACA;AAAA,IACF;AACA,aAAS;AAAA,EACX;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function computePreservedLayoutDepth(from, to, options = {}) {
|
|
2
|
+
const maxDepth = Math.min(from.layoutPaths.length, to.layoutPaths.length);
|
|
3
|
+
let depth = 0;
|
|
4
|
+
for (let index = 0; index < maxDepth; index += 1) {
|
|
5
|
+
if (from.layoutPaths[index] !== to.layoutPaths[index]) break;
|
|
6
|
+
if (from.layoutControllers?.[index] !== to.layoutControllers?.[index]) break;
|
|
7
|
+
if (options.preserveLayouts === false) break;
|
|
8
|
+
if (options.preserveLayouts !== true && (from.layoutPreserve?.[index] !== true || to.layoutPreserve?.[index] !== true)) {
|
|
9
|
+
break;
|
|
10
|
+
}
|
|
11
|
+
depth += 1;
|
|
12
|
+
}
|
|
13
|
+
return depth;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
computePreservedLayoutDepth
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=layout_preservation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/navigation/layout_preservation.ts"],"sourcesContent":["/**\n * Layout preservation depth — the single production rule for how many\n * leading layouts a navigation transition keeps mounted.\n *\n * One implementation, two callers: the renderer's NavigationShell (real\n * component transitions) and the Node frontend application slice\n * (`createFrontendSurface`). They must agree — a slice test that preserved\n * a layout the renderer rebuilds, or vice versa, would be evidence about a\n * rule that does not exist in production.\n *\n * The rule: walk the layout stacks in parallel and keep a layout only while\n * BOTH sides declare the same layout path, the same layout controller, and\n * both mark it `preserve: true`. `NavigationOptions.preserveLayouts`\n * overrides the declaration in both directions.\n */\nimport type { NavigationOptions } from './index.js';\n\n/** The layout stack of one view, in the shape both callers can supply. */\nexport interface LayoutStack {\n layoutPaths: readonly string[];\n layoutControllers?: ReadonlyArray<string | undefined>;\n layoutPreserve?: readonly boolean[];\n}\n\nexport function computePreservedLayoutDepth(\n from: LayoutStack,\n to: LayoutStack,\n options: NavigationOptions = {},\n): number {\n const maxDepth = Math.min(from.layoutPaths.length, to.layoutPaths.length);\n let depth = 0;\n for (let index = 0; index < maxDepth; index += 1) {\n if (from.layoutPaths[index] !== to.layoutPaths[index]) break;\n if (from.layoutControllers?.[index] !== to.layoutControllers?.[index]) break;\n if (options.preserveLayouts === false) break;\n if (\n options.preserveLayouts !== true &&\n (from.layoutPreserve?.[index] !== true || to.layoutPreserve?.[index] !== true)\n ) {\n break;\n }\n depth += 1;\n }\n return depth;\n}\n"],"mappings":"AAwBO,SAAS,4BACd,MACA,IACA,UAA6B,CAAC,GACtB;AACR,QAAM,WAAW,KAAK,IAAI,KAAK,YAAY,QAAQ,GAAG,YAAY,MAAM;AACxE,MAAI,QAAQ;AACZ,WAAS,QAAQ,GAAG,QAAQ,UAAU,SAAS,GAAG;AAChD,QAAI,KAAK,YAAY,KAAK,MAAM,GAAG,YAAY,KAAK,EAAG;AACvD,QAAI,KAAK,oBAAoB,KAAK,MAAM,GAAG,oBAAoB,KAAK,EAAG;AACvE,QAAI,QAAQ,oBAAoB,MAAO;AACvC,QACE,QAAQ,oBAAoB,SAC3B,KAAK,iBAAiB,KAAK,MAAM,QAAQ,GAAG,iBAAiB,KAAK,MAAM,OACzE;AACA;AAAA,IACF;AACA,aAAS;AAAA,EACX;AACA,SAAO;AACT;","names":[]}
|
package/dist/parser/index.cjs
CHANGED
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var parser_exports = {};
|
|
20
20
|
__export(parser_exports, {
|
|
21
21
|
parseOperations: () => import_operations_parser.parseOperations,
|
|
22
|
+
parseOperationsDocument: () => import_operations_parser.parseOperationsDocument,
|
|
22
23
|
parseOperationsFile: () => import_operations_parser.parseOperationsFile,
|
|
23
24
|
parseViews: () => import_views_parser.parseViews,
|
|
24
25
|
parseViewsFile: () => import_views_parser.parseViewsFile
|
|
@@ -29,6 +30,7 @@ var import_views_parser = require("./views_parser.cjs");
|
|
|
29
30
|
// Annotate the CommonJS export names for ESM import in node:
|
|
30
31
|
0 && (module.exports = {
|
|
31
32
|
parseOperations,
|
|
33
|
+
parseOperationsDocument,
|
|
32
34
|
parseOperationsFile,
|
|
33
35
|
parseViews,
|
|
34
36
|
parseViewsFile
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/index.ts"],"sourcesContent":["export { parseOperations, parseOperationsFile } from './operations_parser.cjs';\nexport { parseViews, parseViewsFile } from './views_parser.cjs';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+
|
|
1
|
+
{"version":3,"sources":["../../src/parser/index.ts"],"sourcesContent":["export { parseOperations, parseOperationsDocument, parseOperationsFile } from './operations_parser.cjs';\nexport { parseViews, parseViewsFile } from './views_parser.cjs';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAA8E;AAC9E,0BAA2C;","names":[]}
|
package/dist/parser/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { parseOperations, parseOperationsFile } from './operations_parser.cjs';
|
|
1
|
+
export { parseOperations, parseOperationsDocument, parseOperationsFile } from './operations_parser.cjs';
|
|
2
2
|
export { parseViews, parseViewsFile } from './views_parser.cjs';
|
|
3
3
|
import '../types/operations.cjs';
|
|
4
4
|
import '../types/views.cjs';
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { parseOperations, parseOperationsFile } from './operations_parser.js';
|
|
1
|
+
export { parseOperations, parseOperationsDocument, parseOperationsFile } from './operations_parser.js';
|
|
2
2
|
export { parseViews, parseViewsFile } from './views_parser.js';
|
|
3
3
|
import '../types/operations.js';
|
|
4
4
|
import '../types/views.js';
|
package/dist/parser/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { parseOperations, parseOperationsFile } from "./operations_parser.js";
|
|
1
|
+
import { parseOperations, parseOperationsDocument, parseOperationsFile } from "./operations_parser.js";
|
|
2
2
|
import { parseViews, parseViewsFile } from "./views_parser.js";
|
|
3
3
|
export {
|
|
4
4
|
parseOperations,
|
|
5
|
+
parseOperationsDocument,
|
|
5
6
|
parseOperationsFile,
|
|
6
7
|
parseViews,
|
|
7
8
|
parseViewsFile
|
package/dist/parser/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/index.ts"],"sourcesContent":["export { parseOperations, parseOperationsFile } from './operations_parser.js';\nexport { parseViews, parseViewsFile } from './views_parser.js';\n"],"mappings":"AAAA,SAAS,iBAAiB,2BAA2B;
|
|
1
|
+
{"version":3,"sources":["../../src/parser/index.ts"],"sourcesContent":["export { parseOperations, parseOperationsDocument, parseOperationsFile } from './operations_parser.js';\nexport { parseViews, parseViewsFile } from './views_parser.js';\n"],"mappings":"AAAA,SAAS,iBAAiB,yBAAyB,2BAA2B;AAC9E,SAAS,YAAY,sBAAsB;","names":[]}
|
|
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
var operations_parser_exports = {};
|
|
30
30
|
__export(operations_parser_exports, {
|
|
31
31
|
parseOperations: () => parseOperations,
|
|
32
|
+
parseOperationsDocument: () => parseOperationsDocument,
|
|
32
33
|
parseOperationsFile: () => parseOperationsFile
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(operations_parser_exports);
|
|
@@ -36,6 +37,9 @@ var import_js_yaml = __toESM(require("js-yaml"), 1);
|
|
|
36
37
|
var import_fs = require("fs");
|
|
37
38
|
function parseOperations(yamlContent) {
|
|
38
39
|
const doc = import_js_yaml.default.load(yamlContent);
|
|
40
|
+
return parseOperationsDocument(doc);
|
|
41
|
+
}
|
|
42
|
+
function parseOperationsDocument(doc) {
|
|
39
43
|
const operations = /* @__PURE__ */ new Map();
|
|
40
44
|
if (doc === null || doc === void 0) {
|
|
41
45
|
throw new Error('Operations document is empty: expected a top-level "operations:" or "module:" mapping.');
|
|
@@ -84,6 +88,7 @@ function parseOperations(yamlContent) {
|
|
|
84
88
|
}
|
|
85
89
|
set(channel, {
|
|
86
90
|
channel,
|
|
91
|
+
module: moduleName,
|
|
87
92
|
controller: opEntry.controller ?? moduleController ?? "",
|
|
88
93
|
action: opEntry.action,
|
|
89
94
|
middleware,
|
|
@@ -110,6 +115,7 @@ function parseOperationsFile(filePath) {
|
|
|
110
115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
111
116
|
0 && (module.exports = {
|
|
112
117
|
parseOperations,
|
|
118
|
+
parseOperationsDocument,
|
|
113
119
|
parseOperationsFile
|
|
114
120
|
});
|
|
115
121
|
//# sourceMappingURL=operations_parser.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/operations_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type { OperationDef, RawOperationsDocument, RawModuleDef, RawOperationEntry, RawModuleOperationEntry } from '../types/index.cjs';\n\nexport function parseOperations(yamlContent: string): Map<string, OperationDef> {\n const doc = yaml.load(yamlContent) as RawOperationsDocument;\n const operations = new Map<string, OperationDef>();\n\n if (doc === null || doc === undefined) {\n throw new Error('Operations document is empty: expected a top-level \"operations:\" or \"module:\" mapping.');\n }\n if (typeof doc !== 'object' || Array.isArray(doc)) {\n throw new Error('Operations document must be a mapping with \"operations:\" and/or \"module:\" keys.');\n }\n if (!doc.operations && !doc.module) {\n throw new Error('Operations document defines no operations: expected an \"operations:\" or \"module:\" key.');\n }\n\n // A channel may only be defined once. Two operations sharing a channel would\n // silently overwrite each other (last-wins), dropping an RPC with no warning.\n const set = (channel: string, def: OperationDef): void => {\n if (operations.has(channel)) {\n throw new Error(`Duplicate operation channel \"${channel}\": each channel may only be defined once.`);\n }\n operations.set(channel, def);\n };\n\n // 1. Parse flat operations (top-level `operations:` key)\n if (doc.operations) {\n for (const [channel, entry] of Object.entries(doc.operations)) {\n set(channel, {\n channel,\n controller: entry.controller,\n action: entry.action,\n middleware: entry.middleware ?? [],\n inputSchema: entry.input,\n outputSchema: entry.output,\n });\n }\n }\n\n // 2. Parse module operations (`module:` key)\n if (doc.module) {\n for (const [moduleName, moduleDef] of Object.entries(doc.module)) {\n const moduleController = moduleDef.controller;\n const moduleMiddleware = moduleDef.middleware ?? [];\n\n if (moduleDef.operations === null || moduleDef.operations === undefined) {\n throw new Error(`Module \"${moduleName}\" has no \"operations:\" block.`);\n }\n if (typeof moduleDef.operations !== 'object' || Array.isArray(moduleDef.operations)) {\n throw new Error(`Module \"${moduleName}\" \"operations:\" must be a mapping of operation names.`);\n }\n\n for (const [opName, opEntry] of Object.entries(moduleDef.operations)) {\n const channel = `${moduleName}.${opName}`;\n\n // Middleware: if operation explicitly sets middleware to [] or null, clear inherited\n // Otherwise, prepend module middleware to operation middleware\n let middleware: string[];\n if (opEntry.middleware === null || (Array.isArray(opEntry.middleware) && opEntry.middleware.length === 0)) {\n middleware = [];\n } else {\n middleware = [...moduleMiddleware, ...(opEntry.middleware ?? [])];\n }\n\n set(channel, {\n channel,\n controller: opEntry.controller ?? moduleController ?? '',\n action: opEntry.action,\n middleware,\n inputSchema: opEntry.input,\n outputSchema: opEntry.output,\n });\n }\n }\n }\n\n // 3. Validate: every operation must have a controller and action\n for (const [channel, op] of operations) {\n if (!op.controller) {\n throw new Error(`Operation \"${channel}\" has no controller defined`);\n }\n if (!op.action) {\n throw new Error(`Operation \"${channel}\" has no action defined`);\n }\n }\n\n return operations;\n}\n\n/** Parse from file path */\nexport function parseOperationsFile(filePath: string): Map<string, OperationDef> {\n const content = readFileSync(filePath, 'utf-8');\n return parseOperations(content);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAiB;AACjB,gBAA6B;AAGtB,SAAS,gBAAgB,aAAgD;AAC9E,QAAM,MAAM,eAAAA,QAAK,KAAK,WAAW;AACjC,QAAM,aAAa,oBAAI,IAA0B;AAEjD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AACA,MAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,MAAI,CAAC,IAAI,cAAc,CAAC,IAAI,QAAQ;AAClC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AAIA,QAAM,MAAM,CAAC,SAAiB,QAA4B;AACxD,QAAI,WAAW,IAAI,OAAO,GAAG;AAC3B,YAAM,IAAI,MAAM,gCAAgC,OAAO,2CAA2C;AAAA,IACpG;AACA,eAAW,IAAI,SAAS,GAAG;AAAA,EAC7B;AAGA,MAAI,IAAI,YAAY;AAClB,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,IAAI,UAAU,GAAG;AAC7D,UAAI,SAAS;AAAA,QACX;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM,cAAc,CAAC;AAAA,QACjC,aAAa,MAAM;AAAA,QACnB,cAAc,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,IAAI,QAAQ;AACd,eAAW,CAAC,YAAY,SAAS,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG;AAChE,YAAM,mBAAmB,UAAU;AACnC,YAAM,mBAAmB,UAAU,cAAc,CAAC;AAElD,UAAI,UAAU,eAAe,QAAQ,UAAU,eAAe,QAAW;AACvE,cAAM,IAAI,MAAM,WAAW,UAAU,+BAA+B;AAAA,MACtE;AACA,UAAI,OAAO,UAAU,eAAe,YAAY,MAAM,QAAQ,UAAU,UAAU,GAAG;AACnF,cAAM,IAAI,MAAM,WAAW,UAAU,uDAAuD;AAAA,MAC9F;AAEA,iBAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AACpE,cAAM,UAAU,GAAG,UAAU,IAAI,MAAM;AAIvC,YAAI;AACJ,YAAI,QAAQ,eAAe,QAAS,MAAM,QAAQ,QAAQ,UAAU,KAAK,QAAQ,WAAW,WAAW,GAAI;AACzG,uBAAa,CAAC;AAAA,QAChB,OAAO;AACL,uBAAa,CAAC,GAAG,kBAAkB,GAAI,QAAQ,cAAc,CAAC,CAAE;AAAA,QAClE;AAEA,YAAI,SAAS;AAAA,UACX;AAAA,UACA,YAAY,QAAQ,cAAc,oBAAoB;AAAA,UACtD,QAAQ,QAAQ;AAAA,UAChB;AAAA,UACA,aAAa,QAAQ;AAAA,UACrB,cAAc,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,SAAS,EAAE,KAAK,YAAY;AACtC,QAAI,CAAC,GAAG,YAAY;AAClB,YAAM,IAAI,MAAM,cAAc,OAAO,6BAA6B;AAAA,IACpE;AACA,QAAI,CAAC,GAAG,QAAQ;AACd,YAAM,IAAI,MAAM,cAAc,OAAO,yBAAyB;AAAA,IAChE;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,UAA6C;AAC/E,QAAM,cAAU,wBAAa,UAAU,OAAO;AAC9C,SAAO,gBAAgB,OAAO;AAChC;","names":["yaml"]}
|
|
1
|
+
{"version":3,"sources":["../../src/parser/operations_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type { OperationDef, RawOperationsDocument, RawModuleDef, RawOperationEntry, RawModuleOperationEntry } from '../types/index.cjs';\n\nexport function parseOperations(yamlContent: string): Map<string, OperationDef> {\n const doc = yaml.load(yamlContent) as RawOperationsDocument;\n return parseOperationsDocument(doc);\n}\n\n/**\n * Parse an already-loaded operations document (the object form of the YAML).\n * This is the production parser; parseOperations/parseOperationsFile are\n * thin loaders over it, and test environments select from the same\n * identities it produces.\n */\nexport function parseOperationsDocument(doc: RawOperationsDocument): Map<string, OperationDef> {\n const operations = new Map<string, OperationDef>();\n\n if (doc === null || doc === undefined) {\n throw new Error('Operations document is empty: expected a top-level \"operations:\" or \"module:\" mapping.');\n }\n if (typeof doc !== 'object' || Array.isArray(doc)) {\n throw new Error('Operations document must be a mapping with \"operations:\" and/or \"module:\" keys.');\n }\n if (!doc.operations && !doc.module) {\n throw new Error('Operations document defines no operations: expected an \"operations:\" or \"module:\" key.');\n }\n\n // A channel may only be defined once. Two operations sharing a channel would\n // silently overwrite each other (last-wins), dropping an RPC with no warning.\n const set = (channel: string, def: OperationDef): void => {\n if (operations.has(channel)) {\n throw new Error(`Duplicate operation channel \"${channel}\": each channel may only be defined once.`);\n }\n operations.set(channel, def);\n };\n\n // 1. Parse flat operations (top-level `operations:` key)\n if (doc.operations) {\n for (const [channel, entry] of Object.entries(doc.operations)) {\n set(channel, {\n channel,\n controller: entry.controller,\n action: entry.action,\n middleware: entry.middleware ?? [],\n inputSchema: entry.input,\n outputSchema: entry.output,\n });\n }\n }\n\n // 2. Parse module operations (`module:` key)\n if (doc.module) {\n for (const [moduleName, moduleDef] of Object.entries(doc.module)) {\n const moduleController = moduleDef.controller;\n const moduleMiddleware = moduleDef.middleware ?? [];\n\n if (moduleDef.operations === null || moduleDef.operations === undefined) {\n throw new Error(`Module \"${moduleName}\" has no \"operations:\" block.`);\n }\n if (typeof moduleDef.operations !== 'object' || Array.isArray(moduleDef.operations)) {\n throw new Error(`Module \"${moduleName}\" \"operations:\" must be a mapping of operation names.`);\n }\n\n for (const [opName, opEntry] of Object.entries(moduleDef.operations)) {\n const channel = `${moduleName}.${opName}`;\n\n // Middleware: if operation explicitly sets middleware to [] or null, clear inherited\n // Otherwise, prepend module middleware to operation middleware\n let middleware: string[];\n if (opEntry.middleware === null || (Array.isArray(opEntry.middleware) && opEntry.middleware.length === 0)) {\n middleware = [];\n } else {\n middleware = [...moduleMiddleware, ...(opEntry.middleware ?? [])];\n }\n\n set(channel, {\n channel,\n module: moduleName,\n controller: opEntry.controller ?? moduleController ?? '',\n action: opEntry.action,\n middleware,\n inputSchema: opEntry.input,\n outputSchema: opEntry.output,\n });\n }\n }\n }\n\n // 3. Validate: every operation must have a controller and action\n for (const [channel, op] of operations) {\n if (!op.controller) {\n throw new Error(`Operation \"${channel}\" has no controller defined`);\n }\n if (!op.action) {\n throw new Error(`Operation \"${channel}\" has no action defined`);\n }\n }\n\n return operations;\n}\n\n/** Parse from file path */\nexport function parseOperationsFile(filePath: string): Map<string, OperationDef> {\n const content = readFileSync(filePath, 'utf-8');\n return parseOperations(content);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAiB;AACjB,gBAA6B;AAGtB,SAAS,gBAAgB,aAAgD;AAC9E,QAAM,MAAM,eAAAA,QAAK,KAAK,WAAW;AACjC,SAAO,wBAAwB,GAAG;AACpC;AAQO,SAAS,wBAAwB,KAAuD;AAC7F,QAAM,aAAa,oBAAI,IAA0B;AAEjD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AACA,MAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,MAAI,CAAC,IAAI,cAAc,CAAC,IAAI,QAAQ;AAClC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AAIA,QAAM,MAAM,CAAC,SAAiB,QAA4B;AACxD,QAAI,WAAW,IAAI,OAAO,GAAG;AAC3B,YAAM,IAAI,MAAM,gCAAgC,OAAO,2CAA2C;AAAA,IACpG;AACA,eAAW,IAAI,SAAS,GAAG;AAAA,EAC7B;AAGA,MAAI,IAAI,YAAY;AAClB,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,IAAI,UAAU,GAAG;AAC7D,UAAI,SAAS;AAAA,QACX;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM,cAAc,CAAC;AAAA,QACjC,aAAa,MAAM;AAAA,QACnB,cAAc,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,IAAI,QAAQ;AACd,eAAW,CAAC,YAAY,SAAS,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG;AAChE,YAAM,mBAAmB,UAAU;AACnC,YAAM,mBAAmB,UAAU,cAAc,CAAC;AAElD,UAAI,UAAU,eAAe,QAAQ,UAAU,eAAe,QAAW;AACvE,cAAM,IAAI,MAAM,WAAW,UAAU,+BAA+B;AAAA,MACtE;AACA,UAAI,OAAO,UAAU,eAAe,YAAY,MAAM,QAAQ,UAAU,UAAU,GAAG;AACnF,cAAM,IAAI,MAAM,WAAW,UAAU,uDAAuD;AAAA,MAC9F;AAEA,iBAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AACpE,cAAM,UAAU,GAAG,UAAU,IAAI,MAAM;AAIvC,YAAI;AACJ,YAAI,QAAQ,eAAe,QAAS,MAAM,QAAQ,QAAQ,UAAU,KAAK,QAAQ,WAAW,WAAW,GAAI;AACzG,uBAAa,CAAC;AAAA,QAChB,OAAO;AACL,uBAAa,CAAC,GAAG,kBAAkB,GAAI,QAAQ,cAAc,CAAC,CAAE;AAAA,QAClE;AAEA,YAAI,SAAS;AAAA,UACX;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,QAAQ,cAAc,oBAAoB;AAAA,UACtD,QAAQ,QAAQ;AAAA,UAChB;AAAA,UACA,aAAa,QAAQ;AAAA,UACrB,cAAc,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,SAAS,EAAE,KAAK,YAAY;AACtC,QAAI,CAAC,GAAG,YAAY;AAClB,YAAM,IAAI,MAAM,cAAc,OAAO,6BAA6B;AAAA,IACpE;AACA,QAAI,CAAC,GAAG,QAAQ;AACd,YAAM,IAAI,MAAM,cAAc,OAAO,yBAAyB;AAAA,IAChE;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,UAA6C;AAC/E,QAAM,cAAU,wBAAa,UAAU,OAAO;AAC9C,SAAO,gBAAgB,OAAO;AAChC;","names":["yaml"]}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import { OperationDef } from '../types/operations.cjs';
|
|
1
|
+
import { OperationDef, RawOperationsDocument } from '../types/operations.cjs';
|
|
2
2
|
|
|
3
3
|
declare function parseOperations(yamlContent: string): Map<string, OperationDef>;
|
|
4
|
+
/**
|
|
5
|
+
* Parse an already-loaded operations document (the object form of the YAML).
|
|
6
|
+
* This is the production parser; parseOperations/parseOperationsFile are
|
|
7
|
+
* thin loaders over it, and test environments select from the same
|
|
8
|
+
* identities it produces.
|
|
9
|
+
*/
|
|
10
|
+
declare function parseOperationsDocument(doc: RawOperationsDocument): Map<string, OperationDef>;
|
|
4
11
|
/** Parse from file path */
|
|
5
12
|
declare function parseOperationsFile(filePath: string): Map<string, OperationDef>;
|
|
6
13
|
|
|
7
|
-
export { parseOperations, parseOperationsFile };
|
|
14
|
+
export { parseOperations, parseOperationsDocument, parseOperationsFile };
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import { OperationDef } from '../types/operations.js';
|
|
1
|
+
import { OperationDef, RawOperationsDocument } from '../types/operations.js';
|
|
2
2
|
|
|
3
3
|
declare function parseOperations(yamlContent: string): Map<string, OperationDef>;
|
|
4
|
+
/**
|
|
5
|
+
* Parse an already-loaded operations document (the object form of the YAML).
|
|
6
|
+
* This is the production parser; parseOperations/parseOperationsFile are
|
|
7
|
+
* thin loaders over it, and test environments select from the same
|
|
8
|
+
* identities it produces.
|
|
9
|
+
*/
|
|
10
|
+
declare function parseOperationsDocument(doc: RawOperationsDocument): Map<string, OperationDef>;
|
|
4
11
|
/** Parse from file path */
|
|
5
12
|
declare function parseOperationsFile(filePath: string): Map<string, OperationDef>;
|
|
6
13
|
|
|
7
|
-
export { parseOperations, parseOperationsFile };
|
|
14
|
+
export { parseOperations, parseOperationsDocument, parseOperationsFile };
|
|
@@ -2,6 +2,9 @@ import yaml from "js-yaml";
|
|
|
2
2
|
import { readFileSync } from "fs";
|
|
3
3
|
function parseOperations(yamlContent) {
|
|
4
4
|
const doc = yaml.load(yamlContent);
|
|
5
|
+
return parseOperationsDocument(doc);
|
|
6
|
+
}
|
|
7
|
+
function parseOperationsDocument(doc) {
|
|
5
8
|
const operations = /* @__PURE__ */ new Map();
|
|
6
9
|
if (doc === null || doc === void 0) {
|
|
7
10
|
throw new Error('Operations document is empty: expected a top-level "operations:" or "module:" mapping.');
|
|
@@ -50,6 +53,7 @@ function parseOperations(yamlContent) {
|
|
|
50
53
|
}
|
|
51
54
|
set(channel, {
|
|
52
55
|
channel,
|
|
56
|
+
module: moduleName,
|
|
53
57
|
controller: opEntry.controller ?? moduleController ?? "",
|
|
54
58
|
action: opEntry.action,
|
|
55
59
|
middleware,
|
|
@@ -75,6 +79,7 @@ function parseOperationsFile(filePath) {
|
|
|
75
79
|
}
|
|
76
80
|
export {
|
|
77
81
|
parseOperations,
|
|
82
|
+
parseOperationsDocument,
|
|
78
83
|
parseOperationsFile
|
|
79
84
|
};
|
|
80
85
|
//# sourceMappingURL=operations_parser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/operations_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type { OperationDef, RawOperationsDocument, RawModuleDef, RawOperationEntry, RawModuleOperationEntry } from '../types/index.js';\n\nexport function parseOperations(yamlContent: string): Map<string, OperationDef> {\n const doc = yaml.load(yamlContent) as RawOperationsDocument;\n const operations = new Map<string, OperationDef>();\n\n if (doc === null || doc === undefined) {\n throw new Error('Operations document is empty: expected a top-level \"operations:\" or \"module:\" mapping.');\n }\n if (typeof doc !== 'object' || Array.isArray(doc)) {\n throw new Error('Operations document must be a mapping with \"operations:\" and/or \"module:\" keys.');\n }\n if (!doc.operations && !doc.module) {\n throw new Error('Operations document defines no operations: expected an \"operations:\" or \"module:\" key.');\n }\n\n // A channel may only be defined once. Two operations sharing a channel would\n // silently overwrite each other (last-wins), dropping an RPC with no warning.\n const set = (channel: string, def: OperationDef): void => {\n if (operations.has(channel)) {\n throw new Error(`Duplicate operation channel \"${channel}\": each channel may only be defined once.`);\n }\n operations.set(channel, def);\n };\n\n // 1. Parse flat operations (top-level `operations:` key)\n if (doc.operations) {\n for (const [channel, entry] of Object.entries(doc.operations)) {\n set(channel, {\n channel,\n controller: entry.controller,\n action: entry.action,\n middleware: entry.middleware ?? [],\n inputSchema: entry.input,\n outputSchema: entry.output,\n });\n }\n }\n\n // 2. Parse module operations (`module:` key)\n if (doc.module) {\n for (const [moduleName, moduleDef] of Object.entries(doc.module)) {\n const moduleController = moduleDef.controller;\n const moduleMiddleware = moduleDef.middleware ?? [];\n\n if (moduleDef.operations === null || moduleDef.operations === undefined) {\n throw new Error(`Module \"${moduleName}\" has no \"operations:\" block.`);\n }\n if (typeof moduleDef.operations !== 'object' || Array.isArray(moduleDef.operations)) {\n throw new Error(`Module \"${moduleName}\" \"operations:\" must be a mapping of operation names.`);\n }\n\n for (const [opName, opEntry] of Object.entries(moduleDef.operations)) {\n const channel = `${moduleName}.${opName}`;\n\n // Middleware: if operation explicitly sets middleware to [] or null, clear inherited\n // Otherwise, prepend module middleware to operation middleware\n let middleware: string[];\n if (opEntry.middleware === null || (Array.isArray(opEntry.middleware) && opEntry.middleware.length === 0)) {\n middleware = [];\n } else {\n middleware = [...moduleMiddleware, ...(opEntry.middleware ?? [])];\n }\n\n set(channel, {\n channel,\n controller: opEntry.controller ?? moduleController ?? '',\n action: opEntry.action,\n middleware,\n inputSchema: opEntry.input,\n outputSchema: opEntry.output,\n });\n }\n }\n }\n\n // 3. Validate: every operation must have a controller and action\n for (const [channel, op] of operations) {\n if (!op.controller) {\n throw new Error(`Operation \"${channel}\" has no controller defined`);\n }\n if (!op.action) {\n throw new Error(`Operation \"${channel}\" has no action defined`);\n }\n }\n\n return operations;\n}\n\n/** Parse from file path */\nexport function parseOperationsFile(filePath: string): Map<string, OperationDef> {\n const content = readFileSync(filePath, 'utf-8');\n return parseOperations(content);\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAGtB,SAAS,gBAAgB,aAAgD;AAC9E,QAAM,MAAM,KAAK,KAAK,WAAW;AACjC,QAAM,aAAa,oBAAI,IAA0B;AAEjD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AACA,MAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,MAAI,CAAC,IAAI,cAAc,CAAC,IAAI,QAAQ;AAClC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AAIA,QAAM,MAAM,CAAC,SAAiB,QAA4B;AACxD,QAAI,WAAW,IAAI,OAAO,GAAG;AAC3B,YAAM,IAAI,MAAM,gCAAgC,OAAO,2CAA2C;AAAA,IACpG;AACA,eAAW,IAAI,SAAS,GAAG;AAAA,EAC7B;AAGA,MAAI,IAAI,YAAY;AAClB,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,IAAI,UAAU,GAAG;AAC7D,UAAI,SAAS;AAAA,QACX;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM,cAAc,CAAC;AAAA,QACjC,aAAa,MAAM;AAAA,QACnB,cAAc,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,IAAI,QAAQ;AACd,eAAW,CAAC,YAAY,SAAS,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG;AAChE,YAAM,mBAAmB,UAAU;AACnC,YAAM,mBAAmB,UAAU,cAAc,CAAC;AAElD,UAAI,UAAU,eAAe,QAAQ,UAAU,eAAe,QAAW;AACvE,cAAM,IAAI,MAAM,WAAW,UAAU,+BAA+B;AAAA,MACtE;AACA,UAAI,OAAO,UAAU,eAAe,YAAY,MAAM,QAAQ,UAAU,UAAU,GAAG;AACnF,cAAM,IAAI,MAAM,WAAW,UAAU,uDAAuD;AAAA,MAC9F;AAEA,iBAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AACpE,cAAM,UAAU,GAAG,UAAU,IAAI,MAAM;AAIvC,YAAI;AACJ,YAAI,QAAQ,eAAe,QAAS,MAAM,QAAQ,QAAQ,UAAU,KAAK,QAAQ,WAAW,WAAW,GAAI;AACzG,uBAAa,CAAC;AAAA,QAChB,OAAO;AACL,uBAAa,CAAC,GAAG,kBAAkB,GAAI,QAAQ,cAAc,CAAC,CAAE;AAAA,QAClE;AAEA,YAAI,SAAS;AAAA,UACX;AAAA,UACA,YAAY,QAAQ,cAAc,oBAAoB;AAAA,UACtD,QAAQ,QAAQ;AAAA,UAChB;AAAA,UACA,aAAa,QAAQ;AAAA,UACrB,cAAc,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,SAAS,EAAE,KAAK,YAAY;AACtC,QAAI,CAAC,GAAG,YAAY;AAClB,YAAM,IAAI,MAAM,cAAc,OAAO,6BAA6B;AAAA,IACpE;AACA,QAAI,CAAC,GAAG,QAAQ;AACd,YAAM,IAAI,MAAM,cAAc,OAAO,yBAAyB;AAAA,IAChE;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,UAA6C;AAC/E,QAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,SAAO,gBAAgB,OAAO;AAChC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/parser/operations_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type { OperationDef, RawOperationsDocument, RawModuleDef, RawOperationEntry, RawModuleOperationEntry } from '../types/index.js';\n\nexport function parseOperations(yamlContent: string): Map<string, OperationDef> {\n const doc = yaml.load(yamlContent) as RawOperationsDocument;\n return parseOperationsDocument(doc);\n}\n\n/**\n * Parse an already-loaded operations document (the object form of the YAML).\n * This is the production parser; parseOperations/parseOperationsFile are\n * thin loaders over it, and test environments select from the same\n * identities it produces.\n */\nexport function parseOperationsDocument(doc: RawOperationsDocument): Map<string, OperationDef> {\n const operations = new Map<string, OperationDef>();\n\n if (doc === null || doc === undefined) {\n throw new Error('Operations document is empty: expected a top-level \"operations:\" or \"module:\" mapping.');\n }\n if (typeof doc !== 'object' || Array.isArray(doc)) {\n throw new Error('Operations document must be a mapping with \"operations:\" and/or \"module:\" keys.');\n }\n if (!doc.operations && !doc.module) {\n throw new Error('Operations document defines no operations: expected an \"operations:\" or \"module:\" key.');\n }\n\n // A channel may only be defined once. Two operations sharing a channel would\n // silently overwrite each other (last-wins), dropping an RPC with no warning.\n const set = (channel: string, def: OperationDef): void => {\n if (operations.has(channel)) {\n throw new Error(`Duplicate operation channel \"${channel}\": each channel may only be defined once.`);\n }\n operations.set(channel, def);\n };\n\n // 1. Parse flat operations (top-level `operations:` key)\n if (doc.operations) {\n for (const [channel, entry] of Object.entries(doc.operations)) {\n set(channel, {\n channel,\n controller: entry.controller,\n action: entry.action,\n middleware: entry.middleware ?? [],\n inputSchema: entry.input,\n outputSchema: entry.output,\n });\n }\n }\n\n // 2. Parse module operations (`module:` key)\n if (doc.module) {\n for (const [moduleName, moduleDef] of Object.entries(doc.module)) {\n const moduleController = moduleDef.controller;\n const moduleMiddleware = moduleDef.middleware ?? [];\n\n if (moduleDef.operations === null || moduleDef.operations === undefined) {\n throw new Error(`Module \"${moduleName}\" has no \"operations:\" block.`);\n }\n if (typeof moduleDef.operations !== 'object' || Array.isArray(moduleDef.operations)) {\n throw new Error(`Module \"${moduleName}\" \"operations:\" must be a mapping of operation names.`);\n }\n\n for (const [opName, opEntry] of Object.entries(moduleDef.operations)) {\n const channel = `${moduleName}.${opName}`;\n\n // Middleware: if operation explicitly sets middleware to [] or null, clear inherited\n // Otherwise, prepend module middleware to operation middleware\n let middleware: string[];\n if (opEntry.middleware === null || (Array.isArray(opEntry.middleware) && opEntry.middleware.length === 0)) {\n middleware = [];\n } else {\n middleware = [...moduleMiddleware, ...(opEntry.middleware ?? [])];\n }\n\n set(channel, {\n channel,\n module: moduleName,\n controller: opEntry.controller ?? moduleController ?? '',\n action: opEntry.action,\n middleware,\n inputSchema: opEntry.input,\n outputSchema: opEntry.output,\n });\n }\n }\n }\n\n // 3. Validate: every operation must have a controller and action\n for (const [channel, op] of operations) {\n if (!op.controller) {\n throw new Error(`Operation \"${channel}\" has no controller defined`);\n }\n if (!op.action) {\n throw new Error(`Operation \"${channel}\" has no action defined`);\n }\n }\n\n return operations;\n}\n\n/** Parse from file path */\nexport function parseOperationsFile(filePath: string): Map<string, OperationDef> {\n const content = readFileSync(filePath, 'utf-8');\n return parseOperations(content);\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAGtB,SAAS,gBAAgB,aAAgD;AAC9E,QAAM,MAAM,KAAK,KAAK,WAAW;AACjC,SAAO,wBAAwB,GAAG;AACpC;AAQO,SAAS,wBAAwB,KAAuD;AAC7F,QAAM,aAAa,oBAAI,IAA0B;AAEjD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AACA,MAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjD,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,MAAI,CAAC,IAAI,cAAc,CAAC,IAAI,QAAQ;AAClC,UAAM,IAAI,MAAM,wFAAwF;AAAA,EAC1G;AAIA,QAAM,MAAM,CAAC,SAAiB,QAA4B;AACxD,QAAI,WAAW,IAAI,OAAO,GAAG;AAC3B,YAAM,IAAI,MAAM,gCAAgC,OAAO,2CAA2C;AAAA,IACpG;AACA,eAAW,IAAI,SAAS,GAAG;AAAA,EAC7B;AAGA,MAAI,IAAI,YAAY;AAClB,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,IAAI,UAAU,GAAG;AAC7D,UAAI,SAAS;AAAA,QACX;AAAA,QACA,YAAY,MAAM;AAAA,QAClB,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM,cAAc,CAAC;AAAA,QACjC,aAAa,MAAM;AAAA,QACnB,cAAc,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,IAAI,QAAQ;AACd,eAAW,CAAC,YAAY,SAAS,KAAK,OAAO,QAAQ,IAAI,MAAM,GAAG;AAChE,YAAM,mBAAmB,UAAU;AACnC,YAAM,mBAAmB,UAAU,cAAc,CAAC;AAElD,UAAI,UAAU,eAAe,QAAQ,UAAU,eAAe,QAAW;AACvE,cAAM,IAAI,MAAM,WAAW,UAAU,+BAA+B;AAAA,MACtE;AACA,UAAI,OAAO,UAAU,eAAe,YAAY,MAAM,QAAQ,UAAU,UAAU,GAAG;AACnF,cAAM,IAAI,MAAM,WAAW,UAAU,uDAAuD;AAAA,MAC9F;AAEA,iBAAW,CAAC,QAAQ,OAAO,KAAK,OAAO,QAAQ,UAAU,UAAU,GAAG;AACpE,cAAM,UAAU,GAAG,UAAU,IAAI,MAAM;AAIvC,YAAI;AACJ,YAAI,QAAQ,eAAe,QAAS,MAAM,QAAQ,QAAQ,UAAU,KAAK,QAAQ,WAAW,WAAW,GAAI;AACzG,uBAAa,CAAC;AAAA,QAChB,OAAO;AACL,uBAAa,CAAC,GAAG,kBAAkB,GAAI,QAAQ,cAAc,CAAC,CAAE;AAAA,QAClE;AAEA,YAAI,SAAS;AAAA,UACX;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,QAAQ,cAAc,oBAAoB;AAAA,UACtD,QAAQ,QAAQ;AAAA,UAChB;AAAA,UACA,aAAa,QAAQ;AAAA,UACrB,cAAc,QAAQ;AAAA,QACxB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,aAAW,CAAC,SAAS,EAAE,KAAK,YAAY;AACtC,QAAI,CAAC,GAAG,YAAY;AAClB,YAAM,IAAI,MAAM,cAAc,OAAO,6BAA6B;AAAA,IACpE;AACA,QAAI,CAAC,GAAG,QAAQ;AACd,YAAM,IAAI,MAAM,cAAc,OAAO,yBAAyB;AAAA,IAChE;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,oBAAoB,UAA6C;AAC/E,QAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,SAAO,gBAAgB,OAAO;AAChC;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction validatePreserveValue(preserve: unknown, contextLabel: string): boolean {\n if (preserve === undefined) return false;\n if (typeof preserve !== 'boolean') {\n throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);\n }\n return preserve;\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string; preserve: boolean } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path, preserve: false };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n preserve: validatePreserveValue(layoutObj.preserve, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n inheritedPreserve?: boolean;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n if (normalizedLayouts.inheritedPreserve) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets layout preserve but has no layout views. Define at least one layout entry.`,\n );\n }\n parsedLayouts[0].preserve = true;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n if (layoutPreserve.some((value) => value)) {\n viewDef.layoutPreserve = layoutPreserve;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":"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","names":["yaml"]}
|
|
1
|
+
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types/index.cjs';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction validatePreserveValue(preserve: unknown, contextLabel: string): boolean {\n if (preserve === undefined) return false;\n if (typeof preserve !== 'boolean') {\n throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);\n }\n return preserve;\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string; preserve: boolean } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path, preserve: false };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n preserve: validatePreserveValue(layoutObj.preserve, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n inheritedPreserve?: boolean;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n if (normalizedLayouts.inheritedPreserve) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets layout preserve but has no layout views. Define at least one layout entry.`,\n );\n }\n parsedLayouts[0].preserve = true;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n if (layoutPreserve.some((value) => value)) {\n viewDef.layoutPreserve = layoutPreserve;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAiB;AACjB,gBAA6B;AAU7B,SAAS,wBAAwB,YAAqB,cAA0C;AAC9F,MAAI,eAAe,OAAW,QAAO;AACrC,MAAI,OAAO,eAAe,YAAY,WAAW,KAAK,EAAE,WAAW,GAAG;AACpE,UAAM,IAAI,MAAM,GAAG,YAAY,uDAAuD;AAAA,EACxF;AACA,SAAO,WAAW,KAAK;AACzB;AAEA,SAAS,sBAAsB,UAAmB,cAA+B;AAC/E,MAAI,aAAa,OAAW,QAAO;AACnC,MAAI,OAAO,aAAa,WAAW;AACjC,UAAM,IAAI,MAAM,GAAG,YAAY,4CAA4C;AAAA,EAC7E;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAuB,SAAiB,OAAyE;AACzI,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,OAAO,MAAM,KAAK;AACxB,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,SAAS,OAAO,uCAAuC,KAAK,qCAAqC;AAAA,IACnH;AACA,WAAO,EAAE,MAAM,UAAU,MAAM;AAAA,EACjC;AAEA,QAAM,YAAY;AAClB,QAAM,aAAa,UAAU,QAAQ,UAAU,QAAQ,UAAU;AACjE,MAAI,OAAO,eAAe,YAAY,WAAW,KAAK,EAAE,WAAW,GAAG;AACpE,UAAM,IAAI;AAAA,MACR,SAAS,OAAO,wCAAwC,KAAK;AAAA,IAE/D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,WAAW,KAAK;AAAA,IACtB,YAAY,wBAAwB,UAAU,YAAY,SAAS,OAAO,YAAY,KAAK,GAAG;AAAA,IAC9F,UAAU,sBAAsB,UAAU,UAAU,SAAS,OAAO,YAAY,KAAK,GAAG;AAAA,EAC1F;AACF;AAEA,SAAS,mBAAmB,OAA8C;AACxE,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,SAAO,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO;AAC5D;AAEA,SAAS,oBAAoB,OAA+C;AAC1E,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,SACE,OAAO,UAAU,eAAe,KAAK,OAAO,MAAM,KAClD,OAAO,UAAU,eAAe,KAAK,OAAO,MAAM,KAClD,OAAO,UAAU,eAAe,KAAK,OAAO,QAAQ,KACpD,OAAO,UAAU,eAAe,KAAK,OAAO,YAAY;AAE5D;AAEA,SAAS,iBAAiB,WAAwC,SAIhE;AACA,MAAI,cAAc,QAAW;AAC3B,WAAO,EAAE,SAAS,CAAC,EAAE;AAAA,EACvB;AAEA,MAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AAEA,MAAI,OAAO,cAAc,UAAU;AACjC,WAAO,EAAE,SAAS,CAAC,SAAS,EAAE;AAAA,EAChC;AAEA,MAAI,mBAAmB,SAAS,GAAG;AACjC,QAAI,CAAC,MAAM,QAAQ,UAAU,KAAK,GAAG;AACnC,YAAM,IAAI,MAAM,SAAS,OAAO,wDAAwD;AAAA,IAC1F;AAEA,WAAO;AAAA,MACL,SAAS,UAAU;AAAA,MACnB,qBAAqB,wBAAwB,UAAU,YAAY,SAAS,OAAO,UAAU;AAAA,MAC7F,mBAAmB,sBAAsB,UAAU,UAAU,SAAS,OAAO,UAAU;AAAA,IACzF;AAAA,EACF;AAEA,MAAI,oBAAoB,SAAS,GAAG;AAClC,WAAO,EAAE,SAAS,CAAC,SAAS,EAAE;AAAA,EAChC;AAEA,QAAM,IAAI;AAAA,IACR,SAAS,OAAO;AAAA,EAClB;AACF;AAMO,SAAS,WAAW,aAAgC;AA7G3D;AA8GE,QAAM,MAAM,eAAAA,QAAK,KAAK,WAAW;AACjC,QAAM,QAAmB,CAAC;AAI1B,MAAI,IAAI,MAAM;AACZ,eAAW,CAAC,SAAS,KAAK,KAAK,OAAO,QAAQ,IAAI,IAAI,GAAG;AACvD,YAAM,WAAW,QAAQ,MAAM,GAAG;AAClC,YAAM,aAAa,SAAS,CAAC;AAC7B,YAAM,WAAW,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAE3C,YAAM,oBAAoB,iBAAiB,MAAM,QAAQ,OAAO;AAChE,YAAM,gBAAgB,kBAAkB,QAAQ;AAAA,QAAI,CAAC,aAAa,UAChE,iBAAiB,aAAa,SAAS,KAAK;AAAA,MAC9C;AACA,UAAI,kBAAkB,qBAAqB;AACzC,YAAI,cAAc,WAAW,GAAG;AAC9B,gBAAM,IAAI;AAAA,YACR,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AAEA,cAAM,wBAAwB,cAAc,CAAC,EAAE;AAC/C,YAAI,yBAAyB,0BAA0B,kBAAkB,qBAAqB;AAC5F,gBAAM,IAAI;AAAA,YACR,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AACA,4BAAc,CAAC,GAAE,eAAjB,GAAiB,aAAe,kBAAkB;AAAA,MACpD;AACA,UAAI,kBAAkB,mBAAmB;AACvC,YAAI,cAAc,WAAW,GAAG;AAC9B,gBAAM,IAAI;AAAA,YACR,SAAS,OAAO;AAAA,UAClB;AAAA,QACF;AACA,sBAAc,CAAC,EAAE,WAAW;AAAA,MAC9B;AAEA,YAAM,cAAc,cAAc,IAAI,CAAC,WAAW,OAAO,IAAI;AAC7D,YAAM,oBAAoB,cAAc,IAAI,CAAC,WAAW,OAAO,UAAU;AACzE,YAAM,iBAAiB,cAAc,IAAI,CAAC,WAAW,OAAO,QAAQ;AAEpE,YAAM,iBAAiB,wBAAwB,MAAM,YAAY,SAAS,OAAO,GAAG;AAEpF,YAAM,UAAmB;AAAA,QACvB;AAAA,QACA;AAAA,QACA,UAAU,MAAM;AAAA,QAChB,SAAS;AAAA,QACT,YAAY,MAAM;AAAA,QAClB,YAAY;AAAA,MACd;AAEA,UAAI,kBAAkB,KAAK,CAAC,UAAU,UAAU,MAAS,GAAG;AAC1D,gBAAQ,oBAAoB;AAAA,MAC9B;AACA,UAAI,eAAe,KAAK,CAAC,UAAU,KAAK,GAAG;AACzC,gBAAQ,iBAAiB;AAAA,MAC3B;AAEA,YAAM,KAAK,OAAO;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,eAAe,UAA6B;AAC1D,QAAM,cAAU,wBAAa,UAAU,OAAO;AAC9C,SAAO,WAAW,OAAO;AAC3B;","names":["yaml"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction validatePreserveValue(preserve: unknown, contextLabel: string): boolean {\n if (preserve === undefined) return false;\n if (typeof preserve !== 'boolean') {\n throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);\n }\n return preserve;\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string; preserve: boolean } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path, preserve: false };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n preserve: validatePreserveValue(layoutObj.preserve, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n inheritedPreserve?: boolean;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n if (normalizedLayouts.inheritedPreserve) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets layout preserve but has no layout views. Define at least one layout entry.`,\n );\n }\n parsedLayouts[0].preserve = true;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n if (layoutPreserve.some((value) => value)) {\n viewDef.layoutPreserve = layoutPreserve;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/parser/views_parser.ts"],"sourcesContent":["import yaml from 'js-yaml';\nimport { readFileSync } from 'fs';\nimport type {\n ViewDef,\n RawViewsDocument,\n RawLayoutEntry,\n RawLayoutObjectEntry,\n RawLayoutConfig,\n RawLayoutGroupEntry,\n} from '../types/index.js';\n\nfunction validateControllerValue(controller: unknown, contextLabel: string): string | undefined {\n if (controller === undefined) return undefined;\n if (typeof controller !== 'string' || controller.trim().length === 0) {\n throw new Error(`${contextLabel} has invalid controller. Expected a non-empty string.`);\n }\n return controller.trim();\n}\n\nfunction validatePreserveValue(preserve: unknown, contextLabel: string): boolean {\n if (preserve === undefined) return false;\n if (typeof preserve !== 'boolean') {\n throw new Error(`${contextLabel} has invalid preserve. Expected a boolean.`);\n }\n return preserve;\n}\n\nfunction parseLayoutEntry(entry: RawLayoutEntry, viewKey: string, index: number): { path: string; controller?: string; preserve: boolean } {\n if (typeof entry === 'string') {\n const path = entry.trim();\n if (!path) {\n throw new Error(`View \"${viewKey}\" has invalid layout entry at index ${index}. Expected a non-empty path string.`);\n }\n return { path, preserve: false };\n }\n\n const layoutObj = entry as RawLayoutObjectEntry;\n const layoutPath = layoutObj.path ?? layoutObj.view ?? layoutObj.layout;\n if (typeof layoutPath !== 'string' || layoutPath.trim().length === 0) {\n throw new Error(\n `View \"${viewKey}\" has invalid layout object at index ${index}. ` +\n 'Expected one of: path, view, or layout as a non-empty string.',\n );\n }\n\n return {\n path: layoutPath.trim(),\n controller: validateControllerValue(layoutObj.controller, `View \"${viewKey}\" layout[${index}]`),\n preserve: validatePreserveValue(layoutObj.preserve, `View \"${viewKey}\" layout[${index}]`),\n };\n}\n\nfunction isLayoutGroupEntry(entry: unknown): entry is RawLayoutGroupEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return Object.prototype.hasOwnProperty.call(entry, 'views');\n}\n\nfunction isLayoutObjectEntry(entry: unknown): entry is RawLayoutObjectEntry {\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;\n return (\n Object.prototype.hasOwnProperty.call(entry, 'path') ||\n Object.prototype.hasOwnProperty.call(entry, 'view') ||\n Object.prototype.hasOwnProperty.call(entry, 'layout') ||\n Object.prototype.hasOwnProperty.call(entry, 'controller')\n );\n}\n\nfunction normalizeLayouts(rawLayout: RawLayoutConfig | undefined, viewKey: string): {\n entries: RawLayoutEntry[];\n inheritedController?: string;\n inheritedPreserve?: boolean;\n} {\n if (rawLayout === undefined) {\n return { entries: [] };\n }\n\n if (Array.isArray(rawLayout)) {\n return { entries: rawLayout };\n }\n\n if (typeof rawLayout === 'string') {\n return { entries: [rawLayout] };\n }\n\n if (isLayoutGroupEntry(rawLayout)) {\n if (!Array.isArray(rawLayout.views)) {\n throw new Error(`View \"${viewKey}\" has invalid layout object. \"views\" must be an array.`);\n }\n\n return {\n entries: rawLayout.views,\n inheritedController: validateControllerValue(rawLayout.controller, `View \"${viewKey}\" layout`),\n inheritedPreserve: validatePreserveValue(rawLayout.preserve, `View \"${viewKey}\" layout`),\n };\n }\n\n if (isLayoutObjectEntry(rawLayout)) {\n return { entries: [rawLayout] };\n }\n\n throw new Error(\n `View \"${viewKey}\" has invalid layout definition. Expected an array, a string path, or an object with \"views\".`,\n );\n}\n\n/**\n * Parse views YAML content into a flat array of ViewDef.\n * Supports Wood-native `view:` format (dot-path keys).\n */\nexport function parseViews(yamlContent: string): ViewDef[] {\n const doc = yaml.load(yamlContent) as RawViewsDocument;\n const views: ViewDef[] = [];\n\n // Wood-native `view:` format\n // First segment = window name, rest = page name\n if (doc.view) {\n for (const [dotPath, entry] of Object.entries(doc.view)) {\n const segments = dotPath.split('.');\n const windowName = segments[0];\n const pageName = segments.slice(1).join('.');\n\n const normalizedLayouts = normalizeLayouts(entry.layout, dotPath);\n const parsedLayouts = normalizedLayouts.entries.map((layoutEntry, index) =>\n parseLayoutEntry(layoutEntry, dotPath, index),\n );\n if (normalizedLayouts.inheritedController) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets a layout controller but has no layout views. Define at least one layout entry.`,\n );\n }\n\n const firstLayoutController = parsedLayouts[0].controller;\n if (firstLayoutController && firstLayoutController !== normalizedLayouts.inheritedController) {\n throw new Error(\n `View \"${dotPath}\" has conflicting layout controllers. Use either layout.controller or layout.views[0].controller.`,\n );\n }\n parsedLayouts[0].controller ??= normalizedLayouts.inheritedController;\n }\n if (normalizedLayouts.inheritedPreserve) {\n if (parsedLayouts.length === 0) {\n throw new Error(\n `View \"${dotPath}\" sets layout preserve but has no layout views. Define at least one layout entry.`,\n );\n }\n parsedLayouts[0].preserve = true;\n }\n\n const layoutPaths = parsedLayouts.map((layout) => layout.path);\n const layoutControllers = parsedLayouts.map((layout) => layout.controller);\n const layoutPreserve = parsedLayouts.map((layout) => layout.preserve);\n\n const viewController = validateControllerValue(entry.controller, `View \"${dotPath}\"`);\n\n const viewDef: ViewDef = {\n windowName,\n pageName,\n viewPath: entry.view,\n layouts: layoutPaths,\n middleware: entry.middleware,\n controller: viewController,\n };\n\n if (layoutControllers.some((value) => value !== undefined)) {\n viewDef.layoutControllers = layoutControllers;\n }\n if (layoutPreserve.some((value) => value)) {\n viewDef.layoutPreserve = layoutPreserve;\n }\n\n views.push(viewDef);\n }\n }\n\n return views;\n}\n\n/** Read a YAML file from disk and parse it into ViewDef[]. */\nexport function parseViewsFile(filePath: string): ViewDef[] {\n const content = readFileSync(filePath, 'utf-8');\n return parseViews(content);\n}\n"],"mappings":"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","names":[]}
|
package/dist/router/index.d.cts
CHANGED
package/dist/router/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ var import_middleware_pipeline = require("../middleware/middleware_pipeline.cjs"
|
|
|
29
29
|
var import_controller_resolver = require("../controller/controller_resolver.cjs");
|
|
30
30
|
var import_controller_lifecycle = require("../controller/controller_lifecycle.cjs");
|
|
31
31
|
var import_logger = require("@noego/logger");
|
|
32
|
+
var import_ioc = require("@noego/ioc");
|
|
32
33
|
var import_tracing = require("../tracing/index.js");
|
|
33
34
|
const logger = (0, import_logger.getLogger)("wood:router");
|
|
34
35
|
function extractTraceConversationId(value) {
|
|
@@ -53,6 +54,10 @@ const RESERVED_CHANNELS = [
|
|
|
53
54
|
"__woodConfig",
|
|
54
55
|
"__traceBatch"
|
|
55
56
|
];
|
|
57
|
+
function controllerAlreadyRegistered(scope, ControllerClass) {
|
|
58
|
+
const candidate = scope;
|
|
59
|
+
return typeof candidate.isRegistered === "function" && candidate.isRegistered(ControllerClass);
|
|
60
|
+
}
|
|
56
61
|
class WoodRouter {
|
|
57
62
|
// IoC Container
|
|
58
63
|
constructor(options) {
|
|
@@ -168,8 +173,14 @@ class WoodRouter {
|
|
|
168
173
|
}
|
|
169
174
|
const ControllerClass = await this.controllerResolver.resolveClass(opDef.controller);
|
|
170
175
|
const scopedContainer = this.container.extend();
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
if (!controllerAlreadyRegistered(scopedContainer, ControllerClass) && typeof scopedContainer.registerClass === "function") {
|
|
177
|
+
scopedContainer.registerClass(ControllerClass);
|
|
178
|
+
}
|
|
179
|
+
const instance = await import_ioc.ExecutionContext.run(
|
|
180
|
+
scopedContainer,
|
|
181
|
+
() => scopedContainer.instance(ControllerClass)
|
|
182
|
+
);
|
|
183
|
+
const args = (0, import_controller_resolver.usesOperationContext)(ControllerClass) ? (0, import_controller_resolver.buildOperationContext)(channel, ipcContext) : (0, import_controller_resolver.buildControllerArgs)(ipcContext, scopedContainer);
|
|
173
184
|
const action = instance[opDef.action];
|
|
174
185
|
let result;
|
|
175
186
|
let actionError;
|
|
@@ -179,7 +190,7 @@ class WoodRouter {
|
|
|
179
190
|
`Controller "${opDef.controller}" has no action "${opDef.action}"`
|
|
180
191
|
);
|
|
181
192
|
}
|
|
182
|
-
result = await action.call(instance, args);
|
|
193
|
+
result = await import_ioc.ExecutionContext.run(scopedContainer, () => action.call(instance, args));
|
|
183
194
|
(0, import_tracing.publishMainTrace)("debug", {
|
|
184
195
|
source: "wood.router",
|
|
185
196
|
type: "dispatch.success",
|
|
@@ -206,6 +217,9 @@ class WoodRouter {
|
|
|
206
217
|
} finally {
|
|
207
218
|
try {
|
|
208
219
|
await (0, import_controller_lifecycle.disposeControllerLifecycle)(instance);
|
|
220
|
+
if (typeof scopedContainer.dispose === "function") {
|
|
221
|
+
await scopedContainer.dispose();
|
|
222
|
+
}
|
|
209
223
|
} catch (disposeError) {
|
|
210
224
|
(0, import_tracing.publishMainTrace)("error", {
|
|
211
225
|
source: "wood.router",
|
|
@@ -236,6 +250,15 @@ class WoodRouter {
|
|
|
236
250
|
registerController(name, ControllerClass) {
|
|
237
251
|
this.controllerResolver.register(name, ControllerClass);
|
|
238
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Pre-register the executable for a production middleware identity so it
|
|
255
|
+
* resolves from the registry instead of the filesystem — the bundled-mode
|
|
256
|
+
* path for middleware, and the seam test environments use to replace an
|
|
257
|
+
* identity's behavior without touching source topology.
|
|
258
|
+
*/
|
|
259
|
+
registerMiddleware(spec, middleware) {
|
|
260
|
+
this.middlewareResolver.register(spec, middleware);
|
|
261
|
+
}
|
|
239
262
|
/**
|
|
240
263
|
* Register an import path for a controller. The controller will be lazily
|
|
241
264
|
* imported from this path when first needed, avoiding circular dependencies.
|
|
@@ -243,6 +266,15 @@ class WoodRouter {
|
|
|
243
266
|
registerControllerPath(name, importPath) {
|
|
244
267
|
this.controllerResolver.registerPath(name, importPath);
|
|
245
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Register a caller-owned lazy loader for a controller: the module is
|
|
271
|
+
* imported through the CALLER's `() => import('…literal specifier…')` only
|
|
272
|
+
* on first dispatch, preserving the caller's module identity for the
|
|
273
|
+
* controller graph (thin production slices in tests).
|
|
274
|
+
*/
|
|
275
|
+
registerControllerLoader(name, loader) {
|
|
276
|
+
this.controllerResolver.registerLoader(name, loader);
|
|
277
|
+
}
|
|
246
278
|
/**
|
|
247
279
|
* Validate that every controller referenced in loaded operations is
|
|
248
280
|
* pre-registered in the controller resolver. Call this after
|