@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
|
@@ -21,6 +21,7 @@ __export(preload_generator_exports, {
|
|
|
21
21
|
generatePreload: () => generatePreload
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(preload_generator_exports);
|
|
24
|
+
var import_config_parser = require("../config/config_parser.cjs");
|
|
24
25
|
var import_generated_file_notice = require("./generated_file_notice.cjs");
|
|
25
26
|
const PRELOAD_SOURCE_INSTRUCTIONS = [
|
|
26
27
|
"To change IPC channels or preload bridge methods, edit the operations YAML configured by",
|
|
@@ -83,10 +84,11 @@ ${entries.join("\n")}
|
|
|
83
84
|
];`;
|
|
84
85
|
}
|
|
85
86
|
const INTERNAL_BRIDGE_GLOBAL_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
86
|
-
function generatePreload(operations) {
|
|
87
|
+
function generatePreload(operations, options = {}) {
|
|
87
88
|
const tree = buildNestedTree(operations);
|
|
88
89
|
const operationsCode = generateTreeCode(tree);
|
|
89
90
|
const rpcManifestCode = generateRpcManifestCode(operations);
|
|
91
|
+
const traceBatching = options.traceBatching ?? import_config_parser.RENDERER_TRACE_BATCHING_DEFAULTS;
|
|
90
92
|
return `${(0, import_generated_file_notice.generatedFileNotice)(PRELOAD_SOURCE_INSTRUCTIONS)}
|
|
91
93
|
import { contextBridge, ipcRenderer } from 'electron';
|
|
92
94
|
import {
|
|
@@ -159,6 +161,9 @@ ${operationsCode}
|
|
|
159
161
|
__log: (level: string, logger: string, message: string, context?: unknown) => {
|
|
160
162
|
ipcRenderer.send('__log', { level, logger, message, context });
|
|
161
163
|
},
|
|
164
|
+
__logBatch: (batch: unknown) => {
|
|
165
|
+
ipcRenderer.send('__log_batch', batch);
|
|
166
|
+
},
|
|
162
167
|
});
|
|
163
168
|
|
|
164
169
|
// Trace transport via MessagePort (activated when main process sends a port)
|
|
@@ -228,13 +233,15 @@ const __traceBatchPort: WoodTraceBatchPort = {
|
|
|
228
233
|
},
|
|
229
234
|
};
|
|
230
235
|
|
|
236
|
+
// Batching policy resolved from wood.config.yml observability configuration
|
|
237
|
+
// at generation time; this file embeds but does not own it.
|
|
231
238
|
const __traceBatchTransport = new RendererTraceBatchTransport({
|
|
232
239
|
producerId: __traceBatchProducerId,
|
|
233
|
-
maxQueuedEvents:
|
|
234
|
-
maxBatchEvents:
|
|
235
|
-
acknowledgementTimeoutMs:
|
|
236
|
-
retryLimit:
|
|
237
|
-
retryBaseDelayMs:
|
|
240
|
+
maxQueuedEvents: ${traceBatching.maxQueuedEvents},
|
|
241
|
+
maxBatchEvents: ${traceBatching.maxBatchEvents},
|
|
242
|
+
acknowledgementTimeoutMs: ${traceBatching.acknowledgementTimeoutMs},
|
|
243
|
+
retryLimit: ${traceBatching.retryLimit},
|
|
244
|
+
retryBaseDelayMs: ${traceBatching.retryBaseDelayMs},
|
|
238
245
|
now: () => Date.now(),
|
|
239
246
|
schedule: (work: () => void) => queueMicrotask(work),
|
|
240
247
|
delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.cjs';\nimport { generatedFileNotice } from './generated_file_notice.cjs';\n\nconst PRELOAD_SOURCE_INSTRUCTIONS = [\n 'To change IPC channels or preload bridge methods, edit the operations YAML configured by',\n 'wood.config.yml main.openapi and regenerate with `wood dev` or `wood build`.',\n] as const;\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `${generatedFileNotice(PRELOAD_SOURCE_INSTRUCTIONS)}\nimport { contextBridge, ipcRenderer } from 'electron';\nimport {\n RendererTraceBatchTransport,\n type WoodRendererTraceInput,\n type WoodTraceBatch,\n type WoodTraceBatchAcknowledgement,\n type WoodTraceBatchPort,\n} from '@noego/wood/trace-transport';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\nconst __TRACE_BUFFER_CAPACITY = 256;\nlet __traceDroppedCapacity = 0;\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else if (__traceBuffer.length >= __TRACE_BUFFER_CAPACITY) {\n __traceDroppedCapacity += 1;\n } else {\n __traceBuffer.push(data);\n }\n },\n status: () => ({\n queuedEvents: __traceBuffer.length,\n droppedCapacity: __traceDroppedCapacity,\n }),\n});\n\nconst __traceBatchProducerId = typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : \\`wood-renderer-\\${Date.now()}-\\${Math.random().toString(16).slice(2)}\\`;\nlet __traceBatchListener: ((message: unknown) => void) | null = null;\n\nconst __traceBatchPort: WoodTraceBatchPort = {\n postMessage: (batch: WoodTraceBatch) => {\n void ipcRenderer.invoke('__traceBatch', batch)\n .then((acknowledgement: WoodTraceBatchAcknowledgement) => {\n __traceBatchListener?.(acknowledgement);\n })\n .catch((error: unknown) => {\n __traceBatchListener?.({\n kind: 'retryable',\n batchId: batch.batchId,\n reason: error instanceof Error ? error.message : String(error),\n retryAfterMs: 100,\n });\n });\n },\n subscribe: (listener: (message: unknown) => void) => {\n __traceBatchListener = listener;\n return () => {\n if (__traceBatchListener === listener) {\n __traceBatchListener = null;\n }\n };\n },\n close: () => {\n __traceBatchListener = null;\n },\n};\n\nconst __traceBatchTransport = new RendererTraceBatchTransport({\n producerId: __traceBatchProducerId,\n maxQueuedEvents: 256,\n maxBatchEvents: 32,\n acknowledgementTimeoutMs: 1_000,\n retryLimit: 3,\n retryBaseDelayMs: 25,\n now: () => Date.now(),\n schedule: (work: () => void) => queueMicrotask(work),\n delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),\n});\n__traceBatchTransport.attachPort(__traceBatchPort);\n\ncontextBridge.exposeInMainWorld('__NOEGO_WOOD_TRACE_BATCH__', {\n send: (event: WoodRendererTraceInput) => __traceBatchTransport.admit(event),\n status: () => __traceBatchTransport.status(),\n flush: (deadlineMs: number) => __traceBatchTransport.flush(deadlineMs),\n close: (deadlineMs: number) => __traceBatchTransport.close(deadlineMs),\n});\n\nipcRenderer.on('__woodTraceDrainRequest', (_event, payload: unknown) => {\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n return;\n }\n const requestId = Reflect.get(payload, 'requestId');\n const deadlineMs = Reflect.get(payload, 'deadlineMs');\n if (typeof requestId !== 'string'\n || typeof deadlineMs !== 'number'\n || !Number.isFinite(deadlineMs)) {\n return;\n }\n void __traceBatchTransport.close(deadlineMs).finally(() => {\n ipcRenderer.send('__woodTraceDrainAck', {\n requestId,\n status: __traceBatchTransport.status(),\n });\n });\n});\n\nglobalThis.addEventListener?.('pagehide', () => {\n void __traceBatchTransport.flush(Date.now() + 100);\n});\n`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.cjs';\nimport type { RendererTraceBatchingConfig } from '../types/config.cjs';\nimport { RENDERER_TRACE_BATCHING_DEFAULTS } from '../config/config_parser.cjs';\nimport { generatedFileNotice } from './generated_file_notice.cjs';\n\nexport type GeneratePreloadOptions = {\n /**\n * Renderer→main IPC trace batching policy resolved from\n * wood.config.yml `observability.trace.rendererToMain.batching`.\n * The generated preload embeds these values but does not own them.\n */\n traceBatching?: RendererTraceBatchingConfig;\n};\n\nconst PRELOAD_SOURCE_INSTRUCTIONS = [\n 'To change IPC channels or preload bridge methods, edit the operations YAML configured by',\n 'wood.config.yml main.openapi and regenerate with `wood dev` or `wood build`.',\n] as const;\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(\n operations: Map<string, OperationDef>,\n options: GeneratePreloadOptions = {},\n): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n const traceBatching = options.traceBatching ?? RENDERER_TRACE_BATCHING_DEFAULTS;\n\n return `${generatedFileNotice(PRELOAD_SOURCE_INSTRUCTIONS)}\nimport { contextBridge, ipcRenderer } from 'electron';\nimport {\n RendererTraceBatchTransport,\n type WoodRendererTraceInput,\n type WoodTraceBatch,\n type WoodTraceBatchAcknowledgement,\n type WoodTraceBatchPort,\n} from '@noego/wood/trace-transport';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n __logBatch: (batch: unknown) => {\n ipcRenderer.send('__log_batch', batch);\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\nconst __TRACE_BUFFER_CAPACITY = 256;\nlet __traceDroppedCapacity = 0;\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else if (__traceBuffer.length >= __TRACE_BUFFER_CAPACITY) {\n __traceDroppedCapacity += 1;\n } else {\n __traceBuffer.push(data);\n }\n },\n status: () => ({\n queuedEvents: __traceBuffer.length,\n droppedCapacity: __traceDroppedCapacity,\n }),\n});\n\nconst __traceBatchProducerId = typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : \\`wood-renderer-\\${Date.now()}-\\${Math.random().toString(16).slice(2)}\\`;\nlet __traceBatchListener: ((message: unknown) => void) | null = null;\n\nconst __traceBatchPort: WoodTraceBatchPort = {\n postMessage: (batch: WoodTraceBatch) => {\n void ipcRenderer.invoke('__traceBatch', batch)\n .then((acknowledgement: WoodTraceBatchAcknowledgement) => {\n __traceBatchListener?.(acknowledgement);\n })\n .catch((error: unknown) => {\n __traceBatchListener?.({\n kind: 'retryable',\n batchId: batch.batchId,\n reason: error instanceof Error ? error.message : String(error),\n retryAfterMs: 100,\n });\n });\n },\n subscribe: (listener: (message: unknown) => void) => {\n __traceBatchListener = listener;\n return () => {\n if (__traceBatchListener === listener) {\n __traceBatchListener = null;\n }\n };\n },\n close: () => {\n __traceBatchListener = null;\n },\n};\n\n// Batching policy resolved from wood.config.yml observability configuration\n// at generation time; this file embeds but does not own it.\nconst __traceBatchTransport = new RendererTraceBatchTransport({\n producerId: __traceBatchProducerId,\n maxQueuedEvents: ${traceBatching.maxQueuedEvents},\n maxBatchEvents: ${traceBatching.maxBatchEvents},\n acknowledgementTimeoutMs: ${traceBatching.acknowledgementTimeoutMs},\n retryLimit: ${traceBatching.retryLimit},\n retryBaseDelayMs: ${traceBatching.retryBaseDelayMs},\n now: () => Date.now(),\n schedule: (work: () => void) => queueMicrotask(work),\n delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),\n});\n__traceBatchTransport.attachPort(__traceBatchPort);\n\ncontextBridge.exposeInMainWorld('__NOEGO_WOOD_TRACE_BATCH__', {\n send: (event: WoodRendererTraceInput) => __traceBatchTransport.admit(event),\n status: () => __traceBatchTransport.status(),\n flush: (deadlineMs: number) => __traceBatchTransport.flush(deadlineMs),\n close: (deadlineMs: number) => __traceBatchTransport.close(deadlineMs),\n});\n\nipcRenderer.on('__woodTraceDrainRequest', (_event, payload: unknown) => {\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n return;\n }\n const requestId = Reflect.get(payload, 'requestId');\n const deadlineMs = Reflect.get(payload, 'deadlineMs');\n if (typeof requestId !== 'string'\n || typeof deadlineMs !== 'number'\n || !Number.isFinite(deadlineMs)) {\n return;\n }\n void __traceBatchTransport.close(deadlineMs).finally(() => {\n ipcRenderer.send('__woodTraceDrainAck', {\n requestId,\n status: __traceBatchTransport.status(),\n });\n });\n});\n\nglobalThis.addEventListener?.('pagehide', () => {\n void __traceBatchTransport.flush(Date.now() + 100);\n});\n`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { RendererTraceBatchingConfig } from '../types/config.cjs';
|
|
1
2
|
import { OperationDef } from '../types/operations.cjs';
|
|
2
3
|
|
|
4
|
+
type GeneratePreloadOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Renderer→main IPC trace batching policy resolved from
|
|
7
|
+
* wood.config.yml `observability.trace.rendererToMain.batching`.
|
|
8
|
+
* The generated preload embeds these values but does not own them.
|
|
9
|
+
*/
|
|
10
|
+
traceBatching?: RendererTraceBatchingConfig;
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
13
|
* Generate a complete preload.ts file from operations.
|
|
5
14
|
*/
|
|
6
|
-
declare function generatePreload(operations: Map<string, OperationDef
|
|
15
|
+
declare function generatePreload(operations: Map<string, OperationDef>, options?: GeneratePreloadOptions): string;
|
|
7
16
|
|
|
8
|
-
export { generatePreload };
|
|
17
|
+
export { type GeneratePreloadOptions, generatePreload };
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { RendererTraceBatchingConfig } from '../types/config.js';
|
|
1
2
|
import { OperationDef } from '../types/operations.js';
|
|
2
3
|
|
|
4
|
+
type GeneratePreloadOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Renderer→main IPC trace batching policy resolved from
|
|
7
|
+
* wood.config.yml `observability.trace.rendererToMain.batching`.
|
|
8
|
+
* The generated preload embeds these values but does not own them.
|
|
9
|
+
*/
|
|
10
|
+
traceBatching?: RendererTraceBatchingConfig;
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
13
|
* Generate a complete preload.ts file from operations.
|
|
5
14
|
*/
|
|
6
|
-
declare function generatePreload(operations: Map<string, OperationDef
|
|
15
|
+
declare function generatePreload(operations: Map<string, OperationDef>, options?: GeneratePreloadOptions): string;
|
|
7
16
|
|
|
8
|
-
export { generatePreload };
|
|
17
|
+
export { type GeneratePreloadOptions, generatePreload };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RENDERER_TRACE_BATCHING_DEFAULTS } from "../config/config_parser.js";
|
|
1
2
|
import { generatedFileNotice } from "./generated_file_notice.js";
|
|
2
3
|
const PRELOAD_SOURCE_INSTRUCTIONS = [
|
|
3
4
|
"To change IPC channels or preload bridge methods, edit the operations YAML configured by",
|
|
@@ -60,10 +61,11 @@ ${entries.join("\n")}
|
|
|
60
61
|
];`;
|
|
61
62
|
}
|
|
62
63
|
const INTERNAL_BRIDGE_GLOBAL_KEY = "__NOEGO_WOOD_BRIDGE__";
|
|
63
|
-
function generatePreload(operations) {
|
|
64
|
+
function generatePreload(operations, options = {}) {
|
|
64
65
|
const tree = buildNestedTree(operations);
|
|
65
66
|
const operationsCode = generateTreeCode(tree);
|
|
66
67
|
const rpcManifestCode = generateRpcManifestCode(operations);
|
|
68
|
+
const traceBatching = options.traceBatching ?? RENDERER_TRACE_BATCHING_DEFAULTS;
|
|
67
69
|
return `${generatedFileNotice(PRELOAD_SOURCE_INSTRUCTIONS)}
|
|
68
70
|
import { contextBridge, ipcRenderer } from 'electron';
|
|
69
71
|
import {
|
|
@@ -136,6 +138,9 @@ ${operationsCode}
|
|
|
136
138
|
__log: (level: string, logger: string, message: string, context?: unknown) => {
|
|
137
139
|
ipcRenderer.send('__log', { level, logger, message, context });
|
|
138
140
|
},
|
|
141
|
+
__logBatch: (batch: unknown) => {
|
|
142
|
+
ipcRenderer.send('__log_batch', batch);
|
|
143
|
+
},
|
|
139
144
|
});
|
|
140
145
|
|
|
141
146
|
// Trace transport via MessagePort (activated when main process sends a port)
|
|
@@ -205,13 +210,15 @@ const __traceBatchPort: WoodTraceBatchPort = {
|
|
|
205
210
|
},
|
|
206
211
|
};
|
|
207
212
|
|
|
213
|
+
// Batching policy resolved from wood.config.yml observability configuration
|
|
214
|
+
// at generation time; this file embeds but does not own it.
|
|
208
215
|
const __traceBatchTransport = new RendererTraceBatchTransport({
|
|
209
216
|
producerId: __traceBatchProducerId,
|
|
210
|
-
maxQueuedEvents:
|
|
211
|
-
maxBatchEvents:
|
|
212
|
-
acknowledgementTimeoutMs:
|
|
213
|
-
retryLimit:
|
|
214
|
-
retryBaseDelayMs:
|
|
217
|
+
maxQueuedEvents: ${traceBatching.maxQueuedEvents},
|
|
218
|
+
maxBatchEvents: ${traceBatching.maxBatchEvents},
|
|
219
|
+
acknowledgementTimeoutMs: ${traceBatching.acknowledgementTimeoutMs},
|
|
220
|
+
retryLimit: ${traceBatching.retryLimit},
|
|
221
|
+
retryBaseDelayMs: ${traceBatching.retryBaseDelayMs},
|
|
215
222
|
now: () => Date.now(),
|
|
216
223
|
schedule: (work: () => void) => queueMicrotask(work),
|
|
217
224
|
delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.js';\nimport { generatedFileNotice } from './generated_file_notice.js';\n\nconst PRELOAD_SOURCE_INSTRUCTIONS = [\n 'To change IPC channels or preload bridge methods, edit the operations YAML configured by',\n 'wood.config.yml main.openapi and regenerate with `wood dev` or `wood build`.',\n] as const;\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(operations: Map<string, OperationDef>): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n\n return `${generatedFileNotice(PRELOAD_SOURCE_INSTRUCTIONS)}\nimport { contextBridge, ipcRenderer } from 'electron';\nimport {\n RendererTraceBatchTransport,\n type WoodRendererTraceInput,\n type WoodTraceBatch,\n type WoodTraceBatchAcknowledgement,\n type WoodTraceBatchPort,\n} from '@noego/wood/trace-transport';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\nconst __TRACE_BUFFER_CAPACITY = 256;\nlet __traceDroppedCapacity = 0;\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else if (__traceBuffer.length >= __TRACE_BUFFER_CAPACITY) {\n __traceDroppedCapacity += 1;\n } else {\n __traceBuffer.push(data);\n }\n },\n status: () => ({\n queuedEvents: __traceBuffer.length,\n droppedCapacity: __traceDroppedCapacity,\n }),\n});\n\nconst __traceBatchProducerId = typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : \\`wood-renderer-\\${Date.now()}-\\${Math.random().toString(16).slice(2)}\\`;\nlet __traceBatchListener: ((message: unknown) => void) | null = null;\n\nconst __traceBatchPort: WoodTraceBatchPort = {\n postMessage: (batch: WoodTraceBatch) => {\n void ipcRenderer.invoke('__traceBatch', batch)\n .then((acknowledgement: WoodTraceBatchAcknowledgement) => {\n __traceBatchListener?.(acknowledgement);\n })\n .catch((error: unknown) => {\n __traceBatchListener?.({\n kind: 'retryable',\n batchId: batch.batchId,\n reason: error instanceof Error ? error.message : String(error),\n retryAfterMs: 100,\n });\n });\n },\n subscribe: (listener: (message: unknown) => void) => {\n __traceBatchListener = listener;\n return () => {\n if (__traceBatchListener === listener) {\n __traceBatchListener = null;\n }\n };\n },\n close: () => {\n __traceBatchListener = null;\n },\n};\n\nconst __traceBatchTransport = new RendererTraceBatchTransport({\n producerId: __traceBatchProducerId,\n maxQueuedEvents: 256,\n maxBatchEvents: 32,\n acknowledgementTimeoutMs: 1_000,\n retryLimit: 3,\n retryBaseDelayMs: 25,\n now: () => Date.now(),\n schedule: (work: () => void) => queueMicrotask(work),\n delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),\n});\n__traceBatchTransport.attachPort(__traceBatchPort);\n\ncontextBridge.exposeInMainWorld('__NOEGO_WOOD_TRACE_BATCH__', {\n send: (event: WoodRendererTraceInput) => __traceBatchTransport.admit(event),\n status: () => __traceBatchTransport.status(),\n flush: (deadlineMs: number) => __traceBatchTransport.flush(deadlineMs),\n close: (deadlineMs: number) => __traceBatchTransport.close(deadlineMs),\n});\n\nipcRenderer.on('__woodTraceDrainRequest', (_event, payload: unknown) => {\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n return;\n }\n const requestId = Reflect.get(payload, 'requestId');\n const deadlineMs = Reflect.get(payload, 'deadlineMs');\n if (typeof requestId !== 'string'\n || typeof deadlineMs !== 'number'\n || !Number.isFinite(deadlineMs)) {\n return;\n }\n void __traceBatchTransport.close(deadlineMs).finally(() => {\n ipcRenderer.send('__woodTraceDrainAck', {\n requestId,\n status: __traceBatchTransport.status(),\n });\n });\n});\n\nglobalThis.addEventListener?.('pagehide', () => {\n void __traceBatchTransport.flush(Date.now() + 100);\n});\n`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/preload_generator.ts"],"sourcesContent":["import type { OperationDef } from '../types/index.js';\nimport type { RendererTraceBatchingConfig } from '../types/config.js';\nimport { RENDERER_TRACE_BATCHING_DEFAULTS } from '../config/config_parser.js';\nimport { generatedFileNotice } from './generated_file_notice.js';\n\nexport type GeneratePreloadOptions = {\n /**\n * Renderer→main IPC trace batching policy resolved from\n * wood.config.yml `observability.trace.rendererToMain.batching`.\n * The generated preload embeds these values but does not own them.\n */\n traceBatching?: RendererTraceBatchingConfig;\n};\n\nconst PRELOAD_SOURCE_INSTRUCTIONS = [\n 'To change IPC channels or preload bridge methods, edit the operations YAML configured by',\n 'wood.config.yml main.openapi and regenerate with `wood dev` or `wood build`.',\n] as const;\n\nfunction operationHasInput(op: OperationDef): boolean {\n return Boolean(\n op.inputSchema &&\n (op.inputSchema as Record<string, unknown>).type === 'object' &&\n Object.keys((op.inputSchema as Record<string, Record<string, unknown>>).properties ?? {}).length > 0,\n );\n}\n\n/**\n * Build a nested object tree from dot-path operation channels.\n * \"chat.message.send\" -> { chat: { message: { send: ... } } }\n */\nfunction buildNestedTree(operations: Map<string, OperationDef>): Record<string, unknown> {\n const tree: Record<string, unknown> = {};\n\n for (const [channel, op] of operations) {\n const parts = channel.split('.');\n let current: Record<string, unknown> = tree;\n\n for (let i = 0; i < parts.length - 1; i++) {\n if (!current[parts[i]]) current[parts[i]] = {};\n current = current[parts[i]] as Record<string, unknown>;\n }\n\n const leaf = parts[parts.length - 1];\n current[leaf] = { __operation: op };\n }\n\n return tree;\n}\n\n/**\n * Generate TypeScript code for a nested tree node.\n */\nfunction generateTreeCode(node: Record<string, unknown>, indent: string = ' '): string {\n const lines: string[] = [];\n\n for (const [key, value] of Object.entries(node)) {\n const entry = value as Record<string, unknown>;\n if (entry.__operation) {\n const op = entry.__operation as OperationDef;\n const hasInput = operationHasInput(op);\n\n if (hasInput) {\n lines.push(`${indent}${key}: (data: unknown) =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}', data),`);\n } else {\n lines.push(`${indent}${key}: () =>`);\n lines.push(`${indent} ipcRenderer.invoke('${op.channel}'),`);\n }\n } else {\n lines.push(`${indent}${key}: {`);\n lines.push(generateTreeCode(entry, indent + ' '));\n lines.push(`${indent}},`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction generateRpcManifestCode(operations: Map<string, OperationDef>): string {\n const entries = Array.from(operations.values())\n .sort((a, b) => a.channel.localeCompare(b.channel))\n .map((op) => {\n const payload = {\n channel: op.channel,\n controller: op.controller,\n action: op.action,\n path: op.channel.split('.'),\n hasInput: operationHasInput(op),\n };\n return ` ${JSON.stringify(payload)},`;\n });\n\n return `const __WOOD_RPC_MANIFEST = [\n${entries.join('\\n')}\n];`;\n}\n\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\n/**\n * Generate a complete preload.ts file from operations.\n */\nexport function generatePreload(\n operations: Map<string, OperationDef>,\n options: GeneratePreloadOptions = {},\n): string {\n const tree = buildNestedTree(operations);\n const operationsCode = generateTreeCode(tree);\n const rpcManifestCode = generateRpcManifestCode(operations);\n const traceBatching = options.traceBatching ?? RENDERER_TRACE_BATCHING_DEFAULTS;\n\n return `${generatedFileNotice(PRELOAD_SOURCE_INSTRUCTIONS)}\nimport { contextBridge, ipcRenderer } from 'electron';\nimport {\n RendererTraceBatchTransport,\n type WoodRendererTraceInput,\n type WoodTraceBatch,\n type WoodTraceBatchAcknowledgement,\n type WoodTraceBatchPort,\n} from '@noego/wood/trace-transport';\n\n${rpcManifestCode}\n\nconst __WOOD_INTERNAL_BRIDGE_KEY = '${INTERNAL_BRIDGE_GLOBAL_KEY}';\n\ncontextBridge.exposeInMainWorld(__WOOD_INTERNAL_BRIDGE_KEY, {\n${operationsCode}\n\n // Internal: backend controller/action RPC map for @Inject(App)\n __rpcManifest: __WOOD_RPC_MANIFEST,\n\n // Internal: loader data requests\n __load: (route: string, params: Record<string, string>) =>\n ipcRenderer.invoke('__load', { route, params }),\n\n // Internal: cross-window navigation\n __navigate: (windowId: string, route: string) =>\n ipcRenderer.invoke('__navigate', { windowId, route }),\n\n // Internal: window management\n __window: {\n current: () =>\n ipcRenderer.invoke('__window', { action: 'current' }),\n open: (windowId: string, options?: Record<string, unknown>) =>\n ipcRenderer.invoke('__window', { action: 'open', windowId, options }),\n close: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'close', windowId }),\n focus: (windowId: string) =>\n ipcRenderer.invoke('__window', { action: 'focus', windowId }),\n minimize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'minimize', windowId }),\n maximize: (windowId?: string) =>\n ipcRenderer.invoke('__window', { action: 'maximize', windowId }),\n },\n\n // Internal: framework config (trace min level, etc)\n __woodConfig: () =>\n ipcRenderer.invoke('__woodConfig'),\n\n // Internal: debug metadata\n __debug: {\n current: () =>\n ipcRenderer.invoke('__debug', { action: 'current' }),\n },\n\n // Internal: context menu\n __contextMenu: {\n show: (data: unknown) =>\n ipcRenderer.invoke('__contextMenu', { action: 'show', ...data as Record<string, unknown> }),\n },\n\n // Push events from main process\n on: (channel: string, callback: (...args: unknown[]) => void) => {\n const subscription = (_event: unknown, ...args: unknown[]) => callback(...args);\n ipcRenderer.on(\\`__event:\\${channel}\\`, subscription);\n return () => ipcRenderer.removeListener(\\`__event:\\${channel}\\`, subscription);\n },\n __trace: (event: unknown) => {\n ipcRenderer.send('__trace', event);\n },\n __log: (level: string, logger: string, message: string, context?: unknown) => {\n ipcRenderer.send('__log', { level, logger, message, context });\n },\n __logBatch: (batch: unknown) => {\n ipcRenderer.send('__log_batch', batch);\n },\n});\n\n// Trace transport via MessagePort (activated when main process sends a port)\nlet __tracePort: MessagePort | null = null;\nconst __traceBuffer: unknown[] = [];\nconst __TRACE_BUFFER_CAPACITY = 256;\nlet __traceDroppedCapacity = 0;\n\nfunction __traceFlush(): void {\n while (__traceBuffer.length > 0 && __tracePort) {\n __tracePort.postMessage(__traceBuffer.shift());\n }\n}\n\nipcRenderer.on('__trace-port', (event) => {\n __tracePort = event.ports[0];\n __tracePort.start();\n __traceFlush();\n});\n\ncontextBridge.exposeInMainWorld('__NOEGO_TRACE__', {\n send: (data: unknown) => {\n if (__tracePort) {\n __tracePort.postMessage(data);\n } else if (__traceBuffer.length >= __TRACE_BUFFER_CAPACITY) {\n __traceDroppedCapacity += 1;\n } else {\n __traceBuffer.push(data);\n }\n },\n status: () => ({\n queuedEvents: __traceBuffer.length,\n droppedCapacity: __traceDroppedCapacity,\n }),\n});\n\nconst __traceBatchProducerId = typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : \\`wood-renderer-\\${Date.now()}-\\${Math.random().toString(16).slice(2)}\\`;\nlet __traceBatchListener: ((message: unknown) => void) | null = null;\n\nconst __traceBatchPort: WoodTraceBatchPort = {\n postMessage: (batch: WoodTraceBatch) => {\n void ipcRenderer.invoke('__traceBatch', batch)\n .then((acknowledgement: WoodTraceBatchAcknowledgement) => {\n __traceBatchListener?.(acknowledgement);\n })\n .catch((error: unknown) => {\n __traceBatchListener?.({\n kind: 'retryable',\n batchId: batch.batchId,\n reason: error instanceof Error ? error.message : String(error),\n retryAfterMs: 100,\n });\n });\n },\n subscribe: (listener: (message: unknown) => void) => {\n __traceBatchListener = listener;\n return () => {\n if (__traceBatchListener === listener) {\n __traceBatchListener = null;\n }\n };\n },\n close: () => {\n __traceBatchListener = null;\n },\n};\n\n// Batching policy resolved from wood.config.yml observability configuration\n// at generation time; this file embeds but does not own it.\nconst __traceBatchTransport = new RendererTraceBatchTransport({\n producerId: __traceBatchProducerId,\n maxQueuedEvents: ${traceBatching.maxQueuedEvents},\n maxBatchEvents: ${traceBatching.maxBatchEvents},\n acknowledgementTimeoutMs: ${traceBatching.acknowledgementTimeoutMs},\n retryLimit: ${traceBatching.retryLimit},\n retryBaseDelayMs: ${traceBatching.retryBaseDelayMs},\n now: () => Date.now(),\n schedule: (work: () => void) => queueMicrotask(work),\n delay: (durationMs: number) => new Promise((resolve) => setTimeout(resolve, durationMs)),\n});\n__traceBatchTransport.attachPort(__traceBatchPort);\n\ncontextBridge.exposeInMainWorld('__NOEGO_WOOD_TRACE_BATCH__', {\n send: (event: WoodRendererTraceInput) => __traceBatchTransport.admit(event),\n status: () => __traceBatchTransport.status(),\n flush: (deadlineMs: number) => __traceBatchTransport.flush(deadlineMs),\n close: (deadlineMs: number) => __traceBatchTransport.close(deadlineMs),\n});\n\nipcRenderer.on('__woodTraceDrainRequest', (_event, payload: unknown) => {\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n return;\n }\n const requestId = Reflect.get(payload, 'requestId');\n const deadlineMs = Reflect.get(payload, 'deadlineMs');\n if (typeof requestId !== 'string'\n || typeof deadlineMs !== 'number'\n || !Number.isFinite(deadlineMs)) {\n return;\n }\n void __traceBatchTransport.close(deadlineMs).finally(() => {\n ipcRenderer.send('__woodTraceDrainAck', {\n requestId,\n status: __traceBatchTransport.status(),\n });\n });\n});\n\nglobalThis.addEventListener?.('pagehide', () => {\n void __traceBatchTransport.flush(Date.now() + 100);\n});\n`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -270,69 +270,108 @@ function createScopedContainer(): WoodScopedContainer {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
if (typeof scopedContainer.registerFunction === 'function') {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
// Explicit Singleton lifetime: these return renderer-root globals, and
|
|
274
|
+
// registerFunction defaults to Transient \u2014 rejected by ioc >= 0.5's
|
|
275
|
+
// captive-lifetime validation under singleton dependents.
|
|
276
|
+
const singleton = { loadAs: 0 /* LoadAs.Singleton */ };
|
|
277
|
+
scopedContainer.registerFunction(Navigation, () => getNavigation(), singleton);
|
|
278
|
+
scopedContainer.registerFunction(App, () => getApp(), singleton);
|
|
279
|
+
scopedContainer.registerFunction(Ipc, () => getIpc(), singleton);
|
|
276
280
|
}
|
|
277
281
|
|
|
278
282
|
return scopedContainer as WoodScopedContainer;
|
|
279
283
|
}
|
|
280
284
|
|
|
281
|
-
async function
|
|
285
|
+
async function resolveControllerInScope(
|
|
286
|
+
scope: WoodScopedContainer,
|
|
282
287
|
entry: GeneratedAppViewEntry,
|
|
283
288
|
ControllerClass: any,
|
|
284
289
|
contextLabel: string,
|
|
285
290
|
): Promise<unknown> {
|
|
286
|
-
const scopedContainer = createScopedContainer();
|
|
287
|
-
|
|
288
291
|
if (typeof ControllerClass !== 'function') {
|
|
289
292
|
throw new Error(
|
|
290
293
|
\`Controller for \${contextLabel} in view "\${entry.key}" is invalid. Expected a default exported class.\`,
|
|
291
294
|
);
|
|
292
295
|
}
|
|
293
296
|
|
|
294
|
-
if (typeof
|
|
295
|
-
|
|
297
|
+
if (typeof scope.registerClass === 'function') {
|
|
298
|
+
scope.registerClass(ControllerClass);
|
|
296
299
|
}
|
|
297
300
|
|
|
298
|
-
return await
|
|
301
|
+
return await scope.get(ControllerClass);
|
|
299
302
|
}
|
|
300
303
|
|
|
301
|
-
async function
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
async function disposeScopeQuietly(scope: { dispose?: () => Promise<void> } | null): Promise<void> {
|
|
305
|
+
if (!scope || typeof scope.dispose !== 'function') return;
|
|
306
|
+
try {
|
|
307
|
+
await scope.dispose();
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error('[wood] renderer scope disposal failed:', error);
|
|
306
310
|
}
|
|
307
|
-
|
|
308
|
-
return await resolveController(entry, entry.controllerClass, 'page');
|
|
309
311
|
}
|
|
310
312
|
|
|
311
|
-
|
|
313
|
+
/**
|
|
314
|
+
* One page scope per view lifecycle: the view controller and every
|
|
315
|
+
* non-preservable layout controller resolve from the SAME scope, so Scoped
|
|
316
|
+
* services/state are shared within the page (the NoEgo renderer model).
|
|
317
|
+
* Layouts marked preservable in the manifest own dedicated scopes that
|
|
318
|
+
* survive page transitions and dispose when the layout finally leaves.
|
|
319
|
+
*/
|
|
320
|
+
async function createControllerSet(
|
|
312
321
|
entry: GeneratedAppViewEntry,
|
|
313
|
-
|
|
314
|
-
): Promise<
|
|
315
|
-
|
|
322
|
+
preservedDepth = 0,
|
|
323
|
+
): Promise<{
|
|
324
|
+
controller: unknown;
|
|
325
|
+
layoutControllers: Array<unknown>;
|
|
326
|
+
pageScope: WoodScopedContainer;
|
|
327
|
+
layoutScopes: Array<WoodScopedContainer | null>;
|
|
328
|
+
}> {
|
|
329
|
+
const pageScope = createScopedContainer();
|
|
330
|
+
const layoutControllers: unknown[] = [];
|
|
331
|
+
const layoutScopes: Array<WoodScopedContainer | null> = [];
|
|
332
|
+
const createdLayoutScopes: WoodScopedContainer[] = [];
|
|
316
333
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
334
|
+
try {
|
|
335
|
+
for (let i = 0; i < preservedDepth; i += 1) {
|
|
336
|
+
layoutControllers.push(undefined);
|
|
337
|
+
layoutScopes.push(null);
|
|
338
|
+
}
|
|
320
339
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
340
|
+
for (let i = preservedDepth; i < entry.layoutControllerClasses.length; i += 1) {
|
|
341
|
+
const LayoutController = entry.layoutControllerClasses[i];
|
|
342
|
+
if (!LayoutController) {
|
|
343
|
+
layoutControllers.push(undefined);
|
|
344
|
+
layoutScopes.push(null);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const preservable = entry.layoutPreserve?.[i] === true;
|
|
348
|
+
let scope = pageScope;
|
|
349
|
+
if (preservable) {
|
|
350
|
+
scope = createScopedContainer();
|
|
351
|
+
createdLayoutScopes.push(scope);
|
|
352
|
+
}
|
|
353
|
+
const controller = await resolveControllerInScope(
|
|
354
|
+
scope,
|
|
355
|
+
entry,
|
|
356
|
+
LayoutController,
|
|
357
|
+
\`layout[\${i}]\`,
|
|
358
|
+
);
|
|
359
|
+
layoutControllers.push(controller);
|
|
360
|
+
layoutScopes.push(preservable ? scope : null);
|
|
326
361
|
}
|
|
327
|
-
const controller = await resolveController(
|
|
328
|
-
entry,
|
|
329
|
-
LayoutController,
|
|
330
|
-
\`layout[\${i}]\`,
|
|
331
|
-
);
|
|
332
|
-
controllers.push(controller);
|
|
333
|
-
}
|
|
334
362
|
|
|
335
|
-
|
|
363
|
+
const controller = entry.controllerClass
|
|
364
|
+
? await resolveControllerInScope(pageScope, entry, entry.controllerClass, 'page')
|
|
365
|
+
: undefined;
|
|
366
|
+
|
|
367
|
+
return { controller, layoutControllers, pageScope, layoutScopes };
|
|
368
|
+
} catch (error) {
|
|
369
|
+
// Failed transitions must not orphan scopes: dispose everything this
|
|
370
|
+
// set created before rethrowing.
|
|
371
|
+
for (const scope of createdLayoutScopes) await disposeScopeQuietly(scope as any);
|
|
372
|
+
await disposeScopeQuietly(pageScope as any);
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
336
375
|
}
|
|
337
376
|
|
|
338
377
|
const DEFAULT_VIEW_KEY = '${escapeSingleQuoted(defaultViewKey)}';
|
|
@@ -408,18 +447,15 @@ export async function app(target: Element) {
|
|
|
408
447
|
const bootstrapWindow = await getWoodBridge()?.__window?.current?.();
|
|
409
448
|
installAutomationIntrospection({ windowId: bootstrapWindow?.windowId ?? null });
|
|
410
449
|
}
|
|
411
|
-
const
|
|
412
|
-
const layoutControllers = await createLayoutControllers(entry);
|
|
450
|
+
const initialSet = await createControllerSet(entry);
|
|
413
451
|
|
|
414
452
|
return mount(NavigationShell, {
|
|
415
453
|
target,
|
|
416
454
|
props: {
|
|
417
455
|
manifest: VIEW_MANIFEST,
|
|
418
456
|
initialEntry: entry,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
createViewControllerFn: createViewController,
|
|
422
|
-
createLayoutControllersFn: createLayoutControllers,
|
|
457
|
+
initialControllerSet: initialSet,
|
|
458
|
+
createControllerSetFn: createControllerSet,
|
|
423
459
|
getViewManifestEntryFn: getViewManifestEntry,
|
|
424
460
|
},
|
|
425
461
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/renderer_app_generator.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport path from 'path';\nimport type { ViewDef } from '../types/index.cjs';\nimport { generatedFileNotice } from './generated_file_notice.cjs';\n\nconst RENDERER_APP_SOURCE_INSTRUCTIONS = [\n 'To change routes, page components, layouts, or renderer controllers, edit the views YAML',\n 'configured by wood.config.yml renderer.openapi and the matching UI source files.',\n] as const;\n\nexport interface RendererAppGeneratorOptions {\n /** Absolute path to renderer component root (e.g. /app/ui) */\n componentDir: string;\n /** Absolute output directory where generated file will be written */\n outDir: string;\n /** Optional explicit default view key (e.g. \"main.chat\") */\n entryViewKey?: string;\n}\n\nfunction computeImportPath(fromDir: string, toFile: string): string {\n let rel = path.relative(fromDir, toFile);\n\n if (!rel.startsWith('.')) {\n rel = './' + rel;\n }\n\n return rel.replace(/\\\\/g, '/');\n}\n\nfunction resolveFromComponentDir(componentDir: string, fileRef: string): string {\n if (path.isAbsolute(fileRef)) {\n return fileRef;\n }\n\n const normalizedRef = fileRef.replace(/\\\\/g, '/');\n const componentDirName = path.basename(componentDir).replace(/\\\\/g, '/');\n\n if (normalizedRef.startsWith(componentDirName + '/')) {\n const stripped = normalizedRef.slice(componentDirName.length + 1);\n return path.resolve(componentDir, stripped);\n }\n\n return path.resolve(componentDir, normalizedRef);\n}\n\nfunction viewKey(view: ViewDef): string {\n return `${view.windowName}.${view.pageName}`;\n}\n\nfunction toIdentifier(prefix: string, value: string): string {\n const core = value\n .replace(/[^a-zA-Z0-9]+/g, ' ')\n .trim()\n .split(/\\s+/)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join('');\n\n return `${prefix}${core || 'Component'}`;\n}\n\nfunction escapeSingleQuoted(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nconst CONTROLLER_FILE_SUFFIXES = [\n '.ts',\n '.js',\n '.controller.ts',\n '.controller.js',\n] as const;\n\nconst CONTROLLER_INDEX_CANDIDATES = [\n 'index.ts',\n 'index.js',\n] as const;\n\nfunction resolveControllerFile(controllerRef: string, componentDir: string): string | null {\n const normalized = controllerRef.trim();\n const basePath = resolveFromComponentDir(componentDir, normalized);\n\n if (existsSync(basePath)) {\n return basePath;\n }\n\n if (path.extname(basePath)) {\n return null;\n }\n\n for (const suffix of CONTROLLER_FILE_SUFFIXES) {\n const candidate = basePath + suffix;\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n for (const indexFile of CONTROLLER_INDEX_CANDIDATES) {\n const candidate = path.resolve(basePath, indexFile);\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nfunction resolveDefaultViewKey(sortedViews: ViewDef[], entryViewKey?: string): string {\n if (sortedViews.length === 0) return '';\n\n const configuredEntry = entryViewKey?.trim();\n if (!configuredEntry) {\n return viewKey(sortedViews[0]);\n }\n\n const availableKeys = sortedViews.map((view) => viewKey(view));\n if (availableKeys.includes(configuredEntry)) {\n return configuredEntry;\n }\n\n throw new Error(\n `Renderer entry \"${configuredEntry}\" was not found in parsed views. Available view keys: ${availableKeys.join(', ')}`,\n );\n}\n\nexport function generateRendererApp(\n views: ViewDef[],\n options: RendererAppGeneratorOptions,\n): string {\n const sortedViews = [...views].sort((a, b) => viewKey(a).localeCompare(viewKey(b)));\n const defaultViewKey = resolveDefaultViewKey(sortedViews, options.entryViewKey);\n\n const importLines: string[] = [];\n const viewIdentifiers = new Map<string, string>();\n const layoutIdentifiers = new Map<string, string>();\n const controllerIdentifiers = new Map<string, { identifier: string; importPath: string }>();\n const usedNames = new Set<string>();\n\n const allocateName = (prefix: string, filePath: string): string => {\n const base = toIdentifier(prefix, filePath);\n let name = base;\n let suffix = 2;\n while (usedNames.has(name)) {\n name = `${base}${suffix}`;\n suffix += 1;\n }\n usedNames.add(name);\n return name;\n };\n\n const ensureControllerImported = (controllerRef: string, owningViewKey: string): void => {\n if (controllerIdentifiers.has(controllerRef)) return;\n\n const resolvedControllerFile = resolveControllerFile(controllerRef, options.componentDir);\n if (!resolvedControllerFile) {\n throw new Error(\n `Renderer app codegen failed: controller \"${controllerRef}\" for view \"${owningViewKey}\" was not found. ` +\n 'Controller must point to an existing file with a default exported class.',\n );\n }\n\n const identifier = allocateName('PageController', controllerRef);\n const importPath = computeImportPath(options.outDir, resolvedControllerFile);\n controllerIdentifiers.set(controllerRef, { identifier, importPath });\n importLines.push(`import ${identifier} from '${importPath}';`);\n };\n\n for (const view of sortedViews) {\n if (!viewIdentifiers.has(view.viewPath)) {\n const identifier = allocateName('View', view.viewPath);\n viewIdentifiers.set(view.viewPath, identifier);\n const abs = resolveFromComponentDir(options.componentDir, view.viewPath);\n const importPath = computeImportPath(options.outDir, abs);\n importLines.push(`import ${identifier} from '${importPath}';`);\n }\n\n for (const layoutPath of view.layouts) {\n if (layoutIdentifiers.has(layoutPath)) continue;\n const identifier = allocateName('Layout', layoutPath);\n layoutIdentifiers.set(layoutPath, identifier);\n const abs = resolveFromComponentDir(options.componentDir, layoutPath);\n const importPath = computeImportPath(options.outDir, abs);\n importLines.push(`import ${identifier} from '${importPath}';`);\n }\n\n if (view.controller) {\n ensureControllerImported(view.controller, viewKey(view));\n }\n\n for (const layoutController of view.layoutControllers ?? []) {\n if (!layoutController) continue;\n ensureControllerImported(layoutController, viewKey(view));\n }\n }\n\n const manifestEntries = sortedViews.map((view) => {\n const key = viewKey(view);\n const viewIdentifier = viewIdentifiers.get(view.viewPath)!;\n const layoutList = view.layouts.map((layoutPath) => layoutIdentifiers.get(layoutPath)!);\n const controllerPath = view.controller ? `'${escapeSingleQuoted(view.controller)}'` : 'undefined';\n const controllerClass = view.controller\n ? controllerIdentifiers.get(view.controller)!.identifier\n : 'undefined';\n const layoutControllerRefs = view.layoutControllers;\n if (layoutControllerRefs && layoutControllerRefs.length !== view.layouts.length) {\n throw new Error(\n `Renderer app codegen failed: layout controller count for view \"${key}\" does not match layout count.`,\n );\n }\n const normalizedLayoutControllerRefs = layoutControllerRefs ?? Array.from(\n { length: view.layouts.length },\n () => undefined,\n );\n const layoutControllerPaths = `[${normalizedLayoutControllerRefs\n .map((controllerRef) => (controllerRef ? `'${escapeSingleQuoted(controllerRef)}'` : 'undefined'))\n .join(', ')}]`;\n const layoutControllerClasses = `[${normalizedLayoutControllerRefs\n .map((controllerRef) => (controllerRef ? controllerIdentifiers.get(controllerRef)!.identifier : 'undefined'))\n .join(', ')}]`;\n const layoutPreserve = view.layoutPreserve ?? Array.from(\n { length: view.layouts.length },\n () => false,\n );\n if (layoutPreserve.length !== view.layouts.length) {\n throw new Error(\n `Renderer app codegen failed: layout preserve count for view \"${key}\" does not match layout count.`,\n );\n }\n\n return ` '${escapeSingleQuoted(key)}': {\n key: '${escapeSingleQuoted(key)}',\n windowName: '${escapeSingleQuoted(view.windowName)}',\n pageName: '${escapeSingleQuoted(view.pageName)}',\n viewPath: '${escapeSingleQuoted(view.viewPath)}',\n layoutPaths: ${JSON.stringify(view.layouts)},\n layoutControllers: ${layoutControllerPaths},\n layoutPreserve: ${JSON.stringify(layoutPreserve)},\n controller: ${controllerPath},\n layoutControllerClasses: ${layoutControllerClasses},\n controllerClass: ${controllerClass},\n view: ${viewIdentifier},\n layouts: [${layoutList.join(', ')}],\n },`;\n });\n\n return `${generatedFileNotice(RENDERER_APP_SOURCE_INSTRUCTIONS)}\nimport { mount } from 'svelte';\nimport { App, getApp, getContainer } from '@noego/wood/client';\nimport { Navigation, getNavigation, installAutomationIntrospection } from '@noego/wood/navigation';\nimport { Ipc, getIpc } from '@noego/wood/ipc';\nimport NavigationShell from '@noego/wood/navigation-shell';\n${importLines.join('\\n')}\n\ninterface GeneratedAppViewEntry {\n key: string;\n windowName: string;\n pageName: string;\n viewPath: string;\n layoutPaths: string[];\n layoutControllers: Array<string | undefined>;\n layoutPreserve: boolean[];\n controller?: string;\n layoutControllerClasses: Array<any | undefined>;\n controllerClass?: any;\n view: any;\n layouts: any[];\n}\n\nconst VIEW_MANIFEST: Record<string, GeneratedAppViewEntry> = {\n${manifestEntries.join('\\n')}\n};\n\nfunction getViewManifestEntry(viewKey: string): GeneratedAppViewEntry | null {\n return VIEW_MANIFEST[viewKey] ?? null;\n}\n\ninterface WoodScopedContainer {\n get(token: any): unknown | Promise<unknown>;\n registerClass?(token: any): void;\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\ninterface WoodContainer extends WoodScopedContainer {\n extend?(): WoodScopedContainer;\n}\n\nfunction createScopedContainer(): WoodScopedContainer {\n const container = getContainer() as WoodContainer;\n if (typeof container.extend !== 'function') {\n throw new Error('Wood client container does not support extend().');\n }\n\n const scopedContainer = container.extend();\n\n if (!scopedContainer || typeof scopedContainer.get !== 'function') {\n throw new Error('Wood client container does not support get().');\n }\n\n if (typeof scopedContainer.registerFunction === 'function') {\n scopedContainer.registerFunction(Navigation, () => getNavigation());\n scopedContainer.registerFunction(App, () => getApp());\n scopedContainer.registerFunction(Ipc, () => getIpc());\n }\n\n return scopedContainer as WoodScopedContainer;\n}\n\nasync function resolveController(\n entry: GeneratedAppViewEntry,\n ControllerClass: any,\n contextLabel: string,\n): Promise<unknown> {\n const scopedContainer = createScopedContainer();\n\n if (typeof ControllerClass !== 'function') {\n throw new Error(\n \\`Controller for \\${contextLabel} in view \"\\${entry.key}\" is invalid. Expected a default exported class.\\`,\n );\n }\n\n if (typeof scopedContainer.registerClass === 'function') {\n scopedContainer.registerClass(ControllerClass);\n }\n\n return await scopedContainer.get(ControllerClass);\n}\n\nasync function createViewController(\n entry: GeneratedAppViewEntry,\n): Promise<unknown> {\n if (!entry.controllerClass) {\n return undefined;\n }\n\n return await resolveController(entry, entry.controllerClass, 'page');\n}\n\nasync function createLayoutControllers(\n entry: GeneratedAppViewEntry,\n startIndex = 0,\n): Promise<Array<unknown>> {\n const controllers: unknown[] = [];\n\n for (let i = 0; i < startIndex; i += 1) {\n controllers.push(undefined);\n }\n\n for (let i = startIndex; i < entry.layoutControllerClasses.length; i += 1) {\n const LayoutController = entry.layoutControllerClasses[i];\n if (!LayoutController) {\n controllers.push(undefined);\n continue;\n }\n const controller = await resolveController(\n entry,\n LayoutController,\n \\`layout[\\${i}]\\`,\n );\n controllers.push(controller);\n }\n\n return controllers;\n}\n\nconst DEFAULT_VIEW_KEY = '${escapeSingleQuoted(defaultViewKey)}';\n\ntype WoodWindowBootstrapInfo = {\n ok?: boolean;\n windowId?: string;\n defaultRoute?: string;\n};\n\ntype WoodBootstrapBridgeLike = {\n __window?: {\n current?: () => Promise<WoodWindowBootstrapInfo>;\n };\n};\n\nfunction getWoodBridge(): WoodBootstrapBridgeLike | null {\n const globalObj = globalThis as typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n };\n };\n\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__ ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n\n return bridge as WoodBootstrapBridgeLike;\n}\n\nasync function resolveInitialEntry(): Promise<GeneratedAppViewEntry> {\n if (!DEFAULT_VIEW_KEY) {\n throw new Error('No views available in renderer manifest. Define at least one view in renderer OpenAPI YAML.');\n }\n\n const bridge = getWoodBridge();\n const bootstrapWindow = await bridge?.__window?.current?.();\n const requestedRoute = bootstrapWindow?.defaultRoute;\n\n if (requestedRoute) {\n const routeEntry = getViewManifestEntry(requestedRoute);\n if (routeEntry) {\n return routeEntry;\n }\n }\n\n const entry = getViewManifestEntry(DEFAULT_VIEW_KEY);\n if (!entry) {\n throw new Error(\\`Default view \"\\${DEFAULT_VIEW_KEY}\" is missing from generated view manifest.\\`);\n }\n\n return entry;\n}\n\nfunction isAutomationBuild(): boolean {\n try {\n const env = (import.meta as { env?: { DEV?: boolean; MODE?: string } }).env;\n return Boolean(env?.DEV) || env?.MODE === 'test';\n } catch {\n return false;\n }\n}\n\nexport async function app(target: Element) {\n const entry = await resolveInitialEntry();\n\n getNavigation().replace(entry.key, {}, {});\n\n // Dev/test-only Axe introspection surface; never present in production builds.\n if (isAutomationBuild()) {\n const bootstrapWindow = await getWoodBridge()?.__window?.current?.();\n installAutomationIntrospection({ windowId: bootstrapWindow?.windowId ?? null });\n }\n const controller = await createViewController(entry);\n const layoutControllers = await createLayoutControllers(entry);\n\n return mount(NavigationShell, {\n target,\n props: {\n manifest: VIEW_MANIFEST,\n initialEntry: entry,\n initialController: controller,\n initialLayoutControllers: layoutControllers,\n createViewControllerFn: createViewController,\n createLayoutControllersFn: createLayoutControllers,\n getViewManifestEntryFn: getViewManifestEntry,\n },\n });\n}\n`;\n}\n"],"mappings":"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","names":["path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/renderer_app_generator.ts"],"sourcesContent":["import { existsSync } from 'fs';\nimport path from 'path';\nimport type { ViewDef } from '../types/index.cjs';\nimport { generatedFileNotice } from './generated_file_notice.cjs';\n\nconst RENDERER_APP_SOURCE_INSTRUCTIONS = [\n 'To change routes, page components, layouts, or renderer controllers, edit the views YAML',\n 'configured by wood.config.yml renderer.openapi and the matching UI source files.',\n] as const;\n\nexport interface RendererAppGeneratorOptions {\n /** Absolute path to renderer component root (e.g. /app/ui) */\n componentDir: string;\n /** Absolute output directory where generated file will be written */\n outDir: string;\n /** Optional explicit default view key (e.g. \"main.chat\") */\n entryViewKey?: string;\n}\n\nfunction computeImportPath(fromDir: string, toFile: string): string {\n let rel = path.relative(fromDir, toFile);\n\n if (!rel.startsWith('.')) {\n rel = './' + rel;\n }\n\n return rel.replace(/\\\\/g, '/');\n}\n\nfunction resolveFromComponentDir(componentDir: string, fileRef: string): string {\n if (path.isAbsolute(fileRef)) {\n return fileRef;\n }\n\n const normalizedRef = fileRef.replace(/\\\\/g, '/');\n const componentDirName = path.basename(componentDir).replace(/\\\\/g, '/');\n\n if (normalizedRef.startsWith(componentDirName + '/')) {\n const stripped = normalizedRef.slice(componentDirName.length + 1);\n return path.resolve(componentDir, stripped);\n }\n\n return path.resolve(componentDir, normalizedRef);\n}\n\nfunction viewKey(view: ViewDef): string {\n return `${view.windowName}.${view.pageName}`;\n}\n\nfunction toIdentifier(prefix: string, value: string): string {\n const core = value\n .replace(/[^a-zA-Z0-9]+/g, ' ')\n .trim()\n .split(/\\s+/)\n .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))\n .join('');\n\n return `${prefix}${core || 'Component'}`;\n}\n\nfunction escapeSingleQuoted(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\nconst CONTROLLER_FILE_SUFFIXES = [\n '.ts',\n '.js',\n '.controller.ts',\n '.controller.js',\n] as const;\n\nconst CONTROLLER_INDEX_CANDIDATES = [\n 'index.ts',\n 'index.js',\n] as const;\n\nfunction resolveControllerFile(controllerRef: string, componentDir: string): string | null {\n const normalized = controllerRef.trim();\n const basePath = resolveFromComponentDir(componentDir, normalized);\n\n if (existsSync(basePath)) {\n return basePath;\n }\n\n if (path.extname(basePath)) {\n return null;\n }\n\n for (const suffix of CONTROLLER_FILE_SUFFIXES) {\n const candidate = basePath + suffix;\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n for (const indexFile of CONTROLLER_INDEX_CANDIDATES) {\n const candidate = path.resolve(basePath, indexFile);\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n\n return null;\n}\n\nfunction resolveDefaultViewKey(sortedViews: ViewDef[], entryViewKey?: string): string {\n if (sortedViews.length === 0) return '';\n\n const configuredEntry = entryViewKey?.trim();\n if (!configuredEntry) {\n return viewKey(sortedViews[0]);\n }\n\n const availableKeys = sortedViews.map((view) => viewKey(view));\n if (availableKeys.includes(configuredEntry)) {\n return configuredEntry;\n }\n\n throw new Error(\n `Renderer entry \"${configuredEntry}\" was not found in parsed views. Available view keys: ${availableKeys.join(', ')}`,\n );\n}\n\nexport function generateRendererApp(\n views: ViewDef[],\n options: RendererAppGeneratorOptions,\n): string {\n const sortedViews = [...views].sort((a, b) => viewKey(a).localeCompare(viewKey(b)));\n const defaultViewKey = resolveDefaultViewKey(sortedViews, options.entryViewKey);\n\n const importLines: string[] = [];\n const viewIdentifiers = new Map<string, string>();\n const layoutIdentifiers = new Map<string, string>();\n const controllerIdentifiers = new Map<string, { identifier: string; importPath: string }>();\n const usedNames = new Set<string>();\n\n const allocateName = (prefix: string, filePath: string): string => {\n const base = toIdentifier(prefix, filePath);\n let name = base;\n let suffix = 2;\n while (usedNames.has(name)) {\n name = `${base}${suffix}`;\n suffix += 1;\n }\n usedNames.add(name);\n return name;\n };\n\n const ensureControllerImported = (controllerRef: string, owningViewKey: string): void => {\n if (controllerIdentifiers.has(controllerRef)) return;\n\n const resolvedControllerFile = resolveControllerFile(controllerRef, options.componentDir);\n if (!resolvedControllerFile) {\n throw new Error(\n `Renderer app codegen failed: controller \"${controllerRef}\" for view \"${owningViewKey}\" was not found. ` +\n 'Controller must point to an existing file with a default exported class.',\n );\n }\n\n const identifier = allocateName('PageController', controllerRef);\n const importPath = computeImportPath(options.outDir, resolvedControllerFile);\n controllerIdentifiers.set(controllerRef, { identifier, importPath });\n importLines.push(`import ${identifier} from '${importPath}';`);\n };\n\n for (const view of sortedViews) {\n if (!viewIdentifiers.has(view.viewPath)) {\n const identifier = allocateName('View', view.viewPath);\n viewIdentifiers.set(view.viewPath, identifier);\n const abs = resolveFromComponentDir(options.componentDir, view.viewPath);\n const importPath = computeImportPath(options.outDir, abs);\n importLines.push(`import ${identifier} from '${importPath}';`);\n }\n\n for (const layoutPath of view.layouts) {\n if (layoutIdentifiers.has(layoutPath)) continue;\n const identifier = allocateName('Layout', layoutPath);\n layoutIdentifiers.set(layoutPath, identifier);\n const abs = resolveFromComponentDir(options.componentDir, layoutPath);\n const importPath = computeImportPath(options.outDir, abs);\n importLines.push(`import ${identifier} from '${importPath}';`);\n }\n\n if (view.controller) {\n ensureControllerImported(view.controller, viewKey(view));\n }\n\n for (const layoutController of view.layoutControllers ?? []) {\n if (!layoutController) continue;\n ensureControllerImported(layoutController, viewKey(view));\n }\n }\n\n const manifestEntries = sortedViews.map((view) => {\n const key = viewKey(view);\n const viewIdentifier = viewIdentifiers.get(view.viewPath)!;\n const layoutList = view.layouts.map((layoutPath) => layoutIdentifiers.get(layoutPath)!);\n const controllerPath = view.controller ? `'${escapeSingleQuoted(view.controller)}'` : 'undefined';\n const controllerClass = view.controller\n ? controllerIdentifiers.get(view.controller)!.identifier\n : 'undefined';\n const layoutControllerRefs = view.layoutControllers;\n if (layoutControllerRefs && layoutControllerRefs.length !== view.layouts.length) {\n throw new Error(\n `Renderer app codegen failed: layout controller count for view \"${key}\" does not match layout count.`,\n );\n }\n const normalizedLayoutControllerRefs = layoutControllerRefs ?? Array.from(\n { length: view.layouts.length },\n () => undefined,\n );\n const layoutControllerPaths = `[${normalizedLayoutControllerRefs\n .map((controllerRef) => (controllerRef ? `'${escapeSingleQuoted(controllerRef)}'` : 'undefined'))\n .join(', ')}]`;\n const layoutControllerClasses = `[${normalizedLayoutControllerRefs\n .map((controllerRef) => (controllerRef ? controllerIdentifiers.get(controllerRef)!.identifier : 'undefined'))\n .join(', ')}]`;\n const layoutPreserve = view.layoutPreserve ?? Array.from(\n { length: view.layouts.length },\n () => false,\n );\n if (layoutPreserve.length !== view.layouts.length) {\n throw new Error(\n `Renderer app codegen failed: layout preserve count for view \"${key}\" does not match layout count.`,\n );\n }\n\n return ` '${escapeSingleQuoted(key)}': {\n key: '${escapeSingleQuoted(key)}',\n windowName: '${escapeSingleQuoted(view.windowName)}',\n pageName: '${escapeSingleQuoted(view.pageName)}',\n viewPath: '${escapeSingleQuoted(view.viewPath)}',\n layoutPaths: ${JSON.stringify(view.layouts)},\n layoutControllers: ${layoutControllerPaths},\n layoutPreserve: ${JSON.stringify(layoutPreserve)},\n controller: ${controllerPath},\n layoutControllerClasses: ${layoutControllerClasses},\n controllerClass: ${controllerClass},\n view: ${viewIdentifier},\n layouts: [${layoutList.join(', ')}],\n },`;\n });\n\n return `${generatedFileNotice(RENDERER_APP_SOURCE_INSTRUCTIONS)}\nimport { mount } from 'svelte';\nimport { App, getApp, getContainer } from '@noego/wood/client';\nimport { Navigation, getNavigation, installAutomationIntrospection } from '@noego/wood/navigation';\nimport { Ipc, getIpc } from '@noego/wood/ipc';\nimport NavigationShell from '@noego/wood/navigation-shell';\n${importLines.join('\\n')}\n\ninterface GeneratedAppViewEntry {\n key: string;\n windowName: string;\n pageName: string;\n viewPath: string;\n layoutPaths: string[];\n layoutControllers: Array<string | undefined>;\n layoutPreserve: boolean[];\n controller?: string;\n layoutControllerClasses: Array<any | undefined>;\n controllerClass?: any;\n view: any;\n layouts: any[];\n}\n\nconst VIEW_MANIFEST: Record<string, GeneratedAppViewEntry> = {\n${manifestEntries.join('\\n')}\n};\n\nfunction getViewManifestEntry(viewKey: string): GeneratedAppViewEntry | null {\n return VIEW_MANIFEST[viewKey] ?? null;\n}\n\ninterface WoodScopedContainer {\n get(token: any): unknown | Promise<unknown>;\n registerClass?(token: any): void;\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\ninterface WoodContainer extends WoodScopedContainer {\n extend?(): WoodScopedContainer;\n}\n\nfunction createScopedContainer(): WoodScopedContainer {\n const container = getContainer() as WoodContainer;\n if (typeof container.extend !== 'function') {\n throw new Error('Wood client container does not support extend().');\n }\n\n const scopedContainer = container.extend();\n\n if (!scopedContainer || typeof scopedContainer.get !== 'function') {\n throw new Error('Wood client container does not support get().');\n }\n\n if (typeof scopedContainer.registerFunction === 'function') {\n // Explicit Singleton lifetime: these return renderer-root globals, and\n // registerFunction defaults to Transient — rejected by ioc >= 0.5's\n // captive-lifetime validation under singleton dependents.\n const singleton = { loadAs: 0 /* LoadAs.Singleton */ };\n scopedContainer.registerFunction(Navigation, () => getNavigation(), singleton);\n scopedContainer.registerFunction(App, () => getApp(), singleton);\n scopedContainer.registerFunction(Ipc, () => getIpc(), singleton);\n }\n\n return scopedContainer as WoodScopedContainer;\n}\n\nasync function resolveControllerInScope(\n scope: WoodScopedContainer,\n entry: GeneratedAppViewEntry,\n ControllerClass: any,\n contextLabel: string,\n): Promise<unknown> {\n if (typeof ControllerClass !== 'function') {\n throw new Error(\n \\`Controller for \\${contextLabel} in view \"\\${entry.key}\" is invalid. Expected a default exported class.\\`,\n );\n }\n\n if (typeof scope.registerClass === 'function') {\n scope.registerClass(ControllerClass);\n }\n\n return await scope.get(ControllerClass);\n}\n\nasync function disposeScopeQuietly(scope: { dispose?: () => Promise<void> } | null): Promise<void> {\n if (!scope || typeof scope.dispose !== 'function') return;\n try {\n await scope.dispose();\n } catch (error) {\n console.error('[wood] renderer scope disposal failed:', error);\n }\n}\n\n/**\n * One page scope per view lifecycle: the view controller and every\n * non-preservable layout controller resolve from the SAME scope, so Scoped\n * services/state are shared within the page (the NoEgo renderer model).\n * Layouts marked preservable in the manifest own dedicated scopes that\n * survive page transitions and dispose when the layout finally leaves.\n */\nasync function createControllerSet(\n entry: GeneratedAppViewEntry,\n preservedDepth = 0,\n): Promise<{\n controller: unknown;\n layoutControllers: Array<unknown>;\n pageScope: WoodScopedContainer;\n layoutScopes: Array<WoodScopedContainer | null>;\n}> {\n const pageScope = createScopedContainer();\n const layoutControllers: unknown[] = [];\n const layoutScopes: Array<WoodScopedContainer | null> = [];\n const createdLayoutScopes: WoodScopedContainer[] = [];\n\n try {\n for (let i = 0; i < preservedDepth; i += 1) {\n layoutControllers.push(undefined);\n layoutScopes.push(null);\n }\n\n for (let i = preservedDepth; i < entry.layoutControllerClasses.length; i += 1) {\n const LayoutController = entry.layoutControllerClasses[i];\n if (!LayoutController) {\n layoutControllers.push(undefined);\n layoutScopes.push(null);\n continue;\n }\n const preservable = entry.layoutPreserve?.[i] === true;\n let scope = pageScope;\n if (preservable) {\n scope = createScopedContainer();\n createdLayoutScopes.push(scope);\n }\n const controller = await resolveControllerInScope(\n scope,\n entry,\n LayoutController,\n \\`layout[\\${i}]\\`,\n );\n layoutControllers.push(controller);\n layoutScopes.push(preservable ? scope : null);\n }\n\n const controller = entry.controllerClass\n ? await resolveControllerInScope(pageScope, entry, entry.controllerClass, 'page')\n : undefined;\n\n return { controller, layoutControllers, pageScope, layoutScopes };\n } catch (error) {\n // Failed transitions must not orphan scopes: dispose everything this\n // set created before rethrowing.\n for (const scope of createdLayoutScopes) await disposeScopeQuietly(scope as any);\n await disposeScopeQuietly(pageScope as any);\n throw error;\n }\n}\n\nconst DEFAULT_VIEW_KEY = '${escapeSingleQuoted(defaultViewKey)}';\n\ntype WoodWindowBootstrapInfo = {\n ok?: boolean;\n windowId?: string;\n defaultRoute?: string;\n};\n\ntype WoodBootstrapBridgeLike = {\n __window?: {\n current?: () => Promise<WoodWindowBootstrapInfo>;\n };\n};\n\nfunction getWoodBridge(): WoodBootstrapBridgeLike | null {\n const globalObj = globalThis as typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n };\n };\n\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__ ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n\n return bridge as WoodBootstrapBridgeLike;\n}\n\nasync function resolveInitialEntry(): Promise<GeneratedAppViewEntry> {\n if (!DEFAULT_VIEW_KEY) {\n throw new Error('No views available in renderer manifest. Define at least one view in renderer OpenAPI YAML.');\n }\n\n const bridge = getWoodBridge();\n const bootstrapWindow = await bridge?.__window?.current?.();\n const requestedRoute = bootstrapWindow?.defaultRoute;\n\n if (requestedRoute) {\n const routeEntry = getViewManifestEntry(requestedRoute);\n if (routeEntry) {\n return routeEntry;\n }\n }\n\n const entry = getViewManifestEntry(DEFAULT_VIEW_KEY);\n if (!entry) {\n throw new Error(\\`Default view \"\\${DEFAULT_VIEW_KEY}\" is missing from generated view manifest.\\`);\n }\n\n return entry;\n}\n\nfunction isAutomationBuild(): boolean {\n try {\n const env = (import.meta as { env?: { DEV?: boolean; MODE?: string } }).env;\n return Boolean(env?.DEV) || env?.MODE === 'test';\n } catch {\n return false;\n }\n}\n\nexport async function app(target: Element) {\n const entry = await resolveInitialEntry();\n\n getNavigation().replace(entry.key, {}, {});\n\n // Dev/test-only Axe introspection surface; never present in production builds.\n if (isAutomationBuild()) {\n const bootstrapWindow = await getWoodBridge()?.__window?.current?.();\n installAutomationIntrospection({ windowId: bootstrapWindow?.windowId ?? null });\n }\n const initialSet = await createControllerSet(entry);\n\n return mount(NavigationShell, {\n target,\n props: {\n manifest: VIEW_MANIFEST,\n initialEntry: entry,\n initialControllerSet: initialSet,\n createControllerSetFn: createControllerSet,\n getViewManifestEntryFn: getViewManifestEntry,\n },\n });\n}\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAA2B;AAC3B,kBAAiB;AAEjB,mCAAoC;AAEpC,MAAM,mCAAmC;AAAA,EACvC;AAAA,EACA;AACF;AAWA,SAAS,kBAAkB,SAAiB,QAAwB;AAClE,MAAI,MAAM,YAAAA,QAAK,SAAS,SAAS,MAAM;AAEvC,MAAI,CAAC,IAAI,WAAW,GAAG,GAAG;AACxB,UAAM,OAAO;AAAA,EACf;AAEA,SAAO,IAAI,QAAQ,OAAO,GAAG;AAC/B;AAEA,SAAS,wBAAwB,cAAsB,SAAyB;AAC9E,MAAI,YAAAA,QAAK,WAAW,OAAO,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,QAAQ,QAAQ,OAAO,GAAG;AAChD,QAAM,mBAAmB,YAAAA,QAAK,SAAS,YAAY,EAAE,QAAQ,OAAO,GAAG;AAEvE,MAAI,cAAc,WAAW,mBAAmB,GAAG,GAAG;AACpD,UAAM,WAAW,cAAc,MAAM,iBAAiB,SAAS,CAAC;AAChE,WAAO,YAAAA,QAAK,QAAQ,cAAc,QAAQ;AAAA,EAC5C;AAEA,SAAO,YAAAA,QAAK,QAAQ,cAAc,aAAa;AACjD;AAEA,SAAS,QAAQ,MAAuB;AACtC,SAAO,GAAG,KAAK,UAAU,IAAI,KAAK,QAAQ;AAC5C;AAEA,SAAS,aAAa,QAAgB,OAAuB;AAC3D,QAAM,OAAO,MACV,QAAQ,kBAAkB,GAAG,EAC7B,KAAK,EACL,MAAM,KAAK,EACX,IAAI,CAAC,YAAY,QAAQ,OAAO,CAAC,EAAE,YAAY,IAAI,QAAQ,MAAM,CAAC,CAAC,EACnE,KAAK,EAAE;AAEV,SAAO,GAAG,MAAM,GAAG,QAAQ,WAAW;AACxC;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AACzD;AAEA,MAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,8BAA8B;AAAA,EAClC;AAAA,EACA;AACF;AAEA,SAAS,sBAAsB,eAAuB,cAAqC;AACzF,QAAM,aAAa,cAAc,KAAK;AACtC,QAAM,WAAW,wBAAwB,cAAc,UAAU;AAEjE,UAAI,sBAAW,QAAQ,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,YAAAA,QAAK,QAAQ,QAAQ,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,aAAW,UAAU,0BAA0B;AAC7C,UAAM,YAAY,WAAW;AAC7B,YAAI,sBAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,aAAW,aAAa,6BAA6B;AACnD,UAAM,YAAY,YAAAA,QAAK,QAAQ,UAAU,SAAS;AAClD,YAAI,sBAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,sBAAsB,aAAwB,cAA+B;AACpF,MAAI,YAAY,WAAW,EAAG,QAAO;AAErC,QAAM,kBAAkB,cAAc,KAAK;AAC3C,MAAI,CAAC,iBAAiB;AACpB,WAAO,QAAQ,YAAY,CAAC,CAAC;AAAA,EAC/B;AAEA,QAAM,gBAAgB,YAAY,IAAI,CAAC,SAAS,QAAQ,IAAI,CAAC;AAC7D,MAAI,cAAc,SAAS,eAAe,GAAG;AAC3C,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR,mBAAmB,eAAe,yDAAyD,cAAc,KAAK,IAAI,CAAC;AAAA,EACrH;AACF;AAEO,SAAS,oBACd,OACA,SACQ;AACR,QAAM,cAAc,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC,EAAE,cAAc,QAAQ,CAAC,CAAC,CAAC;AAClF,QAAM,iBAAiB,sBAAsB,aAAa,QAAQ,YAAY;AAE9E,QAAM,cAAwB,CAAC;AAC/B,QAAM,kBAAkB,oBAAI,IAAoB;AAChD,QAAM,oBAAoB,oBAAI,IAAoB;AAClD,QAAM,wBAAwB,oBAAI,IAAwD;AAC1F,QAAM,YAAY,oBAAI,IAAY;AAElC,QAAM,eAAe,CAAC,QAAgB,aAA6B;AACjE,UAAM,OAAO,aAAa,QAAQ,QAAQ;AAC1C,QAAI,OAAO;AACX,QAAI,SAAS;AACb,WAAO,UAAU,IAAI,IAAI,GAAG;AAC1B,aAAO,GAAG,IAAI,GAAG,MAAM;AACvB,gBAAU;AAAA,IACZ;AACA,cAAU,IAAI,IAAI;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,2BAA2B,CAAC,eAAuB,kBAAgC;AACvF,QAAI,sBAAsB,IAAI,aAAa,EAAG;AAE9C,UAAM,yBAAyB,sBAAsB,eAAe,QAAQ,YAAY;AACxF,QAAI,CAAC,wBAAwB;AAC3B,YAAM,IAAI;AAAA,QACR,4CAA4C,aAAa,eAAe,aAAa;AAAA,MAEvF;AAAA,IACF;AAEA,UAAM,aAAa,aAAa,kBAAkB,aAAa;AAC/D,UAAM,aAAa,kBAAkB,QAAQ,QAAQ,sBAAsB;AAC3E,0BAAsB,IAAI,eAAe,EAAE,YAAY,WAAW,CAAC;AACnE,gBAAY,KAAK,UAAU,UAAU,UAAU,UAAU,IAAI;AAAA,EAC/D;AAEA,aAAW,QAAQ,aAAa;AAC9B,QAAI,CAAC,gBAAgB,IAAI,KAAK,QAAQ,GAAG;AACvC,YAAM,aAAa,aAAa,QAAQ,KAAK,QAAQ;AACrD,sBAAgB,IAAI,KAAK,UAAU,UAAU;AAC7C,YAAM,MAAM,wBAAwB,QAAQ,cAAc,KAAK,QAAQ;AACvE,YAAM,aAAa,kBAAkB,QAAQ,QAAQ,GAAG;AACxD,kBAAY,KAAK,UAAU,UAAU,UAAU,UAAU,IAAI;AAAA,IAC/D;AAEA,eAAW,cAAc,KAAK,SAAS;AACrC,UAAI,kBAAkB,IAAI,UAAU,EAAG;AACvC,YAAM,aAAa,aAAa,UAAU,UAAU;AACpD,wBAAkB,IAAI,YAAY,UAAU;AAC5C,YAAM,MAAM,wBAAwB,QAAQ,cAAc,UAAU;AACpE,YAAM,aAAa,kBAAkB,QAAQ,QAAQ,GAAG;AACxD,kBAAY,KAAK,UAAU,UAAU,UAAU,UAAU,IAAI;AAAA,IAC/D;AAEA,QAAI,KAAK,YAAY;AACnB,+BAAyB,KAAK,YAAY,QAAQ,IAAI,CAAC;AAAA,IACzD;AAEA,eAAW,oBAAoB,KAAK,qBAAqB,CAAC,GAAG;AAC3D,UAAI,CAAC,iBAAkB;AACvB,+BAAyB,kBAAkB,QAAQ,IAAI,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,kBAAkB,YAAY,IAAI,CAAC,SAAS;AAChD,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,iBAAiB,gBAAgB,IAAI,KAAK,QAAQ;AACxD,UAAM,aAAa,KAAK,QAAQ,IAAI,CAAC,eAAe,kBAAkB,IAAI,UAAU,CAAE;AACtF,UAAM,iBAAiB,KAAK,aAAa,IAAI,mBAAmB,KAAK,UAAU,CAAC,MAAM;AACtF,UAAM,kBAAkB,KAAK,aACzB,sBAAsB,IAAI,KAAK,UAAU,EAAG,aAC5C;AACJ,UAAM,uBAAuB,KAAK;AAClC,QAAI,wBAAwB,qBAAqB,WAAW,KAAK,QAAQ,QAAQ;AAC/E,YAAM,IAAI;AAAA,QACR,kEAAkE,GAAG;AAAA,MACvE;AAAA,IACF;AACA,UAAM,iCAAiC,wBAAwB,MAAM;AAAA,MACnE,EAAE,QAAQ,KAAK,QAAQ,OAAO;AAAA,MAC9B,MAAM;AAAA,IACR;AACA,UAAM,wBAAwB,IAAI,+BAC/B,IAAI,CAAC,kBAAmB,gBAAgB,IAAI,mBAAmB,aAAa,CAAC,MAAM,WAAY,EAC/F,KAAK,IAAI,CAAC;AACb,UAAM,0BAA0B,IAAI,+BACjC,IAAI,CAAC,kBAAmB,gBAAgB,sBAAsB,IAAI,aAAa,EAAG,aAAa,WAAY,EAC3G,KAAK,IAAI,CAAC;AACb,UAAM,iBAAiB,KAAK,kBAAkB,MAAM;AAAA,MAClD,EAAE,QAAQ,KAAK,QAAQ,OAAO;AAAA,MAC9B,MAAM;AAAA,IACR;AACA,QAAI,eAAe,WAAW,KAAK,QAAQ,QAAQ;AACjD,YAAM,IAAI;AAAA,QACR,gEAAgE,GAAG;AAAA,MACrE;AAAA,IACF;AAEA,WAAO,MAAM,mBAAmB,GAAG,CAAC;AAAA,YAC5B,mBAAmB,GAAG,CAAC;AAAA,mBAChB,mBAAmB,KAAK,UAAU,CAAC;AAAA,iBACrC,mBAAmB,KAAK,QAAQ,CAAC;AAAA,iBACjC,mBAAmB,KAAK,QAAQ,CAAC;AAAA,mBAC/B,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,yBACtB,qBAAqB;AAAA,sBACxB,KAAK,UAAU,cAAc,CAAC;AAAA,kBAClC,cAAc;AAAA,+BACD,uBAAuB;AAAA,uBAC/B,eAAe;AAAA,YAC1B,cAAc;AAAA,gBACV,WAAW,KAAK,IAAI,CAAC;AAAA;AAAA,EAEnC,CAAC;AAED,SAAO,OAAG,kDAAoB,gCAAgC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM/D,YAAY,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBtB,gBAAgB,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BA0IA,mBAAmB,cAAc,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuF9D;","names":["path"]}
|