@noego/wood 0.4.6 → 0.4.8

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 (243) hide show
  1. package/bin/wood.js +6 -1
  2. package/dist/client/app_env.cjs +39 -0
  3. package/dist/client/app_env.cjs.map +1 -0
  4. package/dist/client/app_env.d.cts +7 -0
  5. package/dist/client/app_env.d.ts +7 -0
  6. package/dist/client/app_env.js +14 -0
  7. package/dist/client/app_env.js.map +1 -0
  8. package/dist/client/index.cjs +33 -9
  9. package/dist/client/index.cjs.map +1 -1
  10. package/dist/client/index.d.cts +5 -1
  11. package/dist/client/index.d.ts +5 -1
  12. package/dist/client/index.js +32 -9
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/codegen/channel_map_generator.cjs +102 -0
  15. package/dist/codegen/channel_map_generator.cjs.map +1 -0
  16. package/dist/codegen/channel_map_generator.d.cts +6 -0
  17. package/dist/codegen/channel_map_generator.d.ts +6 -0
  18. package/dist/codegen/channel_map_generator.js +78 -0
  19. package/dist/codegen/channel_map_generator.js.map +1 -0
  20. package/dist/codegen/controller_registration_generator.cjs +6 -1
  21. package/dist/codegen/controller_registration_generator.cjs.map +1 -1
  22. package/dist/codegen/controller_registration_generator.js +6 -1
  23. package/dist/codegen/controller_registration_generator.js.map +1 -1
  24. package/dist/codegen/generated_file_notice.cjs +40 -0
  25. package/dist/codegen/generated_file_notice.cjs.map +1 -0
  26. package/dist/codegen/generated_file_notice.d.cts +3 -0
  27. package/dist/codegen/generated_file_notice.d.ts +3 -0
  28. package/dist/codegen/generated_file_notice.js +16 -0
  29. package/dist/codegen/generated_file_notice.js.map +1 -0
  30. package/dist/codegen/index.cjs +3 -0
  31. package/dist/codegen/index.cjs.map +1 -1
  32. package/dist/codegen/index.d.cts +1 -0
  33. package/dist/codegen/index.d.ts +1 -0
  34. package/dist/codegen/index.js +2 -0
  35. package/dist/codegen/index.js.map +1 -1
  36. package/dist/codegen/preload_generator.cjs +97 -1
  37. package/dist/codegen/preload_generator.cjs.map +1 -1
  38. package/dist/codegen/preload_generator.js +97 -1
  39. package/dist/codegen/preload_generator.js.map +1 -1
  40. package/dist/codegen/renderer_app_generator.cjs +6 -1
  41. package/dist/codegen/renderer_app_generator.cjs.map +1 -1
  42. package/dist/codegen/renderer_app_generator.js +6 -1
  43. package/dist/codegen/renderer_app_generator.js.map +1 -1
  44. package/dist/codegen/renderer_controller_registration_generator.cjs +6 -1
  45. package/dist/codegen/renderer_controller_registration_generator.cjs.map +1 -1
  46. package/dist/codegen/renderer_controller_registration_generator.js +6 -1
  47. package/dist/codegen/renderer_controller_registration_generator.js.map +1 -1
  48. package/dist/codegen/renderer_view_manifest_generator.cjs +6 -1
  49. package/dist/codegen/renderer_view_manifest_generator.cjs.map +1 -1
  50. package/dist/codegen/renderer_view_manifest_generator.js +6 -1
  51. package/dist/codegen/renderer_view_manifest_generator.js.map +1 -1
  52. package/dist/codegen/runtime_generator.cjs +6 -1
  53. package/dist/codegen/runtime_generator.cjs.map +1 -1
  54. package/dist/codegen/runtime_generator.js +6 -1
  55. package/dist/codegen/runtime_generator.js.map +1 -1
  56. package/dist/codegen/test_mock_generator.cjs +5 -1
  57. package/dist/codegen/test_mock_generator.cjs.map +1 -1
  58. package/dist/codegen/test_mock_generator.js +5 -1
  59. package/dist/codegen/test_mock_generator.js.map +1 -1
  60. package/dist/codegen/type_generator.cjs +6 -1
  61. package/dist/codegen/type_generator.cjs.map +1 -1
  62. package/dist/codegen/type_generator.js +6 -1
  63. package/dist/codegen/type_generator.js.map +1 -1
  64. package/dist/index.cjs +19 -6
  65. package/dist/index.cjs.map +1 -1
  66. package/dist/index.d.cts +7 -1
  67. package/dist/index.d.ts +7 -1
  68. package/dist/index.js +13 -1
  69. package/dist/index.js.map +1 -1
  70. package/dist/main/index.cjs +133 -55
  71. package/dist/main/index.cjs.map +1 -1
  72. package/dist/main/index.d.cts +30 -2
  73. package/dist/main/index.d.ts +30 -2
  74. package/dist/main/index.js +134 -55
  75. package/dist/main/index.js.map +1 -1
  76. package/dist/main/renderer_trace_drain_coordinator.cjs +127 -0
  77. package/dist/main/renderer_trace_drain_coordinator.cjs.map +1 -0
  78. package/dist/main/renderer_trace_drain_coordinator.d.cts +46 -0
  79. package/dist/main/renderer_trace_drain_coordinator.d.ts +46 -0
  80. package/dist/main/renderer_trace_drain_coordinator.js +101 -0
  81. package/dist/main/renderer_trace_drain_coordinator.js.map +1 -0
  82. package/dist/router/wood_router.cjs +9 -1
  83. package/dist/router/wood_router.cjs.map +1 -1
  84. package/dist/router/wood_router.d.cts +1 -1
  85. package/dist/router/wood_router.d.ts +1 -1
  86. package/dist/router/wood_router.js +9 -1
  87. package/dist/router/wood_router.js.map +1 -1
  88. package/dist/runtime/app_env.cjs +52 -0
  89. package/dist/runtime/app_env.cjs.map +1 -0
  90. package/dist/runtime/app_env.d.cts +10 -0
  91. package/dist/runtime/app_env.d.ts +10 -0
  92. package/dist/runtime/app_env.js +18 -0
  93. package/dist/runtime/app_env.js.map +1 -0
  94. package/dist/runtime/index.cjs +9 -2
  95. package/dist/runtime/index.cjs.map +1 -1
  96. package/dist/runtime/index.d.cts +5 -1
  97. package/dist/runtime/index.d.ts +5 -1
  98. package/dist/runtime/index.js +9 -1
  99. package/dist/runtime/index.js.map +1 -1
  100. package/dist/runtime/renderer_log_dispatcher.cjs.map +1 -1
  101. package/dist/runtime/renderer_log_dispatcher.d.cts +1 -1
  102. package/dist/runtime/renderer_log_dispatcher.d.ts +1 -1
  103. package/dist/runtime/renderer_log_dispatcher.js.map +1 -1
  104. package/dist/runtime/renderer_trace_batch_receiver_registry.cjs +50 -0
  105. package/dist/runtime/renderer_trace_batch_receiver_registry.cjs.map +1 -0
  106. package/dist/runtime/renderer_trace_batch_receiver_registry.d.cts +10 -0
  107. package/dist/runtime/renderer_trace_batch_receiver_registry.d.ts +10 -0
  108. package/dist/runtime/renderer_trace_batch_receiver_registry.js +24 -0
  109. package/dist/runtime/renderer_trace_batch_receiver_registry.js.map +1 -0
  110. package/dist/runtime/runtime.cjs +75 -57
  111. package/dist/runtime/runtime.cjs.map +1 -1
  112. package/dist/runtime/runtime.d.cts +8 -1
  113. package/dist/runtime/runtime.d.ts +8 -1
  114. package/dist/runtime/runtime.js +80 -58
  115. package/dist/runtime/runtime.js.map +1 -1
  116. package/dist/testing/browser.cjs +540 -11
  117. package/dist/testing/browser.cjs.map +1 -1
  118. package/dist/testing/browser.d.cts +219 -3
  119. package/dist/testing/browser.d.ts +219 -3
  120. package/dist/testing/browser.js +540 -11
  121. package/dist/testing/browser.js.map +1 -1
  122. package/dist/testing/create_controller_harness.d.cts +1 -1
  123. package/dist/testing/create_controller_harness.d.ts +1 -1
  124. package/dist/testing/index.cjs +13 -0
  125. package/dist/testing/index.cjs.map +1 -1
  126. package/dist/testing/index.d.cts +7 -2
  127. package/dist/testing/index.d.ts +7 -2
  128. package/dist/testing/index.js +14 -0
  129. package/dist/testing/index.js.map +1 -1
  130. package/dist/trace-testing/index.d.cts +1 -1
  131. package/dist/trace-testing/index.d.ts +1 -1
  132. package/dist/trace-testing/trace_recorder.d.cts +1 -1
  133. package/dist/trace-testing/trace_recorder.d.ts +1 -1
  134. package/dist/trace-transport/index.cjs +32 -0
  135. package/dist/trace-transport/index.cjs.map +1 -0
  136. package/dist/trace-transport/index.d.cts +6 -0
  137. package/dist/trace-transport/index.d.ts +6 -0
  138. package/dist/trace-transport/index.js +7 -0
  139. package/dist/trace-transport/index.js.map +1 -0
  140. package/dist/trace_hub-Cob54qRx.d.cts +56 -0
  141. package/dist/trace_hub-Cob54qRx.d.ts +56 -0
  142. package/dist/tracing/index.cjs +14 -0
  143. package/dist/tracing/index.cjs.map +1 -1
  144. package/dist/tracing/index.d.cts +7 -1
  145. package/dist/tracing/index.d.ts +7 -1
  146. package/dist/tracing/index.js +12 -0
  147. package/dist/tracing/index.js.map +1 -1
  148. package/dist/tracing/renderer_trace_batch_host.cjs +80 -0
  149. package/dist/tracing/renderer_trace_batch_host.cjs.map +1 -0
  150. package/dist/tracing/renderer_trace_batch_host.d.cts +16 -0
  151. package/dist/tracing/renderer_trace_batch_host.d.ts +16 -0
  152. package/dist/tracing/renderer_trace_batch_host.js +56 -0
  153. package/dist/tracing/renderer_trace_batch_host.js.map +1 -0
  154. package/dist/tracing/renderer_trace_batch_transport.cjs +313 -0
  155. package/dist/tracing/renderer_trace_batch_transport.cjs.map +1 -0
  156. package/dist/tracing/renderer_trace_batch_transport.d.cts +53 -0
  157. package/dist/tracing/renderer_trace_batch_transport.d.ts +53 -0
  158. package/dist/tracing/renderer_trace_batch_transport.js +292 -0
  159. package/dist/tracing/renderer_trace_batch_transport.js.map +1 -0
  160. package/dist/tracing/trace_commit_writer.cjs +159 -0
  161. package/dist/tracing/trace_commit_writer.cjs.map +1 -0
  162. package/dist/tracing/trace_commit_writer.d.cts +97 -0
  163. package/dist/tracing/trace_commit_writer.d.ts +97 -0
  164. package/dist/tracing/trace_commit_writer.js +135 -0
  165. package/dist/tracing/trace_commit_writer.js.map +1 -0
  166. package/dist/tracing/trace_hub.cjs +58 -3
  167. package/dist/tracing/trace_hub.cjs.map +1 -1
  168. package/dist/tracing/trace_hub.d.cts +2 -46
  169. package/dist/tracing/trace_hub.d.ts +2 -46
  170. package/dist/tracing/trace_hub.js +56 -3
  171. package/dist/tracing/trace_hub.js.map +1 -1
  172. package/dist/tracing/trace_provider.cjs +42 -12
  173. package/dist/tracing/trace_provider.cjs.map +1 -1
  174. package/dist/tracing/trace_provider.d.cts +15 -1
  175. package/dist/tracing/trace_provider.d.ts +15 -1
  176. package/dist/tracing/trace_provider.js +43 -12
  177. package/dist/tracing/trace_provider.js.map +1 -1
  178. package/dist/tracing/types/wood_trace_batch.types.cjs +17 -0
  179. package/dist/tracing/types/wood_trace_batch.types.cjs.map +1 -0
  180. package/dist/tracing/types/wood_trace_batch.types.d.cts +124 -0
  181. package/dist/tracing/types/wood_trace_batch.types.d.ts +124 -0
  182. package/dist/tracing/types/wood_trace_batch.types.js +1 -0
  183. package/dist/tracing/types/wood_trace_batch.types.js.map +1 -0
  184. package/dist/tracing/types/wood_trace_sink.types.cjs +17 -0
  185. package/dist/tracing/types/wood_trace_sink.types.cjs.map +1 -0
  186. package/dist/tracing/types/wood_trace_sink.types.d.cts +2 -0
  187. package/dist/tracing/types/wood_trace_sink.types.d.ts +2 -0
  188. package/dist/tracing/types/wood_trace_sink.types.js +1 -0
  189. package/dist/tracing/types/wood_trace_sink.types.js.map +1 -0
  190. package/dist/tracing/wood_trace_batch_contract.cjs +229 -0
  191. package/dist/tracing/wood_trace_batch_contract.cjs.map +1 -0
  192. package/dist/tracing/wood_trace_batch_contract.d.cts +23 -0
  193. package/dist/tracing/wood_trace_batch_contract.d.ts +23 -0
  194. package/dist/tracing/wood_trace_batch_contract.js +205 -0
  195. package/dist/tracing/wood_trace_batch_contract.js.map +1 -0
  196. package/dist/transport/build_transport_bridge.cjs +62 -0
  197. package/dist/transport/build_transport_bridge.cjs.map +1 -0
  198. package/dist/transport/build_transport_bridge.d.cts +29 -0
  199. package/dist/transport/build_transport_bridge.d.ts +29 -0
  200. package/dist/transport/build_transport_bridge.js +38 -0
  201. package/dist/transport/build_transport_bridge.js.map +1 -0
  202. package/dist/transport/in_process_router_transport.cjs +41 -0
  203. package/dist/transport/in_process_router_transport.cjs.map +1 -0
  204. package/dist/transport/in_process_router_transport.d.cts +31 -0
  205. package/dist/transport/in_process_router_transport.d.ts +31 -0
  206. package/dist/transport/in_process_router_transport.js +17 -0
  207. package/dist/transport/in_process_router_transport.js.map +1 -0
  208. package/dist/transport/index.cjs +42 -0
  209. package/dist/transport/index.cjs.map +1 -0
  210. package/dist/transport/index.d.cts +8 -0
  211. package/dist/transport/index.d.ts +8 -0
  212. package/dist/transport/index.js +19 -0
  213. package/dist/transport/index.js.map +1 -0
  214. package/dist/transport/loopback_remote_transport.cjs +270 -0
  215. package/dist/transport/loopback_remote_transport.cjs.map +1 -0
  216. package/dist/transport/loopback_remote_transport.d.cts +49 -0
  217. package/dist/transport/loopback_remote_transport.d.ts +49 -0
  218. package/dist/transport/loopback_remote_transport.js +237 -0
  219. package/dist/transport/loopback_remote_transport.js.map +1 -0
  220. package/dist/transport/loopback_serialization.cjs +116 -0
  221. package/dist/transport/loopback_serialization.cjs.map +1 -0
  222. package/dist/transport/loopback_serialization.d.cts +28 -0
  223. package/dist/transport/loopback_serialization.d.ts +28 -0
  224. package/dist/transport/loopback_serialization.js +91 -0
  225. package/dist/transport/loopback_serialization.js.map +1 -0
  226. package/dist/transport/transport_contract_suite.cjs +110 -0
  227. package/dist/transport/transport_contract_suite.cjs.map +1 -0
  228. package/dist/transport/transport_contract_suite.d.cts +23 -0
  229. package/dist/transport/transport_contract_suite.d.ts +23 -0
  230. package/dist/transport/transport_contract_suite.js +85 -0
  231. package/dist/transport/transport_contract_suite.js.map +1 -0
  232. package/dist/transport/wood_rpc_transport.cjs +17 -0
  233. package/dist/transport/wood_rpc_transport.cjs.map +1 -0
  234. package/dist/transport/wood_rpc_transport.d.cts +44 -0
  235. package/dist/transport/wood_rpc_transport.d.ts +44 -0
  236. package/dist/transport/wood_rpc_transport.js +1 -0
  237. package/dist/transport/wood_rpc_transport.js.map +1 -0
  238. package/docs/browser-testing.md +139 -1
  239. package/package.json +25 -4
  240. package/src/client/app_env.ts +21 -0
  241. package/src/components/NavigationShell.browser.test.ts +236 -0
  242. package/src/components/NavigationShell.svelte +162 -49
  243. package/src/runtime/app_env.ts +27 -0
@@ -0,0 +1,97 @@
1
+ import { d as TraceProcess, c as TraceLevel, g as WoodTraceEventSnapshot } from '../trace_hub-Cob54qRx.js';
2
+ import 'rxjs';
3
+
4
+ /**
5
+ * A committed trace row. Ordering is authoritative on `commitSeq` — the
6
+ * writer-assigned monotonic sequence stamped at the single ingestion/commit
7
+ * boundary. `producerId` + `producerSeq` identify the producer-side stream;
8
+ * `timestamp` is retained for display but is NEVER used for cross-producer
9
+ * ordering (clocks across main/renderer are not causal).
10
+ */
11
+ interface TraceCommitRow {
12
+ readonly commitSeq: number;
13
+ readonly producerId: string;
14
+ readonly producerSeq: number;
15
+ readonly process: TraceProcess;
16
+ readonly level: TraceLevel;
17
+ readonly type: string;
18
+ readonly source: string;
19
+ readonly message?: string;
20
+ readonly conversationId?: number | string;
21
+ readonly timestamp: string;
22
+ readonly payload?: Record<string, unknown>;
23
+ }
24
+ /**
25
+ * Storage boundary for committed trace rows. Implemented per-runtime: the app's
26
+ * test backend harness supplies a SQLite-backed adapter (better-sqlite3, which
27
+ * wood does not depend on). `persist` may be sync or async; the writer barrier
28
+ * awaits whatever it returns.
29
+ */
30
+ interface WoodTracePersistencePort {
31
+ persist(row: TraceCommitRow): void | Promise<void>;
32
+ close?(): void | Promise<void>;
33
+ }
34
+ interface TraceFlushOptions {
35
+ timeoutMs?: number;
36
+ }
37
+ interface TraceFlushResult {
38
+ /** Highest `commitSeq` durably persisted at flush completion. */
39
+ readonly committedHighWaterMark: number;
40
+ }
41
+ interface TraceCommitWriterOptions {
42
+ /** Stable id for the main-process producer stream (default `'main'`). */
43
+ readonly mainProducerId?: string;
44
+ }
45
+ /**
46
+ * The single trace ingestion/commit boundary. Every trace event (main or, in
47
+ * later phases, renderer batches) is admitted here, assigned a monotonic
48
+ * `commitSeq`, and enqueued for persistence. `flush` drains the async write
49
+ * queue and returns the committed high-water mark — no sleeps, no polling.
50
+ */
51
+ declare class TraceCommitWriter {
52
+ private readonly port;
53
+ private readonly mainProducerId;
54
+ private readonly seen;
55
+ private inflight;
56
+ private nextCommitSeq;
57
+ private committed;
58
+ private pendingError;
59
+ private rendererFlush;
60
+ constructor(port: WoodTracePersistencePort, options?: TraceCommitWriterOptions);
61
+ /**
62
+ * Admit one trace event. Returns the committed row (with its assigned
63
+ * `commitSeq`), or `null` if it is a duplicate of an already-admitted
64
+ * (producerId, producerSeq) pair — this is the batch-retry idempotency guard.
65
+ */
66
+ admit(event: WoodTraceEventSnapshot): TraceCommitRow | null;
67
+ /** WoodTraceEventSink shape — register via `tracer.registerSink(writer.sink)`. */
68
+ readonly sink: (event: WoodTraceEventSnapshot) => void;
69
+ highWaterMark(): number;
70
+ /**
71
+ * Install the renderer-flush hook (Phase 4 browser mode). The browser harness
72
+ * supplies a function that drains the page's in-flight renderer `__trace`
73
+ * round-trips; `flush()` awaits it before the main-writer barrier so
74
+ * renderer-origin traces are persisted deterministically. Backend mode leaves
75
+ * this unset and `requestRendererFlush` resolves immediately.
76
+ */
77
+ setRendererFlush(fn: (() => Promise<void>) | null): void;
78
+ /**
79
+ * Request that renderer producers flush their pending batches and await acks.
80
+ * In browser mode the installed hook (see {@link setRendererFlush}) round-trips
81
+ * the page to drain pending `__trace` deliveries. With no hook (backend mode,
82
+ * no renderer) this resolves immediately.
83
+ */
84
+ requestRendererFlush(): Promise<void>;
85
+ /**
86
+ * Drain the write queue and return the committed high-water mark. Awaits a
87
+ * barrier over all in-flight persists; with `timeoutMs` it rejects on a
88
+ * timer race rather than polling. Rejects if any persist failed.
89
+ */
90
+ flush(options?: TraceFlushOptions): Promise<TraceFlushResult>;
91
+ private awaitBarrier;
92
+ private enqueue;
93
+ private resolveProducerId;
94
+ private toRow;
95
+ }
96
+
97
+ export { type TraceCommitRow, TraceCommitWriter, type TraceCommitWriterOptions, type TraceFlushOptions, type TraceFlushResult, type WoodTracePersistencePort };
@@ -0,0 +1,135 @@
1
+ class TraceCommitWriter {
2
+ constructor(port, options = {}) {
3
+ this.seen = /* @__PURE__ */ new Set();
4
+ this.inflight = [];
5
+ this.nextCommitSeq = 1;
6
+ this.committed = 0;
7
+ this.pendingError = null;
8
+ this.rendererFlush = null;
9
+ /** WoodTraceEventSink shape — register via `tracer.registerSink(writer.sink)`. */
10
+ this.sink = (event) => {
11
+ this.admit(event);
12
+ };
13
+ this.port = port;
14
+ this.mainProducerId = options.mainProducerId ?? "main";
15
+ }
16
+ /**
17
+ * Admit one trace event. Returns the committed row (with its assigned
18
+ * `commitSeq`), or `null` if it is a duplicate of an already-admitted
19
+ * (producerId, producerSeq) pair — this is the batch-retry idempotency guard.
20
+ */
21
+ admit(event) {
22
+ const producerId = this.resolveProducerId(event);
23
+ const producerSeq = event.traceSeq;
24
+ const identity = `${producerId}#${producerSeq}`;
25
+ if (this.seen.has(identity)) {
26
+ return null;
27
+ }
28
+ this.seen.add(identity);
29
+ const row = this.toRow(event, producerId, producerSeq);
30
+ this.enqueue(row);
31
+ return row;
32
+ }
33
+ highWaterMark() {
34
+ return this.committed;
35
+ }
36
+ /**
37
+ * Install the renderer-flush hook (Phase 4 browser mode). The browser harness
38
+ * supplies a function that drains the page's in-flight renderer `__trace`
39
+ * round-trips; `flush()` awaits it before the main-writer barrier so
40
+ * renderer-origin traces are persisted deterministically. Backend mode leaves
41
+ * this unset and `requestRendererFlush` resolves immediately.
42
+ */
43
+ setRendererFlush(fn) {
44
+ this.rendererFlush = fn;
45
+ }
46
+ /**
47
+ * Request that renderer producers flush their pending batches and await acks.
48
+ * In browser mode the installed hook (see {@link setRendererFlush}) round-trips
49
+ * the page to drain pending `__trace` deliveries. With no hook (backend mode,
50
+ * no renderer) this resolves immediately.
51
+ */
52
+ async requestRendererFlush() {
53
+ if (this.rendererFlush) {
54
+ await this.rendererFlush();
55
+ }
56
+ }
57
+ /**
58
+ * Drain the write queue and return the committed high-water mark. Awaits a
59
+ * barrier over all in-flight persists; with `timeoutMs` it rejects on a
60
+ * timer race rather than polling. Rejects if any persist failed.
61
+ */
62
+ async flush(options = {}) {
63
+ await this.requestRendererFlush();
64
+ const barrier = Promise.allSettled(this.inflight.slice());
65
+ await this.awaitBarrier(barrier, options.timeoutMs);
66
+ if (this.pendingError !== null) {
67
+ const error = this.pendingError;
68
+ this.pendingError = null;
69
+ throw error;
70
+ }
71
+ return { committedHighWaterMark: this.committed };
72
+ }
73
+ async awaitBarrier(barrier, timeoutMs) {
74
+ if (timeoutMs === void 0) {
75
+ await barrier;
76
+ return;
77
+ }
78
+ let timer = null;
79
+ const timeout = new Promise((_, reject) => {
80
+ timer = setTimeout(
81
+ () => reject(new Error(`trace flush barrier exceeded ${timeoutMs}ms`)),
82
+ timeoutMs
83
+ );
84
+ });
85
+ try {
86
+ await Promise.race([barrier.then(() => void 0), timeout]);
87
+ } finally {
88
+ if (timer !== null) {
89
+ clearTimeout(timer);
90
+ }
91
+ }
92
+ }
93
+ enqueue(row) {
94
+ const op = Promise.resolve().then(() => this.port.persist(row)).then(
95
+ () => {
96
+ if (row.commitSeq > this.committed) {
97
+ this.committed = row.commitSeq;
98
+ }
99
+ },
100
+ (error) => {
101
+ this.pendingError = error;
102
+ }
103
+ );
104
+ this.inflight.push(op);
105
+ }
106
+ resolveProducerId(event) {
107
+ if (event.process === "renderer") {
108
+ const rendererId = event.payload?.woodRendererProducerId;
109
+ return typeof rendererId === "string" ? rendererId : "renderer";
110
+ }
111
+ if (event.process === "main") {
112
+ return this.mainProducerId;
113
+ }
114
+ return event.process;
115
+ }
116
+ toRow(event, producerId, producerSeq) {
117
+ return {
118
+ commitSeq: this.nextCommitSeq++,
119
+ producerId,
120
+ producerSeq,
121
+ process: event.process,
122
+ level: event.level,
123
+ type: event.type,
124
+ source: event.source,
125
+ ...event.message !== void 0 ? { message: event.message } : {},
126
+ ...event.conversationId !== void 0 ? { conversationId: event.conversationId } : {},
127
+ timestamp: event.timestamp,
128
+ ...event.payload ? { payload: { ...event.payload } } : {}
129
+ };
130
+ }
131
+ }
132
+ export {
133
+ TraceCommitWriter
134
+ };
135
+ //# sourceMappingURL=trace_commit_writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tracing/trace_commit_writer.ts"],"sourcesContent":["import type { TraceLevel, TraceProcess } from './trace_hub.js';\nimport type { WoodTraceEventSnapshot } from './types/wood_trace_sink.types.js';\n\n/**\n * A committed trace row. Ordering is authoritative on `commitSeq` — the\n * writer-assigned monotonic sequence stamped at the single ingestion/commit\n * boundary. `producerId` + `producerSeq` identify the producer-side stream;\n * `timestamp` is retained for display but is NEVER used for cross-producer\n * ordering (clocks across main/renderer are not causal).\n */\nexport interface TraceCommitRow {\n readonly commitSeq: number;\n readonly producerId: string;\n readonly producerSeq: number;\n readonly process: TraceProcess;\n readonly level: TraceLevel;\n readonly type: string;\n readonly source: string;\n readonly message?: string;\n readonly conversationId?: number | string;\n readonly timestamp: string;\n readonly payload?: Record<string, unknown>;\n}\n\n/**\n * Storage boundary for committed trace rows. Implemented per-runtime: the app's\n * test backend harness supplies a SQLite-backed adapter (better-sqlite3, which\n * wood does not depend on). `persist` may be sync or async; the writer barrier\n * awaits whatever it returns.\n */\nexport interface WoodTracePersistencePort {\n persist(row: TraceCommitRow): void | Promise<void>;\n close?(): void | Promise<void>;\n}\n\nexport interface TraceFlushOptions {\n timeoutMs?: number;\n}\n\nexport interface TraceFlushResult {\n /** Highest `commitSeq` durably persisted at flush completion. */\n readonly committedHighWaterMark: number;\n}\n\nexport interface TraceCommitWriterOptions {\n /** Stable id for the main-process producer stream (default `'main'`). */\n readonly mainProducerId?: string;\n}\n\n/**\n * The single trace ingestion/commit boundary. Every trace event (main or, in\n * later phases, renderer batches) is admitted here, assigned a monotonic\n * `commitSeq`, and enqueued for persistence. `flush` drains the async write\n * queue and returns the committed high-water mark — no sleeps, no polling.\n */\nexport class TraceCommitWriter {\n private readonly port: WoodTracePersistencePort;\n private readonly mainProducerId: string;\n private readonly seen = new Set<string>();\n private inflight: Promise<void>[] = [];\n private nextCommitSeq = 1;\n private committed = 0;\n private pendingError: unknown = null;\n private rendererFlush: (() => Promise<void>) | null = null;\n\n constructor(port: WoodTracePersistencePort, options: TraceCommitWriterOptions = {}) {\n this.port = port;\n this.mainProducerId = options.mainProducerId ?? 'main';\n }\n\n /**\n * Admit one trace event. Returns the committed row (with its assigned\n * `commitSeq`), or `null` if it is a duplicate of an already-admitted\n * (producerId, producerSeq) pair — this is the batch-retry idempotency guard.\n */\n admit(event: WoodTraceEventSnapshot): TraceCommitRow | null {\n const producerId = this.resolveProducerId(event);\n const producerSeq = event.traceSeq;\n const identity = `${producerId}#${producerSeq}`;\n if (this.seen.has(identity)) {\n return null;\n }\n this.seen.add(identity);\n const row = this.toRow(event, producerId, producerSeq);\n this.enqueue(row);\n return row;\n }\n\n /** WoodTraceEventSink shape — register via `tracer.registerSink(writer.sink)`. */\n readonly sink = (event: WoodTraceEventSnapshot): void => {\n this.admit(event);\n };\n\n highWaterMark(): number {\n return this.committed;\n }\n\n /**\n * Install the renderer-flush hook (Phase 4 browser mode). The browser harness\n * supplies a function that drains the page's in-flight renderer `__trace`\n * round-trips; `flush()` awaits it before the main-writer barrier so\n * renderer-origin traces are persisted deterministically. Backend mode leaves\n * this unset and `requestRendererFlush` resolves immediately.\n */\n setRendererFlush(fn: (() => Promise<void>) | null): void {\n this.rendererFlush = fn;\n }\n\n /**\n * Request that renderer producers flush their pending batches and await acks.\n * In browser mode the installed hook (see {@link setRendererFlush}) round-trips\n * the page to drain pending `__trace` deliveries. With no hook (backend mode,\n * no renderer) this resolves immediately.\n */\n async requestRendererFlush(): Promise<void> {\n if (this.rendererFlush) {\n await this.rendererFlush();\n }\n }\n\n /**\n * Drain the write queue and return the committed high-water mark. Awaits a\n * barrier over all in-flight persists; with `timeoutMs` it rejects on a\n * timer race rather than polling. Rejects if any persist failed.\n */\n async flush(options: TraceFlushOptions = {}): Promise<TraceFlushResult> {\n await this.requestRendererFlush();\n const barrier = Promise.allSettled(this.inflight.slice());\n await this.awaitBarrier(barrier, options.timeoutMs);\n if (this.pendingError !== null) {\n const error = this.pendingError;\n this.pendingError = null;\n throw error;\n }\n return { committedHighWaterMark: this.committed };\n }\n\n private async awaitBarrier(\n barrier: Promise<unknown>,\n timeoutMs: number | undefined,\n ): Promise<void> {\n if (timeoutMs === undefined) {\n await barrier;\n return;\n }\n let timer: ReturnType<typeof setTimeout> | null = null;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`trace flush barrier exceeded ${timeoutMs}ms`)),\n timeoutMs,\n );\n });\n try {\n await Promise.race([barrier.then(() => undefined), timeout]);\n } finally {\n if (timer !== null) {\n clearTimeout(timer);\n }\n }\n }\n\n private enqueue(row: TraceCommitRow): void {\n const op = Promise.resolve()\n .then(() => this.port.persist(row))\n .then(\n () => {\n if (row.commitSeq > this.committed) {\n this.committed = row.commitSeq;\n }\n },\n (error: unknown) => {\n this.pendingError = error;\n },\n );\n this.inflight.push(op);\n }\n\n private resolveProducerId(event: WoodTraceEventSnapshot): string {\n if (event.process === 'renderer') {\n const rendererId = event.payload?.woodRendererProducerId;\n return typeof rendererId === 'string' ? rendererId : 'renderer';\n }\n if (event.process === 'main') {\n return this.mainProducerId;\n }\n return event.process;\n }\n\n private toRow(\n event: WoodTraceEventSnapshot,\n producerId: string,\n producerSeq: number,\n ): TraceCommitRow {\n return {\n commitSeq: this.nextCommitSeq++,\n producerId,\n producerSeq,\n process: event.process,\n level: event.level,\n type: event.type,\n source: event.source,\n ...(event.message !== undefined ? { message: event.message } : {}),\n ...(event.conversationId !== undefined ? { conversationId: event.conversationId } : {}),\n timestamp: event.timestamp,\n ...(event.payload ? { payload: { ...event.payload } } : {}),\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -22,8 +22,10 @@ __export(trace_hub_exports, {
22
22
  dispatchRendererTracePayload: () => dispatchRendererTracePayload,
23
23
  emitRendererTrace: () => emitRendererTrace,
24
24
  getTraceEvents$: () => getTraceEvents$,
25
+ normalizeTraceLevel: () => normalizeTraceLevel,
25
26
  publishMainTrace: () => publishMainTrace,
26
27
  publishTestTrace: () => publishTestTrace,
28
+ registerTraceSink: () => registerTraceSink,
27
29
  setMainTraceMinLevel: () => setMainTraceMinLevel,
28
30
  setRendererTraceEnabled: () => setRendererTraceEnabled,
29
31
  setRendererTraceMinLevel: () => setRendererTraceMinLevel,
@@ -39,6 +41,17 @@ const LEVEL_ORDER = {
39
41
  warn: 2,
40
42
  error: 3
41
43
  };
44
+ const CUSTOM_LEVEL_ORDER = LEVEL_ORDER.info;
45
+ function normalizeTraceLevel(level, fallback = "info") {
46
+ if (typeof level !== "string") {
47
+ return fallback;
48
+ }
49
+ const normalized = level.trim().toLowerCase();
50
+ return normalized.length > 0 ? normalized : fallback;
51
+ }
52
+ function traceLevelOrder(level) {
53
+ return LEVEL_ORDER[level] ?? CUSTOM_LEVEL_ORDER;
54
+ }
42
55
  let rendererMinLevel = "debug";
43
56
  let rendererEnabled = false;
44
57
  let customRendererTraceTransport = null;
@@ -89,6 +102,7 @@ class TraceHub {
89
102
  constructor() {
90
103
  this.subject = new import_rxjs.Subject();
91
104
  this.shared$ = this.subject.asObservable().pipe((0, import_operators.share)());
105
+ this.sinks = /* @__PURE__ */ new Set();
92
106
  this.nextTraceSeq = 1;
93
107
  this.minLevels = {
94
108
  main: "debug",
@@ -102,6 +116,12 @@ class TraceHub {
102
116
  setMinLevel(process, level) {
103
117
  this.minLevels[process] = level;
104
118
  }
119
+ registerSink(sink) {
120
+ this.sinks.add(sink);
121
+ return () => {
122
+ this.sinks.delete(sink);
123
+ };
124
+ }
105
125
  publish(process, level, input) {
106
126
  const normalized = normalizeTraceInput(input);
107
127
  const event = {
@@ -111,11 +131,40 @@ class TraceHub {
111
131
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
112
132
  traceSeq: this.nextTraceSeq++
113
133
  };
114
- if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {
134
+ if (traceLevelOrder(level) >= traceLevelOrder(this.minLevels[process])) {
135
+ this.publishToSinks(event);
115
136
  this.subject.next(event);
116
137
  }
117
138
  return event;
118
139
  }
140
+ publishRendererToSinks(level, input) {
141
+ if (this.sinks.size === 0) {
142
+ return;
143
+ }
144
+ const event = {
145
+ ...input,
146
+ level,
147
+ process: "renderer",
148
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
149
+ traceSeq: this.nextTraceSeq++
150
+ };
151
+ this.publishToSinks(event);
152
+ }
153
+ publishToSinks(event) {
154
+ if (this.sinks.size === 0) {
155
+ return;
156
+ }
157
+ const snapshot = Object.freeze({
158
+ ...event,
159
+ ...event.payload ? { payload: Object.freeze({ ...event.payload }) } : {}
160
+ });
161
+ for (const sink of this.sinks) {
162
+ try {
163
+ sink(snapshot);
164
+ } catch {
165
+ }
166
+ }
167
+ }
119
168
  }
120
169
  function getGlobalTraceHub() {
121
170
  const globalObj = globalThis;
@@ -127,6 +176,9 @@ function getGlobalTraceHub() {
127
176
  function getTraceEvents$() {
128
177
  return getGlobalTraceHub().events$;
129
178
  }
179
+ function registerTraceSink(sink) {
180
+ return getGlobalTraceHub().registerSink(sink);
181
+ }
130
182
  function setMainTraceMinLevel(level) {
131
183
  getGlobalTraceHub().setMinLevel("main", level);
132
184
  }
@@ -145,7 +197,7 @@ function publishTestTrace(process, level, input) {
145
197
  }
146
198
  function dispatchRendererTracePayload(rawPayload, context) {
147
199
  const payload = rawPayload ?? {};
148
- const level = payload.level === "debug" || payload.level === "info" || payload.level === "warn" || payload.level === "error" ? payload.level : "info";
200
+ const level = normalizeTraceLevel(payload.level);
149
201
  getGlobalTraceHub().publish("renderer", level, mergeTraceRuntimeContext(payload, context));
150
202
  return {
151
203
  ok: true,
@@ -159,10 +211,11 @@ function emitRendererTrace(level, input) {
159
211
  if (!rendererEnabled) {
160
212
  return;
161
213
  }
162
- if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {
214
+ if (traceLevelOrder(level) < traceLevelOrder(rendererMinLevel)) {
163
215
  return;
164
216
  }
165
217
  const normalized = { ...normalizeTraceInput(input), level };
218
+ getGlobalTraceHub().publishRendererToSinks(level, normalized);
166
219
  if (customRendererTraceTransport) {
167
220
  try {
168
221
  customRendererTraceTransport(normalized);
@@ -192,8 +245,10 @@ function clearTraceHubForTests() {
192
245
  dispatchRendererTracePayload,
193
246
  emitRendererTrace,
194
247
  getTraceEvents$,
248
+ normalizeTraceLevel,
195
249
  publishMainTrace,
196
250
  publishTestTrace,
251
+ registerTraceSink,
197
252
  setMainTraceMinLevel,
198
253
  setRendererTraceEnabled,
199
254
  setRendererTraceMinLevel,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject } from 'rxjs';\nimport { share } from 'rxjs/operators';\n\nexport type TraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n windowId?: string;\n webContentsId?: number;\n routeKey?: string;\n harnessId?: string;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceRuntimeContext = {\n windowId?: string;\n webContentsId?: number;\n routeKey?: string;\n harnessId?: string;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<TraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nlet rendererMinLevel: TraceLevel = 'debug';\nlet rendererEnabled = false;\nlet customRendererTraceTransport: ((payload: unknown) => void) | null = null;\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const windowId = typeof input.windowId === 'string' && input.windowId.trim().length > 0\n ? input.windowId\n : undefined;\n const webContentsId = typeof input.webContentsId === 'number' && Number.isFinite(input.webContentsId)\n ? input.webContentsId\n : undefined;\n const routeKey = typeof input.routeKey === 'string' && input.routeKey.trim().length > 0\n ? input.routeKey\n : undefined;\n const harnessId = typeof input.harnessId === 'string' && input.harnessId.trim().length > 0\n ? input.harnessId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(windowId !== undefined ? { windowId } : {}),\n ...(webContentsId !== undefined ? { webContentsId } : {}),\n ...(routeKey !== undefined ? { routeKey } : {}),\n ...(harnessId !== undefined ? { harnessId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction mergeTraceRuntimeContext(input: unknown, context: TraceRuntimeContext | undefined): TraceInput {\n if (!context) {\n return normalizeTraceInput(input);\n }\n return normalizeTraceInput({\n ...(isRecord(input) ? input : {}),\n ...context,\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private nextTraceSeq = 1;\n private minLevels: Record<TraceProcess, TraceLevel> = {\n main: 'debug',\n renderer: 'debug',\n test: 'debug',\n };\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(process: TraceProcess, level: TraceLevel): void {\n this.minLevels[process] = level;\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {\n this.subject.next(event);\n }\n return event;\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel('main', level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n getGlobalTraceHub().setMinLevel('renderer', level);\n}\n\nexport function setRendererTraceEnabled(enabled: boolean): void {\n rendererEnabled = enabled;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(\n rawPayload: unknown,\n context?: TraceRuntimeContext,\n): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: TraceLevel };\n const level = payload.level === 'debug' || payload.level === 'info' || payload.level === 'warn' || payload.level === 'error'\n ? payload.level\n : 'info';\n getGlobalTraceHub().publish('renderer', level, mergeTraceRuntimeContext(payload, context));\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void {\n customRendererTraceTransport = fn;\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (!rendererEnabled) {\n return;\n }\n if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {\n return;\n }\n const normalized = { ...normalizeTraceInput(input), level };\n\n if (customRendererTraceTransport) {\n try {\n customRendererTraceTransport(normalized);\n return;\n } catch {\n // Fall through to bridge.__trace\n }\n }\n\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace(normalized);\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n rendererEnabled = false;\n customRendererTraceTransport = null;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/tracing/trace_hub.ts"],"sourcesContent":["import { Observable, Subject } from 'rxjs';\nimport { share } from 'rxjs/operators';\nimport type {\n WoodTraceEventSink,\n WoodTraceEventSnapshot,\n WoodTraceSinkDisposer,\n} from './types/wood_trace_sink.types.js';\n\nexport type StandardTraceLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type TraceLevel = StandardTraceLevel | (string & {});\nexport type TraceProcess = 'main' | 'renderer' | 'test';\n\nexport type TraceInput = {\n source: string;\n type: string;\n message?: string;\n conversationId?: number | string;\n payload?: Record<string, unknown>;\n windowId?: string;\n webContentsId?: number;\n routeKey?: string;\n harnessId?: string;\n};\n\nexport type TraceEvent = TraceInput & {\n level: TraceLevel;\n process: TraceProcess;\n timestamp: string;\n traceSeq: number;\n};\n\nexport type TraceDispatchResult = {\n ok: boolean;\n forwarded: boolean;\n};\n\nexport type TraceRuntimeContext = {\n windowId?: string;\n webContentsId?: number;\n routeKey?: string;\n harnessId?: string;\n};\n\nexport type TraceWaitOptions = {\n timeoutMs?: number;\n};\n\nconst TRACE_HUB_SYMBOL = Symbol.for('@noego/wood.trace.hub');\n\nconst LEVEL_ORDER: Record<StandardTraceLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\nconst CUSTOM_LEVEL_ORDER = LEVEL_ORDER.info;\n\nexport function normalizeTraceLevel(level: unknown, fallback: TraceLevel = 'info'): TraceLevel {\n if (typeof level !== 'string') {\n return fallback;\n }\n\n const normalized = level.trim().toLowerCase();\n return normalized.length > 0 ? normalized : fallback;\n}\n\nfunction traceLevelOrder(level: TraceLevel): number {\n return LEVEL_ORDER[level as StandardTraceLevel] ?? CUSTOM_LEVEL_ORDER;\n}\n\nlet rendererMinLevel: TraceLevel = 'debug';\nlet rendererEnabled = false;\nlet customRendererTraceTransport: ((payload: unknown) => void) | null = null;\n\ntype GlobalWithTraceHub = typeof globalThis & {\n [TRACE_HUB_SYMBOL]?: TraceHub;\n};\n\ntype TraceBridgeLike = Record<string, unknown> & {\n __trace?: (payload: unknown) => void;\n};\n\ntype GlobalWithTraceBridge = typeof globalThis & {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n window?: {\n __NOEGO_WOOD_BRIDGE__?: unknown;\n wood?: unknown;\n };\n};\n\nfunction normalizeTraceInput(raw: unknown): TraceInput {\n const input = (raw ?? {}) as Partial<TraceInput> & Record<string, unknown>;\n const source = typeof input.source === 'string' && input.source.trim().length > 0\n ? input.source\n : 'unknown';\n const type = typeof input.type === 'string' && input.type.trim().length > 0\n ? input.type\n : 'unknown';\n const message = typeof input.message === 'string' ? input.message : undefined;\n const conversationId = typeof input.conversationId === 'number' || typeof input.conversationId === 'string'\n ? input.conversationId\n : undefined;\n const windowId = typeof input.windowId === 'string' && input.windowId.trim().length > 0\n ? input.windowId\n : undefined;\n const webContentsId = typeof input.webContentsId === 'number' && Number.isFinite(input.webContentsId)\n ? input.webContentsId\n : undefined;\n const routeKey = typeof input.routeKey === 'string' && input.routeKey.trim().length > 0\n ? input.routeKey\n : undefined;\n const harnessId = typeof input.harnessId === 'string' && input.harnessId.trim().length > 0\n ? input.harnessId\n : undefined;\n const payload = isRecord(input.payload) ? { ...input.payload } : undefined;\n\n return {\n source,\n type,\n ...(message ? { message } : {}),\n ...(conversationId !== undefined ? { conversationId } : {}),\n ...(windowId !== undefined ? { windowId } : {}),\n ...(webContentsId !== undefined ? { webContentsId } : {}),\n ...(routeKey !== undefined ? { routeKey } : {}),\n ...(harnessId !== undefined ? { harnessId } : {}),\n ...(payload ? { payload } : {}),\n };\n}\n\nfunction mergeTraceRuntimeContext(input: unknown, context: TraceRuntimeContext | undefined): TraceInput {\n if (!context) {\n return normalizeTraceInput(input);\n }\n return normalizeTraceInput({\n ...(isRecord(input) ? input : {}),\n ...context,\n });\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction resolveTraceBridge(): TraceBridgeLike | null {\n const globalObj = globalThis as GlobalWithTraceBridge;\n const bridge = globalObj.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.window?.__NOEGO_WOOD_BRIDGE__\n ?? globalObj.wood\n ?? globalObj.window?.wood;\n if (!bridge || typeof bridge !== 'object') {\n return null;\n }\n return bridge as TraceBridgeLike;\n}\n\nclass TraceHub {\n private readonly subject = new Subject<TraceEvent>();\n private readonly shared$ = this.subject.asObservable().pipe(share());\n private readonly sinks = new Set<WoodTraceEventSink>();\n private nextTraceSeq = 1;\n private minLevels: Record<TraceProcess, TraceLevel> = {\n main: 'debug',\n renderer: 'debug',\n test: 'debug',\n };\n\n get events$(): Observable<TraceEvent> {\n return this.shared$;\n }\n\n setMinLevel(process: TraceProcess, level: TraceLevel): void {\n this.minLevels[process] = level;\n }\n\n registerSink(sink: WoodTraceEventSink): WoodTraceSinkDisposer {\n this.sinks.add(sink);\n return () => {\n this.sinks.delete(sink);\n };\n }\n\n publish(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n const normalized = normalizeTraceInput(input);\n const event: TraceEvent = {\n ...normalized,\n level,\n process,\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n if (traceLevelOrder(level) >= traceLevelOrder(this.minLevels[process])) {\n this.publishToSinks(event);\n this.subject.next(event);\n }\n return event;\n }\n\n publishRendererToSinks(level: TraceLevel, input: TraceInput): void {\n if (this.sinks.size === 0) {\n return;\n }\n const event: TraceEvent = {\n ...input,\n level,\n process: 'renderer',\n timestamp: new Date().toISOString(),\n traceSeq: this.nextTraceSeq++,\n };\n this.publishToSinks(event);\n }\n\n private publishToSinks(event: TraceEvent): void {\n if (this.sinks.size === 0) {\n return;\n }\n const snapshot: WoodTraceEventSnapshot = Object.freeze({\n ...event,\n ...(event.payload ? { payload: Object.freeze({ ...event.payload }) } : {}),\n });\n for (const sink of this.sinks) {\n try {\n sink(snapshot);\n } catch {\n // Trace sinks are observational and cannot fail trace callers or peers.\n }\n }\n }\n}\n\nfunction getGlobalTraceHub(): TraceHub {\n const globalObj = globalThis as GlobalWithTraceHub;\n if (!globalObj[TRACE_HUB_SYMBOL]) {\n globalObj[TRACE_HUB_SYMBOL] = new TraceHub();\n }\n return globalObj[TRACE_HUB_SYMBOL] as TraceHub;\n}\n\nexport function getTraceEvents$(): Observable<TraceEvent> {\n return getGlobalTraceHub().events$;\n}\n\nexport function registerTraceSink(sink: WoodTraceEventSink): WoodTraceSinkDisposer {\n return getGlobalTraceHub().registerSink(sink);\n}\n\nexport function setMainTraceMinLevel(level: TraceLevel): void {\n getGlobalTraceHub().setMinLevel('main', level);\n}\n\nexport function setRendererTraceMinLevel(level: TraceLevel): void {\n rendererMinLevel = level;\n getGlobalTraceHub().setMinLevel('renderer', level);\n}\n\nexport function setRendererTraceEnabled(enabled: boolean): void {\n rendererEnabled = enabled;\n}\n\nexport function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish('main', level, input);\n}\n\nexport function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent {\n return getGlobalTraceHub().publish(process, level, input);\n}\n\nexport function dispatchRendererTracePayload(\n rawPayload: unknown,\n context?: TraceRuntimeContext,\n): TraceDispatchResult {\n const payload = (rawPayload ?? {}) as Partial<TraceInput> & { level?: unknown };\n const level = normalizeTraceLevel(payload.level);\n getGlobalTraceHub().publish('renderer', level, mergeTraceRuntimeContext(payload, context));\n return {\n ok: true,\n forwarded: true,\n };\n}\n\nexport function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void {\n customRendererTraceTransport = fn;\n}\n\nexport function emitRendererTrace(level: TraceLevel, input: TraceInput): void {\n if (!rendererEnabled) {\n return;\n }\n if (traceLevelOrder(level) < traceLevelOrder(rendererMinLevel)) {\n return;\n }\n const normalized = { ...normalizeTraceInput(input), level };\n getGlobalTraceHub().publishRendererToSinks(level, normalized);\n\n if (customRendererTraceTransport) {\n try {\n customRendererTraceTransport(normalized);\n return;\n } catch {\n // Fall through to bridge.__trace\n }\n }\n\n const bridge = resolveTraceBridge();\n if (!bridge || typeof bridge.__trace !== 'function') {\n return;\n }\n try {\n bridge.__trace(normalized);\n } catch {\n // Tracing is observational and must never break the caller.\n }\n}\n\nexport function clearTraceHubForTests(): void {\n const globalObj = globalThis as GlobalWithTraceHub;\n delete globalObj[TRACE_HUB_SYMBOL];\n rendererMinLevel = 'debug';\n rendererEnabled = false;\n customRendererTraceTransport = null;\n}\n"],"mappings":"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","names":[]}
@@ -1,46 +1,2 @@
1
- import { Observable } from 'rxjs';
2
-
3
- type TraceLevel = 'debug' | 'info' | 'warn' | 'error';
4
- type TraceProcess = 'main' | 'renderer' | 'test';
5
- type TraceInput = {
6
- source: string;
7
- type: string;
8
- message?: string;
9
- conversationId?: number | string;
10
- payload?: Record<string, unknown>;
11
- windowId?: string;
12
- webContentsId?: number;
13
- routeKey?: string;
14
- harnessId?: string;
15
- };
16
- type TraceEvent = TraceInput & {
17
- level: TraceLevel;
18
- process: TraceProcess;
19
- timestamp: string;
20
- traceSeq: number;
21
- };
22
- type TraceDispatchResult = {
23
- ok: boolean;
24
- forwarded: boolean;
25
- };
26
- type TraceRuntimeContext = {
27
- windowId?: string;
28
- webContentsId?: number;
29
- routeKey?: string;
30
- harnessId?: string;
31
- };
32
- type TraceWaitOptions = {
33
- timeoutMs?: number;
34
- };
35
- declare function getTraceEvents$(): Observable<TraceEvent>;
36
- declare function setMainTraceMinLevel(level: TraceLevel): void;
37
- declare function setRendererTraceMinLevel(level: TraceLevel): void;
38
- declare function setRendererTraceEnabled(enabled: boolean): void;
39
- declare function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent;
40
- declare function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent;
41
- declare function dispatchRendererTracePayload(rawPayload: unknown, context?: TraceRuntimeContext): TraceDispatchResult;
42
- declare function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void;
43
- declare function emitRendererTrace(level: TraceLevel, input: TraceInput): void;
44
- declare function clearTraceHubForTests(): void;
45
-
46
- export { type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceRuntimeContext, type TraceWaitOptions, clearTraceHubForTests, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport };
1
+ import 'rxjs';
2
+ export { S as StandardTraceLevel, T as TraceDispatchResult, a as TraceEvent, b as TraceInput, c as TraceLevel, d as TraceProcess, e as TraceRuntimeContext, f as TraceWaitOptions, i as clearTraceHubForTests, l as dispatchRendererTracePayload, m as emitRendererTrace, j as getTraceEvents$, t as normalizeTraceLevel, p as publishMainTrace, n as publishTestTrace, r as registerTraceSink, o as setMainTraceMinLevel, s as setRendererTraceEnabled, q as setRendererTraceMinLevel, k as setRendererTraceTransport } from '../trace_hub-Cob54qRx.cjs';
@@ -1,46 +1,2 @@
1
- import { Observable } from 'rxjs';
2
-
3
- type TraceLevel = 'debug' | 'info' | 'warn' | 'error';
4
- type TraceProcess = 'main' | 'renderer' | 'test';
5
- type TraceInput = {
6
- source: string;
7
- type: string;
8
- message?: string;
9
- conversationId?: number | string;
10
- payload?: Record<string, unknown>;
11
- windowId?: string;
12
- webContentsId?: number;
13
- routeKey?: string;
14
- harnessId?: string;
15
- };
16
- type TraceEvent = TraceInput & {
17
- level: TraceLevel;
18
- process: TraceProcess;
19
- timestamp: string;
20
- traceSeq: number;
21
- };
22
- type TraceDispatchResult = {
23
- ok: boolean;
24
- forwarded: boolean;
25
- };
26
- type TraceRuntimeContext = {
27
- windowId?: string;
28
- webContentsId?: number;
29
- routeKey?: string;
30
- harnessId?: string;
31
- };
32
- type TraceWaitOptions = {
33
- timeoutMs?: number;
34
- };
35
- declare function getTraceEvents$(): Observable<TraceEvent>;
36
- declare function setMainTraceMinLevel(level: TraceLevel): void;
37
- declare function setRendererTraceMinLevel(level: TraceLevel): void;
38
- declare function setRendererTraceEnabled(enabled: boolean): void;
39
- declare function publishMainTrace(level: TraceLevel, input: unknown): TraceEvent;
40
- declare function publishTestTrace(process: TraceProcess, level: TraceLevel, input: unknown): TraceEvent;
41
- declare function dispatchRendererTracePayload(rawPayload: unknown, context?: TraceRuntimeContext): TraceDispatchResult;
42
- declare function setRendererTraceTransport(fn: ((payload: unknown) => void) | null): void;
43
- declare function emitRendererTrace(level: TraceLevel, input: TraceInput): void;
44
- declare function clearTraceHubForTests(): void;
45
-
46
- export { type TraceDispatchResult, type TraceEvent, type TraceInput, type TraceLevel, type TraceProcess, type TraceRuntimeContext, type TraceWaitOptions, clearTraceHubForTests, dispatchRendererTracePayload, emitRendererTrace, getTraceEvents$, publishMainTrace, publishTestTrace, setMainTraceMinLevel, setRendererTraceEnabled, setRendererTraceMinLevel, setRendererTraceTransport };
1
+ import 'rxjs';
2
+ export { S as StandardTraceLevel, T as TraceDispatchResult, a as TraceEvent, b as TraceInput, c as TraceLevel, d as TraceProcess, e as TraceRuntimeContext, f as TraceWaitOptions, i as clearTraceHubForTests, l as dispatchRendererTracePayload, m as emitRendererTrace, j as getTraceEvents$, t as normalizeTraceLevel, p as publishMainTrace, n as publishTestTrace, r as registerTraceSink, o as setMainTraceMinLevel, s as setRendererTraceEnabled, q as setRendererTraceMinLevel, k as setRendererTraceTransport } from '../trace_hub-Cob54qRx.js';
@@ -7,6 +7,17 @@ const LEVEL_ORDER = {
7
7
  warn: 2,
8
8
  error: 3
9
9
  };
10
+ const CUSTOM_LEVEL_ORDER = LEVEL_ORDER.info;
11
+ function normalizeTraceLevel(level, fallback = "info") {
12
+ if (typeof level !== "string") {
13
+ return fallback;
14
+ }
15
+ const normalized = level.trim().toLowerCase();
16
+ return normalized.length > 0 ? normalized : fallback;
17
+ }
18
+ function traceLevelOrder(level) {
19
+ return LEVEL_ORDER[level] ?? CUSTOM_LEVEL_ORDER;
20
+ }
10
21
  let rendererMinLevel = "debug";
11
22
  let rendererEnabled = false;
12
23
  let customRendererTraceTransport = null;
@@ -57,6 +68,7 @@ class TraceHub {
57
68
  constructor() {
58
69
  this.subject = new Subject();
59
70
  this.shared$ = this.subject.asObservable().pipe(share());
71
+ this.sinks = /* @__PURE__ */ new Set();
60
72
  this.nextTraceSeq = 1;
61
73
  this.minLevels = {
62
74
  main: "debug",
@@ -70,6 +82,12 @@ class TraceHub {
70
82
  setMinLevel(process, level) {
71
83
  this.minLevels[process] = level;
72
84
  }
85
+ registerSink(sink) {
86
+ this.sinks.add(sink);
87
+ return () => {
88
+ this.sinks.delete(sink);
89
+ };
90
+ }
73
91
  publish(process, level, input) {
74
92
  const normalized = normalizeTraceInput(input);
75
93
  const event = {
@@ -79,11 +97,40 @@ class TraceHub {
79
97
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
80
98
  traceSeq: this.nextTraceSeq++
81
99
  };
82
- if (LEVEL_ORDER[level] >= LEVEL_ORDER[this.minLevels[process]]) {
100
+ if (traceLevelOrder(level) >= traceLevelOrder(this.minLevels[process])) {
101
+ this.publishToSinks(event);
83
102
  this.subject.next(event);
84
103
  }
85
104
  return event;
86
105
  }
106
+ publishRendererToSinks(level, input) {
107
+ if (this.sinks.size === 0) {
108
+ return;
109
+ }
110
+ const event = {
111
+ ...input,
112
+ level,
113
+ process: "renderer",
114
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
115
+ traceSeq: this.nextTraceSeq++
116
+ };
117
+ this.publishToSinks(event);
118
+ }
119
+ publishToSinks(event) {
120
+ if (this.sinks.size === 0) {
121
+ return;
122
+ }
123
+ const snapshot = Object.freeze({
124
+ ...event,
125
+ ...event.payload ? { payload: Object.freeze({ ...event.payload }) } : {}
126
+ });
127
+ for (const sink of this.sinks) {
128
+ try {
129
+ sink(snapshot);
130
+ } catch {
131
+ }
132
+ }
133
+ }
87
134
  }
88
135
  function getGlobalTraceHub() {
89
136
  const globalObj = globalThis;
@@ -95,6 +142,9 @@ function getGlobalTraceHub() {
95
142
  function getTraceEvents$() {
96
143
  return getGlobalTraceHub().events$;
97
144
  }
145
+ function registerTraceSink(sink) {
146
+ return getGlobalTraceHub().registerSink(sink);
147
+ }
98
148
  function setMainTraceMinLevel(level) {
99
149
  getGlobalTraceHub().setMinLevel("main", level);
100
150
  }
@@ -113,7 +163,7 @@ function publishTestTrace(process, level, input) {
113
163
  }
114
164
  function dispatchRendererTracePayload(rawPayload, context) {
115
165
  const payload = rawPayload ?? {};
116
- const level = payload.level === "debug" || payload.level === "info" || payload.level === "warn" || payload.level === "error" ? payload.level : "info";
166
+ const level = normalizeTraceLevel(payload.level);
117
167
  getGlobalTraceHub().publish("renderer", level, mergeTraceRuntimeContext(payload, context));
118
168
  return {
119
169
  ok: true,
@@ -127,10 +177,11 @@ function emitRendererTrace(level, input) {
127
177
  if (!rendererEnabled) {
128
178
  return;
129
179
  }
130
- if (LEVEL_ORDER[level] < LEVEL_ORDER[rendererMinLevel]) {
180
+ if (traceLevelOrder(level) < traceLevelOrder(rendererMinLevel)) {
131
181
  return;
132
182
  }
133
183
  const normalized = { ...normalizeTraceInput(input), level };
184
+ getGlobalTraceHub().publishRendererToSinks(level, normalized);
134
185
  if (customRendererTraceTransport) {
135
186
  try {
136
187
  customRendererTraceTransport(normalized);
@@ -159,8 +210,10 @@ export {
159
210
  dispatchRendererTracePayload,
160
211
  emitRendererTrace,
161
212
  getTraceEvents$,
213
+ normalizeTraceLevel,
162
214
  publishMainTrace,
163
215
  publishTestTrace,
216
+ registerTraceSink,
164
217
  setMainTraceMinLevel,
165
218
  setRendererTraceEnabled,
166
219
  setRendererTraceMinLevel,