@noego/wood 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +31 -0
  2. package/dist/client/index.cjs +18 -11
  3. package/dist/client/index.cjs.map +1 -1
  4. package/dist/client/index.d.cts +21 -1
  5. package/dist/client/index.d.ts +21 -1
  6. package/dist/client/index.js +17 -11
  7. package/dist/client/index.js.map +1 -1
  8. package/dist/codegen/renderer_app_generator.cjs +2 -9
  9. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  10. package/dist/codegen/renderer_app_generator.js +2 -9
  11. package/dist/codegen/renderer_app_generator.js.map +1 -1
  12. package/dist/controller/cleanup.cjs +39 -0
  13. package/dist/controller/cleanup.cjs.map +1 -0
  14. package/dist/controller/cleanup.d.cts +4 -0
  15. package/dist/controller/cleanup.d.ts +4 -0
  16. package/dist/controller/cleanup.js +15 -0
  17. package/dist/controller/cleanup.js.map +1 -0
  18. package/dist/controller/controller_lifecycle.cjs +26 -2
  19. package/dist/controller/controller_lifecycle.cjs.map +1 -1
  20. package/dist/controller/controller_lifecycle.d.cts +3 -1
  21. package/dist/controller/controller_lifecycle.d.ts +3 -1
  22. package/dist/controller/controller_lifecycle.js +24 -1
  23. package/dist/controller/controller_lifecycle.js.map +1 -1
  24. package/dist/frontend/frontend_lifecycle.cjs +5 -7
  25. package/dist/frontend/frontend_lifecycle.cjs.map +1 -1
  26. package/dist/frontend/frontend_lifecycle.d.cts +1 -14
  27. package/dist/frontend/frontend_lifecycle.d.ts +1 -14
  28. package/dist/frontend/frontend_lifecycle.js +5 -7
  29. package/dist/frontend/frontend_lifecycle.js.map +1 -1
  30. package/dist/frontend/frontend_surface.cjs +130 -104
  31. package/dist/frontend/frontend_surface.cjs.map +1 -1
  32. package/dist/frontend/frontend_surface.js +131 -105
  33. package/dist/frontend/frontend_surface.js.map +1 -1
  34. package/dist/frontend/work_registry.cjs +29 -4
  35. package/dist/frontend/work_registry.cjs.map +1 -1
  36. package/dist/frontend/work_registry.d.cts +2 -0
  37. package/dist/frontend/work_registry.d.ts +2 -0
  38. package/dist/frontend/work_registry.js +29 -4
  39. package/dist/frontend/work_registry.js.map +1 -1
  40. package/dist/index.d.cts +2 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/ipc/index.cjs +6 -2
  43. package/dist/ipc/index.cjs.map +1 -1
  44. package/dist/ipc/index.d.cts +9 -0
  45. package/dist/ipc/index.d.ts +9 -0
  46. package/dist/ipc/index.js +6 -2
  47. package/dist/ipc/index.js.map +1 -1
  48. package/dist/main/index.cjs +48 -34
  49. package/dist/main/index.cjs.map +1 -1
  50. package/dist/main/index.d.cts +11 -2
  51. package/dist/main/index.d.ts +11 -2
  52. package/dist/main/index.js +51 -39
  53. package/dist/main/index.js.map +1 -1
  54. package/dist/main/startup_failure_handler.cjs +67 -0
  55. package/dist/main/startup_failure_handler.cjs.map +1 -0
  56. package/dist/main/startup_failure_handler.d.cts +16 -0
  57. package/dist/main/startup_failure_handler.d.ts +16 -0
  58. package/dist/main/startup_failure_handler.js +43 -0
  59. package/dist/main/startup_failure_handler.js.map +1 -0
  60. package/dist/router/index.d.cts +3 -0
  61. package/dist/router/index.d.ts +3 -0
  62. package/dist/router/wood_router.cjs +204 -123
  63. package/dist/router/wood_router.cjs.map +1 -1
  64. package/dist/router/wood_router.d.cts +20 -0
  65. package/dist/router/wood_router.d.ts +20 -0
  66. package/dist/router/wood_router.js +206 -125
  67. package/dist/router/wood_router.js.map +1 -1
  68. package/dist/runtime/index.cjs +13 -0
  69. package/dist/runtime/index.cjs.map +1 -1
  70. package/dist/runtime/index.d.cts +8 -4
  71. package/dist/runtime/index.d.ts +8 -4
  72. package/dist/runtime/index.js +8 -0
  73. package/dist/runtime/index.js.map +1 -1
  74. package/dist/runtime/renderer_config_extension.cjs +1 -2
  75. package/dist/runtime/renderer_config_extension.cjs.map +1 -1
  76. package/dist/runtime/renderer_config_extension.d.cts +1 -1
  77. package/dist/runtime/renderer_config_extension.d.ts +1 -1
  78. package/dist/runtime/renderer_config_extension.js +1 -2
  79. package/dist/runtime/renderer_config_extension.js.map +1 -1
  80. package/dist/runtime/renderer_log_dispatcher.cjs +2 -0
  81. package/dist/runtime/renderer_log_dispatcher.cjs.map +1 -1
  82. package/dist/runtime/renderer_log_dispatcher.d.cts +1 -1
  83. package/dist/runtime/renderer_log_dispatcher.d.ts +1 -1
  84. package/dist/runtime/renderer_log_dispatcher.js +1 -0
  85. package/dist/runtime/renderer_log_dispatcher.js.map +1 -1
  86. package/dist/runtime/runtime.cjs +107 -57
  87. package/dist/runtime/runtime.cjs.map +1 -1
  88. package/dist/runtime/runtime.d.cts +12 -0
  89. package/dist/runtime/runtime.d.ts +12 -0
  90. package/dist/runtime/runtime.js +110 -59
  91. package/dist/runtime/runtime.js.map +1 -1
  92. package/dist/runtime/runtime_module.cjs +58 -0
  93. package/dist/runtime/runtime_module.cjs.map +1 -0
  94. package/dist/runtime/runtime_module.d.cts +22 -0
  95. package/dist/runtime/runtime_module.d.ts +22 -0
  96. package/dist/runtime/runtime_module.js +30 -0
  97. package/dist/runtime/runtime_module.js.map +1 -0
  98. package/dist/testing/create_controller_harness.d.cts +3 -0
  99. package/dist/testing/create_controller_harness.d.ts +3 -0
  100. package/dist/testing/create_router_bridge.d.cts +3 -0
  101. package/dist/testing/create_router_bridge.d.ts +3 -0
  102. package/dist/testing/create_test_router.d.cts +3 -0
  103. package/dist/testing/create_test_router.d.ts +3 -0
  104. package/dist/testing/index.cjs +3 -0
  105. package/dist/testing/index.cjs.map +1 -1
  106. package/dist/testing/index.d.cts +6 -1
  107. package/dist/testing/index.d.ts +6 -1
  108. package/dist/testing/index.js +2 -0
  109. package/dist/testing/index.js.map +1 -1
  110. package/dist/testing/registration_only_root.cjs +168 -0
  111. package/dist/testing/registration_only_root.cjs.map +1 -0
  112. package/dist/testing/registration_only_root.d.cts +46 -0
  113. package/dist/testing/registration_only_root.d.ts +46 -0
  114. package/dist/testing/registration_only_root.js +141 -0
  115. package/dist/testing/registration_only_root.js.map +1 -0
  116. package/dist/testing/test_wood_config.cjs +382 -70
  117. package/dist/testing/test_wood_config.cjs.map +1 -1
  118. package/dist/testing/test_wood_config.d.cts +25 -1
  119. package/dist/testing/test_wood_config.d.ts +25 -1
  120. package/dist/testing/test_wood_config.js +388 -71
  121. package/dist/testing/test_wood_config.js.map +1 -1
  122. package/dist/testing/wood_dom_animation.cjs +213 -0
  123. package/dist/testing/wood_dom_animation.cjs.map +1 -0
  124. package/dist/testing/wood_dom_animation.d.cts +89 -0
  125. package/dist/testing/wood_dom_animation.d.ts +89 -0
  126. package/dist/testing/wood_dom_animation.js +185 -0
  127. package/dist/testing/wood_dom_animation.js.map +1 -0
  128. package/dist/testing/wood_env_renderer.cjs +81 -32
  129. package/dist/testing/wood_env_renderer.cjs.map +1 -1
  130. package/dist/testing/wood_env_renderer.d.cts +32 -1
  131. package/dist/testing/wood_env_renderer.d.ts +32 -1
  132. package/dist/testing/wood_env_renderer.js +84 -32
  133. package/dist/testing/wood_env_renderer.js.map +1 -1
  134. package/dist/transport/in_process_router_transport.d.cts +3 -0
  135. package/dist/transport/in_process_router_transport.d.ts +3 -0
  136. package/dist/transport/index.d.cts +3 -0
  137. package/dist/transport/index.d.ts +3 -0
  138. package/package.json +7 -6
  139. package/src/components/NavigationShell.svelte +12 -7
  140. package/src/controller/cleanup.ts +8 -0
  141. package/src/controller/controller_lifecycle.ts +24 -0
package/README.md CHANGED
@@ -6,6 +6,37 @@ Declarative Electron desktop apps using the noego ecosystem.
6
6
  runtime wiring, code generation, tracing, testing helpers, and CLI utilities for
7
7
  building Electron applications with Svelte-based renderer flows.
8
8
 
9
+ ## Runtime ownership migration (0.6.0)
10
+
11
+ - `runtime()` creates a fresh root when no `container` is supplied. A supplied
12
+ root remains caller-owned. The returned `dispose(timeoutMs?)` stops admission,
13
+ removes its IPC handlers/listeners, and drains operations. A timeout rejects;
14
+ pending work retains an internally owned root until disposal is retried.
15
+ - `installWoodRuntime(root, options)` from `@noego/wood/runtime` installs the same
16
+ lazy router, validation, middleware and controller-resolution registrations
17
+ used by production and `testWood`. Apply replacements before resolving
18
+ `WoodRouter`; pure helpers remain ordinary functions.
19
+ - `testWood` flat `classes/functions/values/methods` calls target the sole owning
20
+ selected root. Unknown tokens fail; ambiguous tokens require
21
+ `.main(main => main.values(...))` or `.renderer(renderer => renderer.values(...))`.
22
+ Explicit root composition can introduce registrations. `.use()` retains its
23
+ main-root default. Compose/configure hooks must register only; resolve in
24
+ `start()` or after build. Cleanup reports every failing owner.
25
+ - This release requires `@noego/testing >=0.6.0` for `build({ root })` and
26
+ `@noego/ioc ^0.8.0` for lifecycle ownership. Wood checks the testing capability
27
+ and fails explicitly with older artifacts.
28
+ - `startWoodMain` always owns a fresh root. Its former `scope` option (including
29
+ the `appBoot` result option) is deprecated and ignored; migrate registrations
30
+ to `configureContainer` rather than relying on a process-global named root.
31
+ - Prefer root-injected `Ipc` and explicit `rendererTraceBatchReceiver`,
32
+ `rendererLogDispatcher`, and `rendererConfigExtension`. `legacyGlobals: true`
33
+ explicitly enables the old observation/push adapters during migration;
34
+ global convenience exports remain compatibility APIs, not isolated owners.
35
+ - Frontend `act`/`settle` bound registered finite work, not floating promises or
36
+ arbitrary timers. Preserved layouts retain their scopes; cleanup failures
37
+ remain observable. `NavigationShell.onLifecycleError` observes navigation and
38
+ teardown errors without preventing the remaining cleanup steps.
39
+
9
40
  ## Install
10
41
 
11
42
  ```bash
@@ -33,6 +33,7 @@ __export(client_exports, {
33
33
  clearRendererLogTransportForTests: () => clearRendererLogTransportForTests,
34
34
  clearTraceHubForTests: () => import_tracing.clearTraceHubForTests,
35
35
  configureRendererLogTransport: () => import_renderer_log_transport2.configureRendererLogTransport,
36
+ createAppWithBridge: () => createAppWithBridge,
36
37
  createLogger: () => createLogger,
37
38
  createSvelteBoot: () => createSvelteBoot,
38
39
  flushRendererLogs: () => import_renderer_log_transport2.flushRendererLogs,
@@ -157,8 +158,7 @@ function validateManifestEntry(entry, index) {
157
158
  );
158
159
  }
159
160
  }
160
- function createAppInstance() {
161
- const bridge = resolveWoodBridge();
161
+ function createAppInstance(bridge) {
162
162
  const manifest = resolveRpcManifest(bridge);
163
163
  const app = {};
164
164
  manifest.forEach((entry, index) => {
@@ -261,10 +261,13 @@ function createAppInstance() {
261
261
  });
262
262
  return app;
263
263
  }
264
+ function createAppWithBridge(bridge) {
265
+ return createAppInstance(bridge);
266
+ }
264
267
  function getApp() {
265
268
  const globalObj = globalThis;
266
269
  if (!globalObj[CLIENT_APP_SYMBOL]) {
267
- globalObj[CLIENT_APP_SYMBOL] = createAppInstance();
270
+ globalObj[CLIENT_APP_SYMBOL] = createAppInstance(resolveWoodBridge());
268
271
  }
269
272
  return globalObj[CLIENT_APP_SYMBOL];
270
273
  }
@@ -272,33 +275,36 @@ function clearClientAppForTests() {
272
275
  const globalObj = globalThis;
273
276
  delete globalObj[CLIENT_APP_SYMBOL];
274
277
  }
275
- function resolveWindowControlsBridgeMethod(method) {
276
- const bridge = resolveWoodBridge();
277
- const fn = bridge.__window?.[method];
278
+ function resolveWindowControlsBridgeMethod(bridge, method) {
279
+ const resolvedBridge = bridge ?? resolveWoodBridge();
280
+ const fn = resolvedBridge.__window?.[method];
278
281
  if (typeof fn !== "function") {
279
282
  throw new Error(`Wood bridge does not expose "__window.${String(method)}".`);
280
283
  }
281
284
  return fn;
282
285
  }
283
286
  class WindowControls {
287
+ constructor(bridge) {
288
+ this.bridge = bridge;
289
+ }
284
290
  async open(windowId, options) {
285
- const open = resolveWindowControlsBridgeMethod("open");
291
+ const open = resolveWindowControlsBridgeMethod(this.bridge, "open");
286
292
  await open(windowId, options);
287
293
  }
288
294
  async close(windowId) {
289
- const close = resolveWindowControlsBridgeMethod("close");
295
+ const close = resolveWindowControlsBridgeMethod(this.bridge, "close");
290
296
  await close(windowId);
291
297
  }
292
298
  async focus(windowId) {
293
- const focus = resolveWindowControlsBridgeMethod("focus");
299
+ const focus = resolveWindowControlsBridgeMethod(this.bridge, "focus");
294
300
  await focus(windowId);
295
301
  }
296
302
  async minimize(windowId) {
297
- const minimize = resolveWindowControlsBridgeMethod("minimize");
303
+ const minimize = resolveWindowControlsBridgeMethod(this.bridge, "minimize");
298
304
  await minimize(windowId);
299
305
  }
300
306
  async maximize(windowId) {
301
- const maximize = resolveWindowControlsBridgeMethod("maximize");
307
+ const maximize = resolveWindowControlsBridgeMethod(this.bridge, "maximize");
302
308
  await maximize(windowId);
303
309
  }
304
310
  }
@@ -526,6 +532,7 @@ function createSvelteBoot(mountRoot) {
526
532
  clearRendererLogTransportForTests,
527
533
  clearTraceHubForTests,
528
534
  configureRendererLogTransport,
535
+ createAppWithBridge,
529
536
  createLogger,
530
537
  createSvelteBoot,
531
538
  flushRendererLogs,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.cjs';\nimport 'reflect-metadata';\nimport { Container, LoadAs } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation/index.js';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport { setWoodAppEnv } from './app_env.js';\nexport { getWoodAppEnv, type WoodAppEnv } from './app_env.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n setRendererTraceTransport,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerLifecycle,\n ControllerState,\n PageController,\n} from './controller_contract.js';\nimport {\n RendererLogTransport,\n getRendererLogTransport,\n clearRendererLogTransportForTests as clearRendererLogTransportSingletonForTests,\n} from './renderer_log_transport.js';\nexport {\n configureRendererLogTransport,\n flushRendererLogs,\n type RendererLogTransportConfig,\n} from './renderer_log_transport.js';\n\n/** Resets the renderer log transport singleton, its bridge wiring latch, and any app sender. */\nexport function clearRendererLogTransportForTests(): void {\n rendererLogWiringDone = false;\n appRendererLogSender = null;\n rendererBootConfig = null;\n clearRendererLogTransportSingletonForTests();\n}\n\n/** An app-installed outlet for admitted renderer log batches (e.g. a WebSocket to a log host). */\nexport type RendererLogSender = (batch: unknown) => void;\n\nlet appRendererLogSender: RendererLogSender | null = null;\n\n/**\n * Installs (or with `null`, removes) an app-owned batch sender that takes\n * precedence over the preload `__logBatch` IPC path. Records already buffered\n * are flushed through the previous outlet first so nothing is reordered or\n * lost across the swap. A sender that throws falls back to the preload/legacy\n * chain for that batch, exactly like the default outlet.\n */\nexport function setRendererLogSender(sender: RendererLogSender | null): void {\n const transport = getRendererLogTransport();\n transport.flush();\n appRendererLogSender = sender;\n rendererLogWiringDone = false;\n ensureRendererLogWiring(transport);\n}\n\n/** The most recent `__woodConfig` payload received in `startWood`, or null before boot. */\nexport function getRendererBootConfig(): Readonly<Record<string, unknown>> | null {\n return rendererBootConfig;\n}\n\nlet rendererBootConfig: Readonly<Record<string, unknown>> | null = null;\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\ntype WoodBridgeLike = Record<string, unknown> & {\n __rpcManifest?: WoodRpcManifestEntry[];\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __logBatch?: (batch: unknown) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n};\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: object;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(): App {\n const bridge = resolveWoodBridge();\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\nexport function getApp<TApp extends object = App>(): TApp {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance();\n }\n return globalObj[CLIENT_APP_SYMBOL] as TApp;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const bridge = resolveWoodBridge();\n const fn = bridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >('open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >('focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >('maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error' | (string & {});\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n logAt(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n this.log(level, rawMessage, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n const transport = getRendererLogTransport();\n ensureRendererLogWiring(transport);\n transport.admit(level, this.loggerName, message, context);\n }\n}\n\nlet rendererLogWiringDone = false;\n\n/**\n * Lazily binds the renderer log pipe's outlet to the preload bridge: batches\n * ride __logBatch (admitted records, one IPC message per flush); the per-entry\n * legacy __log channel and finally the console act as fallbacks when the\n * bridge predates batching or is absent (tests, plain browsers).\n */\nfunction ensureRendererLogWiring(transport: RendererLogTransport): void {\n if (rendererLogWiringDone) return;\n\n let bridge: WoodBridgeLike | null = null;\n try {\n bridge = resolveWoodBridge();\n } catch {\n bridge = null;\n }\n\n const preloadSend = bridge && typeof bridge.__logBatch === 'function'\n ? bridge.__logBatch.bind(bridge)\n : null;\n const appSend = appRendererLogSender;\n\n if (appSend) {\n // App sender wins; a throw here degrades to the preload batch path for\n // that batch, then to the per-record fallback if that throws too.\n transport.setSender((batch) => {\n try {\n appSend(batch);\n } catch (error) {\n if (!preloadSend) throw error;\n preloadSend(batch);\n }\n });\n } else if (preloadSend) {\n transport.setSender((batch) => preloadSend(batch));\n } else {\n transport.setSender(null);\n }\n\n const legacyLog = bridge && typeof bridge.__log === 'function' ? bridge.__log.bind(bridge) : null;\n transport.setFallback((record) => {\n if (legacyLog) {\n try {\n legacyLog(record.level, record.logger, record.message, record.context);\n return;\n } catch {\n // fall through to console\n }\n }\n writeConsole(record.level, record.logger, record.message, record.context);\n });\n\n // Only latch once a bridge was found (or an app sender makes the bridge\n // optional); keep retrying while both are absent so a late-injected bridge\n // (tests, slow preload) still gets wired.\n rendererLogWiringDone = bridge !== null || appSend !== null;\n}\n\ntype RendererLoggerShape = {\n logAt(level: RendererLogLevel, message: unknown, context?: unknown): void;\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureRuntime('renderer');\n\n // Explicit Singleton lifetime: these factories return renderer-root\n // globals, and registerFunction defaults to Transient — which ioc >= 0.5's\n // captive-lifetime validation rejects under singleton dependents.\n const singleton = { loadAs: LoadAs.Singleton };\n container.registerFunction(Navigation, () => getNavigation(), singleton);\n container.registerFunction(App, () => getApp(), singleton);\n container.registerFunction(Ipc, () => getIpc(), singleton);\n container.registerFunction(WindowControls, () => getWindowControls(), singleton);\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration, singleton);\n}\n\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\ntype RendererTraceBootstrapConfiguration = {\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: string;\n rendererTraceBatchEnabled?: boolean;\n appEnv?: string;\n};\n\nfunction configureRendererTraceTransport(config: RendererTraceBootstrapConfiguration | null): void {\n const globalObj = globalThis as Record<string, unknown>;\n if (config?.rendererTraceBatchEnabled) {\n const batchGlobal = globalObj.__NOEGO_WOOD_TRACE_BATCH__ as\n | { send?: (data: unknown) => unknown }\n | undefined;\n if (batchGlobal && typeof batchGlobal.send === 'function') {\n const sender = batchGlobal.send.bind(batchGlobal);\n setRendererTraceTransport((payload: unknown) => {\n sender(payload);\n });\n return;\n }\n }\n\n const traceGlobal = globalObj.__NOEGO_TRACE__ as\n | { send?: (data: unknown) => void }\n | undefined;\n if (traceGlobal && typeof traceGlobal.send === 'function') {\n const sender = traceGlobal.send.bind(traceGlobal);\n setRendererTraceTransport((payload: unknown) => sender(payload));\n }\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.cjs';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Fetch framework config from the main process and apply renderer trace\n // controls before the first render.\n let rendererTraceConfig: RendererTraceBootstrapConfiguration | null = null;\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as RendererTraceBootstrapConfiguration | null;\n rendererTraceConfig = config;\n rendererBootConfig = config && typeof config === 'object'\n ? Object.freeze({ ...(config as Record<string, unknown>) })\n : null;\n setWoodAppEnv(config?.appEnv);\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n if (typeof config?.rendererTraceEnabled === 'boolean') {\n traceConfiguration?.configureEnabled(config.rendererTraceEnabled);\n }\n const level = config?.rendererTraceMinLevel;\n if (typeof level === 'string' && level.trim().length > 0) {\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n try {\n configureRendererTraceTransport(rendererTraceConfig);\n } catch {\n // Trace transport selection is best-effort; the internal IPC bridge remains available.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { setRendererTraceTransport };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["import_app_env","import_renderer_log_transport","import_tracing","clearRendererLogTransportSingletonForTests","clearNavigationSingletonForTests"]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"sourcesContent":["import './global_polyfill.cjs';\nimport 'reflect-metadata';\nimport { Container, LoadAs } from '@noego/ioc';\nimport {\n Navigation,\n getNavigation,\n clearNavigationForTests as clearNavigationSingletonForTests,\n} from '../navigation/index.js';\nimport { Ipc, getIpc } from '../ipc/index.js';\nimport { setWoodAppEnv } from './app_env.js';\nexport { getWoodAppEnv, type WoodAppEnv } from './app_env.js';\nimport {\n TraceProviderConfiguration,\n clearTraceHubForTests,\n emitRendererTrace,\n setRendererTraceTransport,\n} from '../tracing/index.js';\nexport type {\n ControllerEvents,\n ControllerInput,\n ControllerLifecycle,\n ControllerState,\n PageController,\n} from './controller_contract.js';\nimport {\n RendererLogTransport,\n getRendererLogTransport,\n clearRendererLogTransportForTests as clearRendererLogTransportSingletonForTests,\n} from './renderer_log_transport.js';\nexport {\n configureRendererLogTransport,\n flushRendererLogs,\n type RendererLogTransportConfig,\n} from './renderer_log_transport.js';\n\n/** Resets the renderer log transport singleton, its bridge wiring latch, and any app sender. */\nexport function clearRendererLogTransportForTests(): void {\n rendererLogWiringDone = false;\n appRendererLogSender = null;\n rendererBootConfig = null;\n clearRendererLogTransportSingletonForTests();\n}\n\n/** An app-installed outlet for admitted renderer log batches (e.g. a WebSocket to a log host). */\nexport type RendererLogSender = (batch: unknown) => void;\n\nlet appRendererLogSender: RendererLogSender | null = null;\n\n/**\n * Installs (or with `null`, removes) an app-owned batch sender that takes\n * precedence over the preload `__logBatch` IPC path. Records already buffered\n * are flushed through the previous outlet first so nothing is reordered or\n * lost across the swap. A sender that throws falls back to the preload/legacy\n * chain for that batch, exactly like the default outlet.\n */\nexport function setRendererLogSender(sender: RendererLogSender | null): void {\n const transport = getRendererLogTransport();\n transport.flush();\n appRendererLogSender = sender;\n rendererLogWiringDone = false;\n ensureRendererLogWiring(transport);\n}\n\n/** The most recent `__woodConfig` payload received in `startWood`, or null before boot. */\nexport function getRendererBootConfig(): Readonly<Record<string, unknown>> | null {\n return rendererBootConfig;\n}\n\nlet rendererBootConfig: Readonly<Record<string, unknown>> | null = null;\n\nconst CLIENT_CONTAINER_SYMBOL = Symbol.for('@noego/wood.client.container');\nconst CLIENT_APP_SYMBOL = Symbol.for('@noego/wood.client.app');\nconst CLIENT_WINDOW_CONTROLS_SYMBOL = Symbol.for('@noego/wood.client.window-controls');\nconst CLIENT_LOGGER_REGISTRY_SYMBOL = Symbol.for('@noego/wood.client.logger-registry');\nconst INTERNAL_BRIDGE_GLOBAL_KEY = '__NOEGO_WOOD_BRIDGE__';\n\nexport const WOOD_INTERNAL_BRIDGE_KEY = INTERNAL_BRIDGE_GLOBAL_KEY;\n\nexport interface WoodRpcManifestEntry {\n channel: string;\n controller: string;\n action: string;\n path: string[];\n hasInput: boolean;\n}\n\ntype WoodBridgeRpcFunction = (data?: unknown) => Promise<unknown>;\n\nexport interface WoodClientBridge extends Record<string, unknown> {\n __rpcManifest?: WoodRpcManifestEntry[];\n on?: (channel: string, callback: (...args: unknown[]) => void) => () => void;\n __window?: {\n current?: () => Promise<unknown>;\n open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;\n close?: (windowId?: string) => Promise<unknown>;\n focus?: (windowId: string) => Promise<unknown>;\n minimize?: (windowId?: string) => Promise<unknown>;\n maximize?: (windowId?: string) => Promise<unknown>;\n };\n __log?: (\n level: string,\n logger: string,\n message: string,\n context?: unknown,\n ) => void;\n __logBatch?: (batch: unknown) => void;\n __woodConfig?: () => Promise<unknown>;\n __trace?: (event: unknown) => void;\n}\n\ntype WoodBridgeLike = WoodClientBridge;\n\ntype GlobalWithClientContainer = typeof globalThis & {\n [CLIENT_CONTAINER_SYMBOL]?: any;\n};\n\ntype GlobalWithClientApp = typeof globalThis & {\n [CLIENT_APP_SYMBOL]?: object;\n};\n\ntype GlobalWithWindowControls = typeof globalThis & {\n [CLIENT_WINDOW_CONTROLS_SYMBOL]?: WindowControls;\n};\n\ntype GlobalWithLoggerRegistry = typeof globalThis & {\n [CLIENT_LOGGER_REGISTRY_SYMBOL]?: Map<string, RendererLogger>;\n};\n\ninterface WoodContainerRegistration {\n registerFunction?(\n token: any,\n resolver: (...args: any[]) => unknown,\n options?: Record<string, unknown>,\n ): void;\n}\n\nexport class App {\n [controllerName: string]: unknown;\n}\n\nfunction resolveWoodBridge(): WoodBridgeLike {\n const globalObj = globalThis as typeof globalThis & {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n window?: {\n [INTERNAL_BRIDGE_GLOBAL_KEY]?: unknown;\n wood?: unknown;\n };\n };\n\n const bridge = globalObj[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.window?.[INTERNAL_BRIDGE_GLOBAL_KEY]\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n throw new Error(\n `Wood bridge is not available on globalThis.${INTERNAL_BRIDGE_GLOBAL_KEY}.`,\n );\n }\n\n return bridge as WoodBridgeLike;\n}\n\nfunction resolveRpcManifest(bridge: WoodBridgeLike): WoodRpcManifestEntry[] {\n const manifest = bridge.__rpcManifest;\n if (!Array.isArray(manifest)) {\n throw new Error(\n 'Wood bridge is missing \"__rpcManifest\". Regenerate preload with the latest @noego/wood.',\n );\n }\n\n return manifest;\n}\n\nfunction resolveBridgeFunction(\n bridge: WoodBridgeLike,\n pathSegments: string[],\n operationKey: string,\n): WoodBridgeRpcFunction {\n let current: unknown = bridge;\n\n for (const segment of pathSegments) {\n if (!current || typeof current !== 'object') {\n throw new Error(\n `Wood bridge operation \"${operationKey}\" is missing path \"${pathSegments.join('.')}\".`,\n );\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n if (typeof current !== 'function') {\n throw new Error(\n `Wood bridge path \"${pathSegments.join('.')}\" for \"${operationKey}\" is not callable.`,\n );\n }\n\n return current as WoodBridgeRpcFunction;\n}\n\nfunction assertSerializable(value: unknown, operationKey: string): void {\n if (typeof structuredClone !== 'function') {\n return;\n }\n\n try {\n structuredClone(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(\n `App RPC \"${operationKey}\" payload is not serializable: ${reason}`,\n );\n }\n}\n\nfunction extractTraceConversationId(value: unknown): number | string | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const record = value as Record<string, unknown>;\n if (typeof record.conversationId === 'number' || typeof record.conversationId === 'string') {\n return record.conversationId;\n }\n if (typeof record.threadId === 'number' || typeof record.threadId === 'string') {\n return record.threadId;\n }\n\n return undefined;\n}\n\nfunction validateManifestEntry(entry: WoodRpcManifestEntry, index: number): void {\n if (!entry.channel || !entry.controller || !entry.action) {\n throw new Error(`Invalid Wood RPC manifest entry at index ${index}.`);\n }\n if (!Array.isArray(entry.path) || entry.path.length === 0) {\n throw new Error(\n `Invalid Wood RPC manifest path for \"${entry.controller}.${entry.action}\".`,\n );\n }\n}\n\nfunction createAppInstance(bridge: WoodClientBridge): App {\n const manifest = resolveRpcManifest(bridge);\n const app: Record<string, Record<string, (...args: unknown[]) => Promise<unknown>>> = {};\n\n manifest.forEach((entry, index) => {\n validateManifestEntry(entry, index);\n\n const operationKey = `${entry.controller}.${entry.action}`;\n const invoke = resolveBridgeFunction(bridge, entry.path, operationKey);\n\n if (!app[entry.controller]) {\n app[entry.controller] = {};\n }\n\n if (app[entry.controller][entry.action]) {\n throw new Error(\n `Duplicate RPC action \"${operationKey}\" in Wood manifest. Actions must be unique per controller.`,\n );\n }\n\n app[entry.controller][entry.action] = (...args: unknown[]): Promise<unknown> => {\n if (entry.hasInput) {\n if (args.length !== 1) {\n throw new Error(\n `App RPC \"${operationKey}\" expects exactly one payload argument.`,\n );\n }\n assertSerializable(args[0], operationKey);\n const conversationId = extractTraceConversationId(args[0]);\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke(args[0])\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n ...(conversationId !== undefined ? { conversationId } : {}),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n }\n\n if (args.length !== 0) {\n throw new Error(\n `App RPC \"${operationKey}\" expects no arguments.`,\n );\n }\n\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.request',\n payload: {\n operationKey,\n channel: entry.channel,\n hasInput: entry.hasInput,\n },\n });\n return invoke()\n .then((result) => {\n emitRendererTrace('debug', {\n source: 'wood.client.app',\n type: 'rpc.response',\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'ok',\n },\n });\n return result;\n })\n .catch((error) => {\n emitRendererTrace('error', {\n source: 'wood.client.app',\n type: 'rpc.response',\n message: error instanceof Error ? error.message : String(error),\n payload: {\n operationKey,\n channel: entry.channel,\n outcome: 'error',\n },\n });\n throw error;\n });\n };\n });\n\n return app as unknown as App;\n}\n\n/** Builds an uncached renderer App client owned by the supplied bridge. */\nexport function createAppWithBridge<TApp extends object = App>(bridge: WoodClientBridge): TApp {\n return createAppInstance(bridge) as TApp;\n}\n\nexport function getApp<TApp extends object = App>(): TApp {\n const globalObj = globalThis as GlobalWithClientApp;\n if (!globalObj[CLIENT_APP_SYMBOL]) {\n globalObj[CLIENT_APP_SYMBOL] = createAppInstance(resolveWoodBridge());\n }\n return globalObj[CLIENT_APP_SYMBOL] as TApp;\n}\n\n/** Test-only helper to clear the client App singleton. */\nexport function clearClientAppForTests(): void {\n const globalObj = globalThis as GlobalWithClientApp;\n delete globalObj[CLIENT_APP_SYMBOL];\n}\n\nfunction resolveWindowControlsBridgeMethod<T>(\n bridge: WoodClientBridge | undefined,\n method: keyof NonNullable<WoodBridgeLike['__window']>,\n): T {\n const resolvedBridge = bridge ?? resolveWoodBridge();\n const fn = resolvedBridge.__window?.[method];\n if (typeof fn !== 'function') {\n throw new Error(`Wood bridge does not expose \"__window.${String(method)}\".`);\n }\n return fn as unknown as T;\n}\n\nexport class WindowControls {\n constructor(private readonly bridge?: WoodClientBridge) {}\n\n async open(windowId: string, options?: Record<string, unknown>): Promise<void> {\n const open = resolveWindowControlsBridgeMethod<\n (id: string, opts?: Record<string, unknown>) => Promise<unknown>\n >(this.bridge, 'open');\n await open(windowId, options);\n }\n\n async close(windowId?: string): Promise<void> {\n const close = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >(this.bridge, 'close');\n await close(windowId);\n }\n\n async focus(windowId: string): Promise<void> {\n const focus = resolveWindowControlsBridgeMethod<\n (id: string) => Promise<unknown>\n >(this.bridge, 'focus');\n await focus(windowId);\n }\n\n async minimize(windowId?: string): Promise<void> {\n const minimize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >(this.bridge, 'minimize');\n await minimize(windowId);\n }\n\n async maximize(windowId?: string): Promise<void> {\n const maximize = resolveWindowControlsBridgeMethod<\n (id?: string) => Promise<unknown>\n >(this.bridge, 'maximize');\n await maximize(windowId);\n }\n}\n\nexport function getWindowControls(): WindowControls {\n const globalObj = globalThis as GlobalWithWindowControls;\n if (!globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL]) {\n globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] = new WindowControls();\n }\n return globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL] as WindowControls;\n}\n\n/** Test-only helper to clear the client WindowControls singleton. */\nexport function clearClientWindowControlsForTests(): void {\n const globalObj = globalThis as GlobalWithWindowControls;\n delete globalObj[CLIENT_WINDOW_CONTROLS_SYMBOL];\n}\n\ntype RendererLogLevel = 'debug' | 'info' | 'warn' | 'error' | (string & {});\n\nfunction stringifyLogMessage(message: unknown): string {\n if (typeof message === 'string') return message;\n if (message instanceof Error) return message.message;\n return String(message);\n}\n\nfunction writeConsole(level: RendererLogLevel, logger: string, message: string, context?: unknown): void {\n const prefix = `[${logger}] ${message}`;\n switch (level) {\n case 'debug':\n console.debug(prefix, context);\n return;\n case 'warn':\n console.warn(prefix, context);\n return;\n case 'error':\n console.error(prefix, context);\n return;\n default:\n console.info(prefix, context);\n }\n}\n\nclass RendererLogger {\n constructor(private readonly loggerName: string) {}\n\n debug(message: unknown, context?: unknown): void {\n this.log('debug', message, context);\n }\n\n info(message: unknown, context?: unknown): void {\n this.log('info', message, context);\n }\n\n warn(message: unknown, context?: unknown): void {\n this.log('warn', message, context);\n }\n\n error(message: unknown, context?: unknown): void {\n this.log('error', message, context);\n }\n\n logAt(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n this.log(level, rawMessage, context);\n }\n\n private log(level: RendererLogLevel, rawMessage: unknown, context?: unknown): void {\n const message = stringifyLogMessage(rawMessage);\n\n const transport = getRendererLogTransport();\n ensureRendererLogWiring(transport);\n transport.admit(level, this.loggerName, message, context);\n }\n}\n\nlet rendererLogWiringDone = false;\n\n/**\n * Lazily binds the renderer log pipe's outlet to the preload bridge: batches\n * ride __logBatch (admitted records, one IPC message per flush); the per-entry\n * legacy __log channel and finally the console act as fallbacks when the\n * bridge predates batching or is absent (tests, plain browsers).\n */\nfunction ensureRendererLogWiring(transport: RendererLogTransport): void {\n if (rendererLogWiringDone) return;\n\n let bridge: WoodBridgeLike | null = null;\n try {\n bridge = resolveWoodBridge();\n } catch {\n bridge = null;\n }\n\n const preloadSend = bridge && typeof bridge.__logBatch === 'function'\n ? bridge.__logBatch.bind(bridge)\n : null;\n const appSend = appRendererLogSender;\n\n if (appSend) {\n // App sender wins; a throw here degrades to the preload batch path for\n // that batch, then to the per-record fallback if that throws too.\n transport.setSender((batch) => {\n try {\n appSend(batch);\n } catch (error) {\n if (!preloadSend) throw error;\n preloadSend(batch);\n }\n });\n } else if (preloadSend) {\n transport.setSender((batch) => preloadSend(batch));\n } else {\n transport.setSender(null);\n }\n\n const legacyLog = bridge && typeof bridge.__log === 'function' ? bridge.__log.bind(bridge) : null;\n transport.setFallback((record) => {\n if (legacyLog) {\n try {\n legacyLog(record.level, record.logger, record.message, record.context);\n return;\n } catch {\n // fall through to console\n }\n }\n writeConsole(record.level, record.logger, record.message, record.context);\n });\n\n // Only latch once a bridge was found (or an app sender makes the bridge\n // optional); keep retrying while both are absent so a late-injected bridge\n // (tests, slow preload) still gets wired.\n rendererLogWiringDone = bridge !== null || appSend !== null;\n}\n\ntype RendererLoggerShape = {\n logAt(level: RendererLogLevel, message: unknown, context?: unknown): void;\n debug(message: unknown, context?: unknown): void;\n info(message: unknown, context?: unknown): void;\n warn(message: unknown, context?: unknown): void;\n error(message: unknown, context?: unknown): void;\n};\n\nexport function createLogger(name: string): RendererLoggerShape {\n const loggerName = name.trim() || 'renderer';\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n if (!globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]) {\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL] = new Map<string, RendererLogger>();\n }\n\n const existing = globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.get(loggerName);\n if (existing) return existing;\n\n const logger = new RendererLogger(loggerName);\n globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL]?.set(loggerName, logger);\n return logger;\n}\n\n/** Test-only helper to clear client logger singletons. */\nexport function clearClientLoggersForTests(): void {\n const globalObj = globalThis as GlobalWithLoggerRegistry;\n delete globalObj[CLIENT_LOGGER_REGISTRY_SYMBOL];\n}\n\nfunction registerClientBuiltins(container: WoodContainerRegistration): void {\n if (typeof container.registerFunction !== 'function') {\n return;\n }\n\n const traceConfiguration = new TraceProviderConfiguration();\n traceConfiguration.configureRuntime('renderer');\n\n // Explicit Singleton lifetime: these factories return renderer-root\n // globals, and registerFunction defaults to Transient — which ioc >= 0.5's\n // captive-lifetime validation rejects under singleton dependents.\n const singleton = { loadAs: LoadAs.Singleton };\n container.registerFunction(Navigation, () => getNavigation(), singleton);\n container.registerFunction(App, () => getApp(), singleton);\n container.registerFunction(Ipc, () => getIpc(), singleton);\n container.registerFunction(WindowControls, () => getWindowControls(), singleton);\n container.registerFunction(TraceProviderConfiguration, () => traceConfiguration, singleton);\n}\n\nfunction createClientContainerInstance(): any {\n const container = new Container();\n registerClientBuiltins(container as WoodContainerRegistration);\n return container;\n}\n\n/**\n * Return a process-global client container instance.\n *\n * - First call creates the container and stores it on a symbol.\n * - Later calls return the same container instance.\n */\nexport function getContainer(): any {\n const globalObj = globalThis as GlobalWithClientContainer;\n if (!globalObj[CLIENT_CONTAINER_SYMBOL]) {\n globalObj[CLIENT_CONTAINER_SYMBOL] = createClientContainerInstance();\n }\n return globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client container singleton. */\nexport function clearClientContainerForTests(): void {\n const globalObj = globalThis as GlobalWithClientContainer;\n delete globalObj[CLIENT_CONTAINER_SYMBOL];\n}\n\n/** Test-only helper to clear the client navigation singleton. */\nexport function clearClientNavigationForTests(): void {\n clearNavigationSingletonForTests();\n}\n\n/** Clamp a menu position so it stays within viewport bounds. */\nexport function clampMenuPosition(\n x: number,\n y: number,\n menuWidth: number,\n menuHeight: number,\n): { x: number; y: number } {\n const vw = typeof window !== 'undefined' ? window.innerWidth : Infinity;\n const vh = typeof window !== 'undefined' ? window.innerHeight : Infinity;\n return {\n x: Math.min(x, Math.max(0, vw - menuWidth)),\n y: Math.min(y, Math.max(0, vh - menuHeight)),\n };\n}\n\nfunction resolveTarget(target: Element | null): Element {\n if (!target) {\n throw new Error('Missing renderer mount target');\n }\n return target;\n}\n\ntype RendererTraceBootstrapConfiguration = {\n rendererTraceEnabled?: boolean;\n rendererTraceMinLevel?: string;\n rendererTraceBatchEnabled?: boolean;\n appEnv?: string;\n};\n\nfunction configureRendererTraceTransport(config: RendererTraceBootstrapConfiguration | null): void {\n const globalObj = globalThis as Record<string, unknown>;\n if (config?.rendererTraceBatchEnabled) {\n const batchGlobal = globalObj.__NOEGO_WOOD_TRACE_BATCH__ as\n | { send?: (data: unknown) => unknown }\n | undefined;\n if (batchGlobal && typeof batchGlobal.send === 'function') {\n const sender = batchGlobal.send.bind(batchGlobal);\n setRendererTraceTransport((payload: unknown) => {\n sender(payload);\n });\n return;\n }\n }\n\n const traceGlobal = globalObj.__NOEGO_TRACE__ as\n | { send?: (data: unknown) => void }\n | undefined;\n if (traceGlobal && typeof traceGlobal.send === 'function') {\n const sender = traceGlobal.send.bind(traceGlobal);\n setRendererTraceTransport((payload: unknown) => sender(payload));\n }\n}\n\n/**\n * Start a Wood renderer app by mounting it to a target element.\n * Usage:\n * import { app } from './generated/app.cjs';\n * startWood(app, document.getElementById('app'));\n */\nexport async function startWood<T>(\n mountRoot: (target: Element) => T,\n target: Element | null,\n): Promise<T> {\n // Ensure container singleton exists and registers built-in client tokens.\n const container = getContainer();\n // Ensure navigation singleton exists at first client boot.\n getNavigation();\n\n // Fetch framework config from the main process and apply renderer trace\n // controls before the first render.\n let rendererTraceConfig: RendererTraceBootstrapConfiguration | null = null;\n try {\n const bridge = resolveWoodBridge();\n if (typeof bridge.__woodConfig === 'function') {\n const config = await bridge.__woodConfig() as RendererTraceBootstrapConfiguration | null;\n rendererTraceConfig = config;\n rendererBootConfig = config && typeof config === 'object'\n ? Object.freeze({ ...(config as Record<string, unknown>) })\n : null;\n setWoodAppEnv(config?.appEnv);\n const traceConfiguration = container.instance\n ? await container.instance(TraceProviderConfiguration)\n : null;\n if (typeof config?.rendererTraceEnabled === 'boolean') {\n traceConfiguration?.configureEnabled(config.rendererTraceEnabled);\n }\n const level = config?.rendererTraceMinLevel;\n if (typeof level === 'string' && level.trim().length > 0) {\n traceConfiguration?.configureMinLevel(level);\n }\n }\n } catch {\n // Config fetch is best-effort; fall back to defaults.\n }\n\n try {\n configureRendererTraceTransport(rendererTraceConfig);\n } catch {\n // Trace transport selection is best-effort; the internal IPC bridge remains available.\n }\n\n return mountRoot(resolveTarget(target));\n}\n\n/**\n * Backward-compatible helper that returns a target-aware boot function.\n * Usage:\n * const boot = createSvelteBoot(app);\n * const mounted = boot(document.getElementById('app'));\n */\nexport function createSvelteBoot<T>(\n mountRoot: (target: Element) => T,\n) {\n return (target: Element | null): T => mountRoot(resolveTarget(target));\n}\n\nexport { getNavigation };\nexport { Ipc, getIpc };\nexport { clearTraceHubForTests };\nexport { setRendererTraceTransport };\nexport { TraceProvider, TraceProviderConfiguration } from '../tracing/index.js';\nexport type {\n FrameworkTracer,\n TraceEvent,\n TraceInput,\n TraceLevel,\n TraceProcess,\n TraceProviderMode,\n TraceRuntime,\n TraceWaitOptions,\n} from '../tracing/index.js';\n"],"mappings":"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","names":["import_app_env","import_renderer_log_transport","import_tracing","clearRendererLogTransportSingletonForTests","clearNavigationSingletonForTests"]}
@@ -34,13 +34,33 @@ interface WoodRpcManifestEntry {
34
34
  path: string[];
35
35
  hasInput: boolean;
36
36
  }
37
+ interface WoodClientBridge extends Record<string, unknown> {
38
+ __rpcManifest?: WoodRpcManifestEntry[];
39
+ on?: (channel: string, callback: (...args: unknown[]) => void) => () => void;
40
+ __window?: {
41
+ current?: () => Promise<unknown>;
42
+ open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;
43
+ close?: (windowId?: string) => Promise<unknown>;
44
+ focus?: (windowId: string) => Promise<unknown>;
45
+ minimize?: (windowId?: string) => Promise<unknown>;
46
+ maximize?: (windowId?: string) => Promise<unknown>;
47
+ };
48
+ __log?: (level: string, logger: string, message: string, context?: unknown) => void;
49
+ __logBatch?: (batch: unknown) => void;
50
+ __woodConfig?: () => Promise<unknown>;
51
+ __trace?: (event: unknown) => void;
52
+ }
37
53
  declare class App {
38
54
  [controllerName: string]: unknown;
39
55
  }
56
+ /** Builds an uncached renderer App client owned by the supplied bridge. */
57
+ declare function createAppWithBridge<TApp extends object = App>(bridge: WoodClientBridge): TApp;
40
58
  declare function getApp<TApp extends object = App>(): TApp;
41
59
  /** Test-only helper to clear the client App singleton. */
42
60
  declare function clearClientAppForTests(): void;
43
61
  declare class WindowControls {
62
+ private readonly bridge?;
63
+ constructor(bridge?: WoodClientBridge | undefined);
44
64
  open(windowId: string, options?: Record<string, unknown>): Promise<void>;
45
65
  close(windowId?: string): Promise<void>;
46
66
  focus(windowId: string): Promise<void>;
@@ -92,4 +112,4 @@ declare function startWood<T>(mountRoot: (target: Element) => T, target: Element
92
112
  */
93
113
  declare function createSvelteBoot<T>(mountRoot: (target: Element) => T): (target: Element | null) => T;
94
114
 
95
- export { App, type RendererLogSender, WOOD_INTERNAL_BRIDGE_KEY, WindowControls, type WoodRpcManifestEntry, clampMenuPosition, clearClientAppForTests, clearClientContainerForTests, clearClientLoggersForTests, clearClientNavigationForTests, clearClientWindowControlsForTests, clearRendererLogTransportForTests, createLogger, createSvelteBoot, getApp, getContainer, getRendererBootConfig, getWindowControls, setRendererLogSender, startWood };
115
+ export { App, type RendererLogSender, WOOD_INTERNAL_BRIDGE_KEY, WindowControls, type WoodClientBridge, type WoodRpcManifestEntry, clampMenuPosition, clearClientAppForTests, clearClientContainerForTests, clearClientLoggersForTests, clearClientNavigationForTests, clearClientWindowControlsForTests, clearRendererLogTransportForTests, createAppWithBridge, createLogger, createSvelteBoot, getApp, getContainer, getRendererBootConfig, getWindowControls, setRendererLogSender, startWood };
@@ -34,13 +34,33 @@ interface WoodRpcManifestEntry {
34
34
  path: string[];
35
35
  hasInput: boolean;
36
36
  }
37
+ interface WoodClientBridge extends Record<string, unknown> {
38
+ __rpcManifest?: WoodRpcManifestEntry[];
39
+ on?: (channel: string, callback: (...args: unknown[]) => void) => () => void;
40
+ __window?: {
41
+ current?: () => Promise<unknown>;
42
+ open?: (windowId: string, options?: Record<string, unknown>) => Promise<unknown>;
43
+ close?: (windowId?: string) => Promise<unknown>;
44
+ focus?: (windowId: string) => Promise<unknown>;
45
+ minimize?: (windowId?: string) => Promise<unknown>;
46
+ maximize?: (windowId?: string) => Promise<unknown>;
47
+ };
48
+ __log?: (level: string, logger: string, message: string, context?: unknown) => void;
49
+ __logBatch?: (batch: unknown) => void;
50
+ __woodConfig?: () => Promise<unknown>;
51
+ __trace?: (event: unknown) => void;
52
+ }
37
53
  declare class App {
38
54
  [controllerName: string]: unknown;
39
55
  }
56
+ /** Builds an uncached renderer App client owned by the supplied bridge. */
57
+ declare function createAppWithBridge<TApp extends object = App>(bridge: WoodClientBridge): TApp;
40
58
  declare function getApp<TApp extends object = App>(): TApp;
41
59
  /** Test-only helper to clear the client App singleton. */
42
60
  declare function clearClientAppForTests(): void;
43
61
  declare class WindowControls {
62
+ private readonly bridge?;
63
+ constructor(bridge?: WoodClientBridge | undefined);
44
64
  open(windowId: string, options?: Record<string, unknown>): Promise<void>;
45
65
  close(windowId?: string): Promise<void>;
46
66
  focus(windowId: string): Promise<void>;
@@ -92,4 +112,4 @@ declare function startWood<T>(mountRoot: (target: Element) => T, target: Element
92
112
  */
93
113
  declare function createSvelteBoot<T>(mountRoot: (target: Element) => T): (target: Element | null) => T;
94
114
 
95
- export { App, type RendererLogSender, WOOD_INTERNAL_BRIDGE_KEY, WindowControls, type WoodRpcManifestEntry, clampMenuPosition, clearClientAppForTests, clearClientContainerForTests, clearClientLoggersForTests, clearClientNavigationForTests, clearClientWindowControlsForTests, clearRendererLogTransportForTests, createLogger, createSvelteBoot, getApp, getContainer, getRendererBootConfig, getWindowControls, setRendererLogSender, startWood };
115
+ export { App, type RendererLogSender, WOOD_INTERNAL_BRIDGE_KEY, WindowControls, type WoodClientBridge, type WoodRpcManifestEntry, clampMenuPosition, clearClientAppForTests, clearClientContainerForTests, clearClientLoggersForTests, clearClientNavigationForTests, clearClientWindowControlsForTests, clearRendererLogTransportForTests, createAppWithBridge, createLogger, createSvelteBoot, getApp, getContainer, getRendererBootConfig, getWindowControls, setRendererLogSender, startWood };
@@ -121,8 +121,7 @@ function validateManifestEntry(entry, index) {
121
121
  );
122
122
  }
123
123
  }
124
- function createAppInstance() {
125
- const bridge = resolveWoodBridge();
124
+ function createAppInstance(bridge) {
126
125
  const manifest = resolveRpcManifest(bridge);
127
126
  const app = {};
128
127
  manifest.forEach((entry, index) => {
@@ -225,10 +224,13 @@ function createAppInstance() {
225
224
  });
226
225
  return app;
227
226
  }
227
+ function createAppWithBridge(bridge) {
228
+ return createAppInstance(bridge);
229
+ }
228
230
  function getApp() {
229
231
  const globalObj = globalThis;
230
232
  if (!globalObj[CLIENT_APP_SYMBOL]) {
231
- globalObj[CLIENT_APP_SYMBOL] = createAppInstance();
233
+ globalObj[CLIENT_APP_SYMBOL] = createAppInstance(resolveWoodBridge());
232
234
  }
233
235
  return globalObj[CLIENT_APP_SYMBOL];
234
236
  }
@@ -236,33 +238,36 @@ function clearClientAppForTests() {
236
238
  const globalObj = globalThis;
237
239
  delete globalObj[CLIENT_APP_SYMBOL];
238
240
  }
239
- function resolveWindowControlsBridgeMethod(method) {
240
- const bridge = resolveWoodBridge();
241
- const fn = bridge.__window?.[method];
241
+ function resolveWindowControlsBridgeMethod(bridge, method) {
242
+ const resolvedBridge = bridge ?? resolveWoodBridge();
243
+ const fn = resolvedBridge.__window?.[method];
242
244
  if (typeof fn !== "function") {
243
245
  throw new Error(`Wood bridge does not expose "__window.${String(method)}".`);
244
246
  }
245
247
  return fn;
246
248
  }
247
249
  class WindowControls {
250
+ constructor(bridge) {
251
+ this.bridge = bridge;
252
+ }
248
253
  async open(windowId, options) {
249
- const open = resolveWindowControlsBridgeMethod("open");
254
+ const open = resolveWindowControlsBridgeMethod(this.bridge, "open");
250
255
  await open(windowId, options);
251
256
  }
252
257
  async close(windowId) {
253
- const close = resolveWindowControlsBridgeMethod("close");
258
+ const close = resolveWindowControlsBridgeMethod(this.bridge, "close");
254
259
  await close(windowId);
255
260
  }
256
261
  async focus(windowId) {
257
- const focus = resolveWindowControlsBridgeMethod("focus");
262
+ const focus = resolveWindowControlsBridgeMethod(this.bridge, "focus");
258
263
  await focus(windowId);
259
264
  }
260
265
  async minimize(windowId) {
261
- const minimize = resolveWindowControlsBridgeMethod("minimize");
266
+ const minimize = resolveWindowControlsBridgeMethod(this.bridge, "minimize");
262
267
  await minimize(windowId);
263
268
  }
264
269
  async maximize(windowId) {
265
- const maximize = resolveWindowControlsBridgeMethod("maximize");
270
+ const maximize = resolveWindowControlsBridgeMethod(this.bridge, "maximize");
266
271
  await maximize(windowId);
267
272
  }
268
273
  }
@@ -490,6 +495,7 @@ export {
490
495
  clearRendererLogTransportForTests,
491
496
  clearTraceHubForTests,
492
497
  configureRendererLogTransport,
498
+ createAppWithBridge,
493
499
  createLogger,
494
500
  createSvelteBoot,
495
501
  flushRendererLogs,