@noego/wood 0.4.6 → 0.4.7

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 (241) 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 +22 -3
  240. package/src/components/NavigationShell.browser.test.ts +236 -0
  241. package/src/components/NavigationShell.svelte +162 -49
@@ -1,5 +1,6 @@
1
- import { TraceLevel } from '../tracing/trace_hub.cjs';
1
+ import { c as TraceLevel } from '../trace_hub-Cob54qRx.cjs';
2
2
  import { TraceRecorder } from '../trace-testing/trace_recorder.cjs';
3
+ import { WoodRpcTransport } from '../transport/wood_rpc_transport.cjs';
3
4
  import 'rxjs';
4
5
  import '@noego/trace/testing';
5
6
  import '../tracing/trace_provider.cjs';
@@ -31,6 +32,30 @@ interface WoodBridgeFixtureInput {
31
32
  operations?: Record<string, OperationHandler>;
32
33
  events?: Record<string, unknown | unknown[]>;
33
34
  record?: boolean;
35
+ /**
36
+ * Real-backend mode. When supplied, page `invoke` is routed through this
37
+ * `WoodRpcTransport` (dispatching through the REAL WoodRouter — full schema
38
+ * validation) instead of the fixture `operations` Map, and page `bridge.on(...)`
39
+ * subscriptions are relayed to `transport.subscribe(...)` so main-process push
40
+ * events reach page subscribers. Fixture `operations` still act as a fallback
41
+ * for any channel not registered on the transport, so hybrid fixtures work.
42
+ * Omit for the legacy fixture-only bridge (spec §4: labeled visual tests).
43
+ */
44
+ transport?: WoodRpcTransport;
45
+ /**
46
+ * Full RPC manifest to build the page bridge tree from. Required in transport
47
+ * mode so every real channel is exposed on the bridge (in fixture mode the
48
+ * manifest is derived from `operations`). Supply the app's complete operations
49
+ * manifest (channel/controller/action/path/hasInput per operation).
50
+ */
51
+ rpcManifest?: WoodRpcManifestEntryInput[];
52
+ }
53
+ interface WoodRpcManifestEntryInput {
54
+ channel: string;
55
+ controller: string;
56
+ action: string;
57
+ path: string[];
58
+ hasInput: boolean;
34
59
  }
35
60
  interface WoodBrowserPageContext {
36
61
  page: any;
@@ -120,6 +145,43 @@ interface MountedWoodNavigation {
120
145
  go(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<void>;
121
146
  replace(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<void>;
122
147
  }
148
+ /** Snapshot of one element's viewport-space box, tagged with its source selector. */
149
+ interface WoodRectSnapshot {
150
+ selector: string;
151
+ top: number;
152
+ left: number;
153
+ right: number;
154
+ bottom: number;
155
+ width: number;
156
+ height: number;
157
+ }
158
+ /** Axis along which edge-to-edge spacing is measured. */
159
+ type WoodSpacingAxis = 'horizontal' | 'vertical';
160
+ /**
161
+ * Description of the top-most element painted at a hit-test point, plus its
162
+ * relationship to the queried target. `document.elementFromPoint` honours
163
+ * `pointer-events`, so an element with `pointer-events: none` is transparent to
164
+ * the hit test even when it is painted on top (see `hitTest`/`unoccluded`).
165
+ */
166
+ interface WoodHitTestResult {
167
+ /** False when no element is painted at the point (e.g. outside the document). */
168
+ found: boolean;
169
+ tag: string | null;
170
+ id: string | null;
171
+ classes: string[];
172
+ /** The hit element is exactly the target. */
173
+ isTarget: boolean;
174
+ /** The hit element is a descendant of the target (target paints through it). */
175
+ isDescendantOfTarget: boolean;
176
+ /** The hit element is an ancestor of the target. */
177
+ isAncestorOfTarget: boolean;
178
+ point: {
179
+ x: number;
180
+ y: number;
181
+ };
182
+ /** Human-readable one-line description of the hit element. */
183
+ description: string;
184
+ }
123
185
  interface WoodBrowserExpect {
124
186
  visible(selector: string): Promise<void>;
125
187
  hidden(selector: string): Promise<void>;
@@ -128,6 +190,12 @@ interface WoodBrowserExpect {
128
190
  }): Promise<void>;
129
191
  notClipped(selector: string): Promise<void>;
130
192
  notOverlapping(firstSelector: string, secondSelector: string): Promise<void>;
193
+ /**
194
+ * @deprecated Compares numeric computed `z-index` only, so it lies whenever
195
+ * stacking contexts, DOM paint order (auto/equal z-index), or
196
+ * transform/opacity contexts decide the real front-to-back order. Prefer
197
+ * `inFrontOf`, which hit-tests the actual painted pixel.
198
+ */
131
199
  zIndexAbove(topSelector: string, lowerSelector: string): Promise<void>;
132
200
  stableAfterAnimation(selector?: string, options?: {
133
201
  timeoutMs?: number;
@@ -138,6 +206,142 @@ interface WoodBrowserExpect {
138
206
  mask?: string[];
139
207
  threshold?: number;
140
208
  }): Promise<void>;
209
+ /** Batch `getBoundingClientRect` reader — one snapshot per selector, in order. */
210
+ rects(selectors: string[]): Promise<WoodRectSnapshot[]>;
211
+ /**
212
+ * Hit-test at a point (default the target's center) and describe the top-most
213
+ * painted element and how it relates to the target.
214
+ */
215
+ hitTest(selector: string, point?: {
216
+ x: number;
217
+ y: number;
218
+ } | 'center'): Promise<WoodHitTestResult>;
219
+ /** Assert two elements share the given edge coordinate within `tolerance` px. */
220
+ sharedEdge(a: string, b: string, edge: 'left' | 'right' | 'top' | 'bottom', options?: {
221
+ tolerance?: number;
222
+ }): Promise<void>;
223
+ /** Assert two elements share a center axis (centerX/centerY) within `tolerance` px. */
224
+ sharedAxis(a: string, b: string, axis: 'centerX' | 'centerY', options?: {
225
+ tolerance?: number;
226
+ }): Promise<void>;
227
+ /**
228
+ * Point-specific "does a click land here" check. Asserts the target receives
229
+ * the hit test at `point` (default center): the hit-test element is the target
230
+ * or a descendant of it. Ancestors are accepted only when the target is
231
+ * empty/zero-content at that point and the ancestor is what paints there — but a
232
+ * foreign overlay (not in the target's ancestry) fails. Honours
233
+ * `pointer-events: none`: an overlay so styled does not occlude, because it does
234
+ * not intercept the click.
235
+ *
236
+ * This is NOT the general visibility check — a `pointer-events: none` overlay
237
+ * that visually hides the element still passes here. For "is any foreign layer
238
+ * painted on top of the element", prefer `notCovered` (paint-order coverage).
239
+ */
240
+ unoccluded(selector: string, options?: {
241
+ point?: {
242
+ x: number;
243
+ y: number;
244
+ } | 'center';
245
+ }): Promise<void>;
246
+ /**
247
+ * Honest front/behind primitive. DEFAULT (recommended): a whole-element
248
+ * paint-order comparison backed by Chromium's real paint tree (CDP
249
+ * `DOMSnapshot.captureSnapshot` with paint order). Passes when `topSelector`
250
+ * has a strictly greater global paint-order index than `lowerSelector` — i.e.
251
+ * it is painted later, so it is on top. This answers "which layer paints on
252
+ * top" for the whole element, independent of overlap or `pointer-events`, and
253
+ * is Chromium-only.
254
+ *
255
+ * Opt-in point form `inFrontOf(top, lower, { at })`: hit-tests a single painted
256
+ * pixel (`at` defaults to the center of the two rects' intersection) and passes
257
+ * when the painted element there is the top element (or its descendant) and is
258
+ * NOT the lower element. Use this only for the point-specific question ("is the
259
+ * top element the one painted AT THIS SPOT"); it honours `pointer-events` and
260
+ * requires the rects to intersect when no explicit `at` is given.
261
+ *
262
+ * If an element has multiple paint fragments, its maximum paint-order index is
263
+ * used. When the rects do not overlap the paint-order verdict is still valid
264
+ * (paint order is global), but the failure message notes there is no shared
265
+ * pixel where front/behind is visually observable.
266
+ */
267
+ inFrontOf(topSelector: string, lowerSelector: string, options?: {
268
+ at?: {
269
+ x: number;
270
+ y: number;
271
+ };
272
+ }): Promise<void>;
273
+ /**
274
+ * Whole-element painted-coverage check: assert that NO foreign element paints
275
+ * on top of the target anywhere its rect overlaps. Backed by Chromium's real
276
+ * paint tree (CDP `DOMSnapshot.captureSnapshot` with paint order + computed
277
+ * styles + DOM rects), it is the honest, INSPECTABLE replacement for the old
278
+ * IntersectionObserver-v2 `fullyVisible` matcher, whose `isVisible` field is a
279
+ * black box that silently reports not-visible for any element painted on top —
280
+ * even a fully transparent one (see "why not IntersectionObserver v2" in the
281
+ * geometry docs).
282
+ *
283
+ * Algorithm (Chromium-only):
284
+ * 1. Find every laid-out element whose layout rect intersects the target's rect.
285
+ * 2. Exclude the target itself and its ancestors/descendants (they are not
286
+ * foreign coverage — the target paints through its own subtree).
287
+ * 3. Exclude candidates that paint nothing visible in the overlap, per an
288
+ * explicit transparent-wrapper heuristic: computed `opacity: 0` OR
289
+ * `visibility: hidden`, OR (no background-color alpha AND
290
+ * `background-image: none` AND all four border widths `0px` AND no own text
291
+ * or replaced/image content). This is the ONE rule that lets a transparent
292
+ * positioning wrapper spanning the target pass; it is intentionally
293
+ * documented and testable, unlike IOv2's opaque verdict. (`box-shadow`,
294
+ * `outline` and other purely decorative paints are NOT counted as coverage.)
295
+ * 4. Fail if any remaining foreign candidate has a strictly HIGHER max paint
296
+ * order than the target (painted later == on top). The failure names the
297
+ * covering element (tag/id/classes), its rect, and both paint indices.
298
+ *
299
+ * Note this tracks PAINTED coverage, so an opaque `pointer-events: none` overlay
300
+ * DOES fail `notCovered` (it paints pixels on top) while `unoccluded` still
301
+ * passes (it is transparent to hit testing) — the complementary pair of
302
+ * "painted coverage" vs "click reachability".
303
+ */
304
+ notCovered(selector: string): Promise<void>;
305
+ /**
306
+ * Assert the edge-to-edge gap between two elements along `axis` satisfies a
307
+ * numeric constraint. The order is derived from the rects, not the argument
308
+ * order: the box with the smaller leading edge (left for `'horizontal'`, top
309
+ * for `'vertical'`) is treated as the earlier box and the gap is
310
+ * `later.leading - earlier.trailing` (horizontal: `later.left - earlier.right`;
311
+ * vertical: `later.top - earlier.bottom`).
312
+ *
313
+ * At least one of `equals` / `min` / `max` is required. `equals` passes when
314
+ * `|gap - equals| <= tolerance`; `min`/`max` are inclusive within `tolerance`.
315
+ * A negative gap means the border boxes OVERLAP along the axis; that fails
316
+ * clearly UNLESS the constraint explicitly allows negatives (a negative
317
+ * `equals` or `min`). The failure message states both rects, the computed gap,
318
+ * and the expected constraint.
319
+ *
320
+ * BORDER-BOX CAVEAT: rects are border boxes (`getBoundingClientRect`), so the
321
+ * gap is the distance between the elements' outer borders and EXCLUDES any
322
+ * padding inside either element. To assert visual whitespace between inner
323
+ * content, target the inner content elements instead of their padded wrappers.
324
+ */
325
+ gapBetween(a: string, b: string, axis: WoodSpacingAxis, options: {
326
+ equals?: number;
327
+ min?: number;
328
+ max?: number;
329
+ tolerance?: number;
330
+ }): Promise<void>;
331
+ /**
332
+ * Assert three or more elements are evenly spaced along `axis`. Requires at
333
+ * least 3 selectors; the elements are sorted by their leading edge along the
334
+ * axis, adjacent edge-to-edge gaps are computed, and the assertion passes when
335
+ * `max gap - min gap <= tolerance`. The failure message lists every adjacent
336
+ * gap in sorted order and names the widest and narrowest (offending) pairs.
337
+ *
338
+ * BORDER-BOX CAVEAT: gaps are measured between border boxes
339
+ * (`getBoundingClientRect`), so they exclude padding inside the elements. Tests
340
+ * that care about visual whitespace should target inner content elements.
341
+ */
342
+ evenlySpaced(selectors: string[], axis: WoodSpacingAxis, options?: {
343
+ tolerance?: number;
344
+ }): Promise<void>;
141
345
  }
142
346
  interface MountedWoodSurface {
143
347
  page: any;
@@ -161,6 +365,12 @@ interface WoodBrowserHarness {
161
365
  mountRoute(routeKey: string, options?: MountRouteOptions): Promise<MountedWoodRoute>;
162
366
  mountComponent(component: string, options?: MountComponentOptions): Promise<MountedWoodComponent>;
163
367
  mountTree(tree: MountTreeOptions): Promise<MountedWoodTree>;
368
+ /**
369
+ * Drain in-flight renderer `__trace` exposeBinding round-trips so the caller's
370
+ * trace-flush barrier (e.g. `TraceCommitWriter.requestRendererFlush`) has a
371
+ * definite quiescent point. Resolves immediately when no page is mounted.
372
+ */
373
+ flushRendererTraces(): Promise<void>;
164
374
  destroy(): Promise<void>;
165
375
  }
166
376
  type BridgeState = {
@@ -169,6 +379,8 @@ type BridgeState = {
169
379
  calls: WoodBridgeRecordedCall[];
170
380
  emittedEvents: WoodBridgeRecordedEvent[];
171
381
  record: boolean;
382
+ transport?: WoodRpcTransport;
383
+ rpcManifest?: WoodRpcManifestEntryInput[];
172
384
  };
173
385
  type RuntimeBridgeManifestEntry = {
174
386
  channel: string;
@@ -190,11 +402,15 @@ declare global {
190
402
  interface Window {
191
403
  __WOOD_BROWSER_APP__?: unknown;
192
404
  __WOOD_BROWSER_DEFAULT_ROUTE__?: string;
405
+ __WOOD_BROWSER_NAV__?: unknown;
193
406
  __WOOD_BROWSER_BRIDGE_MANIFEST__?: RuntimeBridgeManifestEntry[];
194
407
  __woodBrowserInvoke?: (channel: string, input: unknown) => Promise<unknown>;
408
+ __woodBrowserSubscribe?: (channel: string) => void;
195
409
  __woodBrowserEmit?: (channel: string, payload: unknown) => void;
196
- __woodBrowserTrace?: (payload: unknown) => void;
410
+ __woodBrowserTrace?: (payload: unknown) => Promise<unknown> | void;
411
+ __woodBrowserTracePending?: Set<Promise<unknown>>;
412
+ __woodBrowserTraceFlush?: () => Promise<void>;
197
413
  }
198
414
  }
199
415
 
200
- export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodViewport, browser, createBrowserBridgeFixture };
416
+ export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browser, createBrowserBridgeFixture };
@@ -1,5 +1,6 @@
1
- import { TraceLevel } from '../tracing/trace_hub.js';
1
+ import { c as TraceLevel } from '../trace_hub-Cob54qRx.js';
2
2
  import { TraceRecorder } from '../trace-testing/trace_recorder.js';
3
+ import { WoodRpcTransport } from '../transport/wood_rpc_transport.js';
3
4
  import 'rxjs';
4
5
  import '@noego/trace/testing';
5
6
  import '../tracing/trace_provider.js';
@@ -31,6 +32,30 @@ interface WoodBridgeFixtureInput {
31
32
  operations?: Record<string, OperationHandler>;
32
33
  events?: Record<string, unknown | unknown[]>;
33
34
  record?: boolean;
35
+ /**
36
+ * Real-backend mode. When supplied, page `invoke` is routed through this
37
+ * `WoodRpcTransport` (dispatching through the REAL WoodRouter — full schema
38
+ * validation) instead of the fixture `operations` Map, and page `bridge.on(...)`
39
+ * subscriptions are relayed to `transport.subscribe(...)` so main-process push
40
+ * events reach page subscribers. Fixture `operations` still act as a fallback
41
+ * for any channel not registered on the transport, so hybrid fixtures work.
42
+ * Omit for the legacy fixture-only bridge (spec §4: labeled visual tests).
43
+ */
44
+ transport?: WoodRpcTransport;
45
+ /**
46
+ * Full RPC manifest to build the page bridge tree from. Required in transport
47
+ * mode so every real channel is exposed on the bridge (in fixture mode the
48
+ * manifest is derived from `operations`). Supply the app's complete operations
49
+ * manifest (channel/controller/action/path/hasInput per operation).
50
+ */
51
+ rpcManifest?: WoodRpcManifestEntryInput[];
52
+ }
53
+ interface WoodRpcManifestEntryInput {
54
+ channel: string;
55
+ controller: string;
56
+ action: string;
57
+ path: string[];
58
+ hasInput: boolean;
34
59
  }
35
60
  interface WoodBrowserPageContext {
36
61
  page: any;
@@ -120,6 +145,43 @@ interface MountedWoodNavigation {
120
145
  go(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<void>;
121
146
  replace(page: string, params?: Record<string, string>, query?: Record<string, string>): Promise<void>;
122
147
  }
148
+ /** Snapshot of one element's viewport-space box, tagged with its source selector. */
149
+ interface WoodRectSnapshot {
150
+ selector: string;
151
+ top: number;
152
+ left: number;
153
+ right: number;
154
+ bottom: number;
155
+ width: number;
156
+ height: number;
157
+ }
158
+ /** Axis along which edge-to-edge spacing is measured. */
159
+ type WoodSpacingAxis = 'horizontal' | 'vertical';
160
+ /**
161
+ * Description of the top-most element painted at a hit-test point, plus its
162
+ * relationship to the queried target. `document.elementFromPoint` honours
163
+ * `pointer-events`, so an element with `pointer-events: none` is transparent to
164
+ * the hit test even when it is painted on top (see `hitTest`/`unoccluded`).
165
+ */
166
+ interface WoodHitTestResult {
167
+ /** False when no element is painted at the point (e.g. outside the document). */
168
+ found: boolean;
169
+ tag: string | null;
170
+ id: string | null;
171
+ classes: string[];
172
+ /** The hit element is exactly the target. */
173
+ isTarget: boolean;
174
+ /** The hit element is a descendant of the target (target paints through it). */
175
+ isDescendantOfTarget: boolean;
176
+ /** The hit element is an ancestor of the target. */
177
+ isAncestorOfTarget: boolean;
178
+ point: {
179
+ x: number;
180
+ y: number;
181
+ };
182
+ /** Human-readable one-line description of the hit element. */
183
+ description: string;
184
+ }
123
185
  interface WoodBrowserExpect {
124
186
  visible(selector: string): Promise<void>;
125
187
  hidden(selector: string): Promise<void>;
@@ -128,6 +190,12 @@ interface WoodBrowserExpect {
128
190
  }): Promise<void>;
129
191
  notClipped(selector: string): Promise<void>;
130
192
  notOverlapping(firstSelector: string, secondSelector: string): Promise<void>;
193
+ /**
194
+ * @deprecated Compares numeric computed `z-index` only, so it lies whenever
195
+ * stacking contexts, DOM paint order (auto/equal z-index), or
196
+ * transform/opacity contexts decide the real front-to-back order. Prefer
197
+ * `inFrontOf`, which hit-tests the actual painted pixel.
198
+ */
131
199
  zIndexAbove(topSelector: string, lowerSelector: string): Promise<void>;
132
200
  stableAfterAnimation(selector?: string, options?: {
133
201
  timeoutMs?: number;
@@ -138,6 +206,142 @@ interface WoodBrowserExpect {
138
206
  mask?: string[];
139
207
  threshold?: number;
140
208
  }): Promise<void>;
209
+ /** Batch `getBoundingClientRect` reader — one snapshot per selector, in order. */
210
+ rects(selectors: string[]): Promise<WoodRectSnapshot[]>;
211
+ /**
212
+ * Hit-test at a point (default the target's center) and describe the top-most
213
+ * painted element and how it relates to the target.
214
+ */
215
+ hitTest(selector: string, point?: {
216
+ x: number;
217
+ y: number;
218
+ } | 'center'): Promise<WoodHitTestResult>;
219
+ /** Assert two elements share the given edge coordinate within `tolerance` px. */
220
+ sharedEdge(a: string, b: string, edge: 'left' | 'right' | 'top' | 'bottom', options?: {
221
+ tolerance?: number;
222
+ }): Promise<void>;
223
+ /** Assert two elements share a center axis (centerX/centerY) within `tolerance` px. */
224
+ sharedAxis(a: string, b: string, axis: 'centerX' | 'centerY', options?: {
225
+ tolerance?: number;
226
+ }): Promise<void>;
227
+ /**
228
+ * Point-specific "does a click land here" check. Asserts the target receives
229
+ * the hit test at `point` (default center): the hit-test element is the target
230
+ * or a descendant of it. Ancestors are accepted only when the target is
231
+ * empty/zero-content at that point and the ancestor is what paints there — but a
232
+ * foreign overlay (not in the target's ancestry) fails. Honours
233
+ * `pointer-events: none`: an overlay so styled does not occlude, because it does
234
+ * not intercept the click.
235
+ *
236
+ * This is NOT the general visibility check — a `pointer-events: none` overlay
237
+ * that visually hides the element still passes here. For "is any foreign layer
238
+ * painted on top of the element", prefer `notCovered` (paint-order coverage).
239
+ */
240
+ unoccluded(selector: string, options?: {
241
+ point?: {
242
+ x: number;
243
+ y: number;
244
+ } | 'center';
245
+ }): Promise<void>;
246
+ /**
247
+ * Honest front/behind primitive. DEFAULT (recommended): a whole-element
248
+ * paint-order comparison backed by Chromium's real paint tree (CDP
249
+ * `DOMSnapshot.captureSnapshot` with paint order). Passes when `topSelector`
250
+ * has a strictly greater global paint-order index than `lowerSelector` — i.e.
251
+ * it is painted later, so it is on top. This answers "which layer paints on
252
+ * top" for the whole element, independent of overlap or `pointer-events`, and
253
+ * is Chromium-only.
254
+ *
255
+ * Opt-in point form `inFrontOf(top, lower, { at })`: hit-tests a single painted
256
+ * pixel (`at` defaults to the center of the two rects' intersection) and passes
257
+ * when the painted element there is the top element (or its descendant) and is
258
+ * NOT the lower element. Use this only for the point-specific question ("is the
259
+ * top element the one painted AT THIS SPOT"); it honours `pointer-events` and
260
+ * requires the rects to intersect when no explicit `at` is given.
261
+ *
262
+ * If an element has multiple paint fragments, its maximum paint-order index is
263
+ * used. When the rects do not overlap the paint-order verdict is still valid
264
+ * (paint order is global), but the failure message notes there is no shared
265
+ * pixel where front/behind is visually observable.
266
+ */
267
+ inFrontOf(topSelector: string, lowerSelector: string, options?: {
268
+ at?: {
269
+ x: number;
270
+ y: number;
271
+ };
272
+ }): Promise<void>;
273
+ /**
274
+ * Whole-element painted-coverage check: assert that NO foreign element paints
275
+ * on top of the target anywhere its rect overlaps. Backed by Chromium's real
276
+ * paint tree (CDP `DOMSnapshot.captureSnapshot` with paint order + computed
277
+ * styles + DOM rects), it is the honest, INSPECTABLE replacement for the old
278
+ * IntersectionObserver-v2 `fullyVisible` matcher, whose `isVisible` field is a
279
+ * black box that silently reports not-visible for any element painted on top —
280
+ * even a fully transparent one (see "why not IntersectionObserver v2" in the
281
+ * geometry docs).
282
+ *
283
+ * Algorithm (Chromium-only):
284
+ * 1. Find every laid-out element whose layout rect intersects the target's rect.
285
+ * 2. Exclude the target itself and its ancestors/descendants (they are not
286
+ * foreign coverage — the target paints through its own subtree).
287
+ * 3. Exclude candidates that paint nothing visible in the overlap, per an
288
+ * explicit transparent-wrapper heuristic: computed `opacity: 0` OR
289
+ * `visibility: hidden`, OR (no background-color alpha AND
290
+ * `background-image: none` AND all four border widths `0px` AND no own text
291
+ * or replaced/image content). This is the ONE rule that lets a transparent
292
+ * positioning wrapper spanning the target pass; it is intentionally
293
+ * documented and testable, unlike IOv2's opaque verdict. (`box-shadow`,
294
+ * `outline` and other purely decorative paints are NOT counted as coverage.)
295
+ * 4. Fail if any remaining foreign candidate has a strictly HIGHER max paint
296
+ * order than the target (painted later == on top). The failure names the
297
+ * covering element (tag/id/classes), its rect, and both paint indices.
298
+ *
299
+ * Note this tracks PAINTED coverage, so an opaque `pointer-events: none` overlay
300
+ * DOES fail `notCovered` (it paints pixels on top) while `unoccluded` still
301
+ * passes (it is transparent to hit testing) — the complementary pair of
302
+ * "painted coverage" vs "click reachability".
303
+ */
304
+ notCovered(selector: string): Promise<void>;
305
+ /**
306
+ * Assert the edge-to-edge gap between two elements along `axis` satisfies a
307
+ * numeric constraint. The order is derived from the rects, not the argument
308
+ * order: the box with the smaller leading edge (left for `'horizontal'`, top
309
+ * for `'vertical'`) is treated as the earlier box and the gap is
310
+ * `later.leading - earlier.trailing` (horizontal: `later.left - earlier.right`;
311
+ * vertical: `later.top - earlier.bottom`).
312
+ *
313
+ * At least one of `equals` / `min` / `max` is required. `equals` passes when
314
+ * `|gap - equals| <= tolerance`; `min`/`max` are inclusive within `tolerance`.
315
+ * A negative gap means the border boxes OVERLAP along the axis; that fails
316
+ * clearly UNLESS the constraint explicitly allows negatives (a negative
317
+ * `equals` or `min`). The failure message states both rects, the computed gap,
318
+ * and the expected constraint.
319
+ *
320
+ * BORDER-BOX CAVEAT: rects are border boxes (`getBoundingClientRect`), so the
321
+ * gap is the distance between the elements' outer borders and EXCLUDES any
322
+ * padding inside either element. To assert visual whitespace between inner
323
+ * content, target the inner content elements instead of their padded wrappers.
324
+ */
325
+ gapBetween(a: string, b: string, axis: WoodSpacingAxis, options: {
326
+ equals?: number;
327
+ min?: number;
328
+ max?: number;
329
+ tolerance?: number;
330
+ }): Promise<void>;
331
+ /**
332
+ * Assert three or more elements are evenly spaced along `axis`. Requires at
333
+ * least 3 selectors; the elements are sorted by their leading edge along the
334
+ * axis, adjacent edge-to-edge gaps are computed, and the assertion passes when
335
+ * `max gap - min gap <= tolerance`. The failure message lists every adjacent
336
+ * gap in sorted order and names the widest and narrowest (offending) pairs.
337
+ *
338
+ * BORDER-BOX CAVEAT: gaps are measured between border boxes
339
+ * (`getBoundingClientRect`), so they exclude padding inside the elements. Tests
340
+ * that care about visual whitespace should target inner content elements.
341
+ */
342
+ evenlySpaced(selectors: string[], axis: WoodSpacingAxis, options?: {
343
+ tolerance?: number;
344
+ }): Promise<void>;
141
345
  }
142
346
  interface MountedWoodSurface {
143
347
  page: any;
@@ -161,6 +365,12 @@ interface WoodBrowserHarness {
161
365
  mountRoute(routeKey: string, options?: MountRouteOptions): Promise<MountedWoodRoute>;
162
366
  mountComponent(component: string, options?: MountComponentOptions): Promise<MountedWoodComponent>;
163
367
  mountTree(tree: MountTreeOptions): Promise<MountedWoodTree>;
368
+ /**
369
+ * Drain in-flight renderer `__trace` exposeBinding round-trips so the caller's
370
+ * trace-flush barrier (e.g. `TraceCommitWriter.requestRendererFlush`) has a
371
+ * definite quiescent point. Resolves immediately when no page is mounted.
372
+ */
373
+ flushRendererTraces(): Promise<void>;
164
374
  destroy(): Promise<void>;
165
375
  }
166
376
  type BridgeState = {
@@ -169,6 +379,8 @@ type BridgeState = {
169
379
  calls: WoodBridgeRecordedCall[];
170
380
  emittedEvents: WoodBridgeRecordedEvent[];
171
381
  record: boolean;
382
+ transport?: WoodRpcTransport;
383
+ rpcManifest?: WoodRpcManifestEntryInput[];
172
384
  };
173
385
  type RuntimeBridgeManifestEntry = {
174
386
  channel: string;
@@ -190,11 +402,15 @@ declare global {
190
402
  interface Window {
191
403
  __WOOD_BROWSER_APP__?: unknown;
192
404
  __WOOD_BROWSER_DEFAULT_ROUTE__?: string;
405
+ __WOOD_BROWSER_NAV__?: unknown;
193
406
  __WOOD_BROWSER_BRIDGE_MANIFEST__?: RuntimeBridgeManifestEntry[];
194
407
  __woodBrowserInvoke?: (channel: string, input: unknown) => Promise<unknown>;
408
+ __woodBrowserSubscribe?: (channel: string) => void;
195
409
  __woodBrowserEmit?: (channel: string, payload: unknown) => void;
196
- __woodBrowserTrace?: (payload: unknown) => void;
410
+ __woodBrowserTrace?: (payload: unknown) => Promise<unknown> | void;
411
+ __woodBrowserTracePending?: Set<Promise<unknown>>;
412
+ __woodBrowserTraceFlush?: () => Promise<void>;
197
413
  }
198
414
  }
199
415
 
200
- export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodViewport, browser, createBrowserBridgeFixture };
416
+ export { type MountComponentOptions, type MountRouteOptions, type MountTreeOptions, type MountedWoodBridge, type MountedWoodComponent, type MountedWoodNavigation, type MountedWoodRoute, type MountedWoodSurface, type MountedWoodTree, type WoodBridgeFixtureContext, type WoodBridgeFixtureInput, type WoodBridgeRecordedCall, type WoodBridgeRecordedEvent, type WoodBrowserExpect, type WoodBrowserHarness, type WoodBrowserHarnessConfig, type WoodBrowserPageContext, type WoodBrowserTraceConfig, type WoodHitTestResult, type WoodRectSnapshot, type WoodRpcManifestEntryInput, type WoodSpacingAxis, type WoodViewport, browser, createBrowserBridgeFixture };