@bespokeagentics/microdots-host 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,810 @@
1
+ import { Option } from 'effect'
2
+ import { beforeEach, describe, expect, test, vi } from 'vitest'
3
+
4
+ import type { Wire } from './wire.ts'
5
+ import {
6
+ type WireEngine,
7
+ type WireEngineOptions,
8
+ type WireTrace,
9
+ applyTransform,
10
+ attachWireEngine,
11
+ planWires,
12
+ } from './wireEngine.ts'
13
+
14
+ /* ============================================================
15
+ The recorded demo-host wires — w1/w2/w3 exactly as
16
+ `apps/host/src/entry.ts` hand-wrote them before Phase 4.
17
+ ============================================================ */
18
+
19
+ const w1: Wire = {
20
+ id: 'w1',
21
+ from: 'price-ticker',
22
+ event: 'quote-changed',
23
+ field: 'symbol',
24
+ fieldType: 'string',
25
+ to: 'fleet-health',
26
+ input: 'region',
27
+ inputType: 'string',
28
+ transform: {
29
+ _tag: 'lookup',
30
+ rows: { FOLD: 'us-east', EFCT: 'eu-west', ELMX: 'ap-south-1' },
31
+ fallback: 'us-east',
32
+ },
33
+ envs: ['dev', 'preview', 'prod'],
34
+ plain: 'When the quote changes, point the fleet at the symbol’s region.',
35
+ }
36
+
37
+ const w2: Wire = {
38
+ id: 'w2',
39
+ from: 'bespoke-contact-form',
40
+ event: 'submission-created',
41
+ field: 'id',
42
+ fieldType: 'string',
43
+ to: 'bespoke-contact-dashboard',
44
+ input: 'refresh-token',
45
+ inputType: 'string',
46
+ transform: { _tag: 'direct' },
47
+ envs: ['dev', 'preview', 'prod'],
48
+ plain: 'When a submission is stored, nudge the dashboard to reload.',
49
+ }
50
+
51
+ const w3: Wire = {
52
+ id: 'w3',
53
+ from: 'bespoke-contact-dashboard',
54
+ event: 'definition-changed',
55
+ field: 'version',
56
+ fieldType: 'number',
57
+ to: 'bespoke-contact-form',
58
+ input: 'definition-token',
59
+ inputType: 'number',
60
+ transform: { _tag: 'direct' },
61
+ envs: ['dev', 'preview', 'prod'],
62
+ plain: 'When the definition changes, nudge the form to refetch it.',
63
+ }
64
+
65
+ const DEMO_WIRES: ReadonlyArray<Wire> = [w1, w2, w3]
66
+
67
+ /* ============================================================
68
+ The `host-changed` collision — pw12 and pg6/pg7, copied field-for-field
69
+ from `apps/platform/host-topology.json`. TWO dots emit `host-changed`
70
+ (`wiring-canvas` and `pages-rail`, `microdots/wiring/src/surface.ts:30` and
71
+ `microdots/pages/src/surface.ts:49`) with an identically shaped `{hostId}`
72
+ payload, and three panes across two screens expose a `host-id` attribute.
73
+ Both screens stay mounted — `activate` only toggles `section.hidden` — so
74
+ a name-only match crossed them.
75
+ ============================================================ */
76
+
77
+ const pw12: Wire = {
78
+ id: 'pw12',
79
+ from: 'wiring-canvas',
80
+ event: 'host-changed',
81
+ field: 'hostId',
82
+ fieldType: 'string',
83
+ to: 'wiring-table',
84
+ input: 'host-id',
85
+ inputType: 'string',
86
+ transform: { _tag: 'direct' },
87
+ envs: ['dev', 'preview', 'prod'],
88
+ plain: 'When the canvas switches host, list that host’s wires.',
89
+ }
90
+
91
+ const pg6: Wire = {
92
+ id: 'pg6',
93
+ from: 'pages-rail',
94
+ event: 'host-changed',
95
+ field: 'hostId',
96
+ fieldType: 'string',
97
+ to: 'pages-canvas',
98
+ input: 'host-id',
99
+ inputType: 'string',
100
+ transform: { _tag: 'direct' },
101
+ envs: ['dev', 'preview', 'prod'],
102
+ plain: 'When the rail switches host, draw that host’s routes.',
103
+ }
104
+
105
+ const pg7: Wire = { ...pg6, id: 'pg7', to: 'pages-inspector' }
106
+
107
+ /** A `json` wire — the one declared fieldType the demo records never use. */
108
+ const wJson: Wire = {
109
+ id: 'w-json',
110
+ from: 'bespoke-contact-dashboard',
111
+ event: 'snapshot-changed',
112
+ field: 'snapshot',
113
+ fieldType: 'json',
114
+ to: 'fleet-health',
115
+ input: 'snapshot',
116
+ inputType: 'json',
117
+ transform: { _tag: 'direct' },
118
+ envs: ['dev', 'preview', 'prod'],
119
+ plain: 'When the snapshot changes, hand the fleet the whole document.',
120
+ }
121
+
122
+ const enginesToDetach: Array<{ detach: () => void }> = []
123
+
124
+ /** Every DOM test runs the shells' environment, `dev`. */
125
+ const attach = (options: Omit<WireEngineOptions, 'env'>): WireEngine => {
126
+ const engine = attachWireEngine({ ...options, env: 'dev' })
127
+ enginesToDetach.push(engine)
128
+ return engine
129
+ }
130
+
131
+ const mount = (tag: string): HTMLElement => {
132
+ const element = document.createElement(tag)
133
+ document.body.appendChild(element)
134
+ return element
135
+ }
136
+
137
+ /**
138
+ * Dispatches the way a MicroDot does: ON the emitting element, bubbling up to
139
+ * the engine's `document` listener, so `event.target` names the source exactly
140
+ * as `defineMicroDot` produces it
141
+ * (`packages/microdots-element/src/defineMicroDot.ts:281-283`). A wire's
142
+ * `from` is matched against that tag, so every test that expects a write must
143
+ * emit from the tag its wire names.
144
+ */
145
+ const dispatch = (sourceTag: string, name: string, detail: unknown): void => {
146
+ const existing = document.querySelector(sourceTag)
147
+ const source = existing ?? mount(sourceTag)
148
+ source.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }))
149
+ }
150
+
151
+ /**
152
+ * An event with NO identifiable emitter: dispatched straight at `document`,
153
+ * whose `target` is a Document and not an Element. Nothing in this repo emits
154
+ * this way — it stands for the shapes that reach a `document` listener anyway
155
+ * (a foreign page's synthetic event, a retargeted non-element node).
156
+ */
157
+ const dispatchAnonymously = (name: string, detail: unknown): void => {
158
+ document.dispatchEvent(new CustomEvent(name, { detail }))
159
+ }
160
+
161
+ /** The wire ids a trace list carries, in order — watch taps excluded. */
162
+ const firedWireIds = (
163
+ traces: ReadonlyArray<WireTrace>,
164
+ ): ReadonlyArray<string> =>
165
+ traces.flatMap(trace => (trace._tag === 'wire' ? [trace.wireId] : []))
166
+
167
+ /** The payloads the watch taps logged, in order — wire traces excluded. */
168
+ const tappedPayloads = (
169
+ traces: ReadonlyArray<WireTrace>,
170
+ ): ReadonlyArray<unknown> =>
171
+ traces.flatMap(trace => (trace._tag === 'watch' ? [trace.payload] : []))
172
+
173
+ beforeEach(() => {
174
+ document.body.replaceChildren()
175
+ enginesToDetach.splice(0).forEach(engine => {
176
+ engine.detach()
177
+ })
178
+ })
179
+
180
+ // The failure mode this whole file guards: the hand-written listener blocks
181
+ // in apps/host/src/entry.ts are being REPLACED by these records, and any
182
+ // drift between what they did and what the engine does is a silently broken
183
+ // demo — the events still fire, the log still scrolls, and nothing arrives.
184
+ describe('attachWireEngine', () => {
185
+ test('routes EFCT to eu-west through the w1 lookup rows', () => {
186
+ const fleet = mount('fleet-health')
187
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
188
+
189
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
190
+
191
+ expect(fleet.getAttribute('region')).toBe('eu-west')
192
+ })
193
+
194
+ test('falls back to us-east for a symbol outside the lookup rows', () => {
195
+ const fleet = mount('fleet-health')
196
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
197
+
198
+ dispatch('price-ticker', 'quote-changed', { symbol: 'ZZZZ', cents: 1 })
199
+
200
+ expect(fleet.getAttribute('region')).toBe('us-east')
201
+ })
202
+
203
+ test('a disabled wire writes nothing and traces the skip', () => {
204
+ const fleet = mount('fleet-health')
205
+ const traces: Array<WireTrace> = []
206
+ const engine = attach({
207
+ topology: { wires: DEMO_WIRES, watch: [] },
208
+ onTrace: trace => traces.push(trace),
209
+ })
210
+
211
+ engine.setEnabled('w1', false)
212
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
213
+
214
+ expect(fleet.getAttribute('region')).toBeNull()
215
+ expect(traces).toEqual([
216
+ {
217
+ _tag: 'wire',
218
+ event: 'quote-changed',
219
+ wireId: 'w1',
220
+ delivery: { _tag: 'skipped', reason: 'disabled' },
221
+ },
222
+ ])
223
+
224
+ // The kill switch is not a detach: flipping it back restores the wire.
225
+ engine.setEnabled('w1', true)
226
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
227
+ expect(fleet.getAttribute('region')).toBe('eu-west')
228
+ })
229
+
230
+ test('encodes the numeric version 7 as the attribute string "7"', () => {
231
+ const form = mount('bespoke-contact-form')
232
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
233
+
234
+ dispatch('bespoke-contact-dashboard', 'definition-changed', { version: 7 })
235
+
236
+ expect(form.getAttribute('definition-token')).toBe('7')
237
+ })
238
+
239
+ /**
240
+ * `setAttribute` fires `attributeChangedCallback` even when the value is
241
+ * unchanged, so two identical events must reach the element as ONE write —
242
+ * the guard lives in `setMicroDotAttribute` and the engine must go through
243
+ * it (apps/host/src/loader.test.ts pins the same guard directly).
244
+ */
245
+ test('two identical events produce exactly one setAttribute call', () => {
246
+ const form = mount('bespoke-contact-form')
247
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
248
+
249
+ dispatch('bespoke-contact-dashboard', 'definition-changed', { version: 7 })
250
+ expect(form.getAttribute('definition-token')).toBe('7')
251
+
252
+ const spy = vi.spyOn(form, 'setAttribute')
253
+ dispatch('bespoke-contact-dashboard', 'definition-changed', { version: 7 })
254
+
255
+ expect(spy).not.toHaveBeenCalled()
256
+ spy.mockRestore()
257
+ })
258
+
259
+ test('falls back for a lookup key inherited from Object.prototype', () => {
260
+ // 'constructor' is not in w1's rows, but a bare `rows[encoded]` index
261
+ // would find Object.prototype.constructor and write
262
+ // 'function Object() { [native code] }' where the fallback belongs.
263
+ const fleet = mount('fleet-health')
264
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
265
+
266
+ dispatch('price-ticker', 'quote-changed', {
267
+ symbol: 'constructor',
268
+ cents: 1,
269
+ })
270
+
271
+ expect(fleet.getAttribute('region')).toBe('us-east')
272
+ })
273
+
274
+ test('a payload that is not a record skips as no-field', () => {
275
+ const form = mount('bespoke-contact-form')
276
+ const traces: Array<WireTrace> = []
277
+ attach({
278
+ topology: { wires: DEMO_WIRES, watch: [] },
279
+ onTrace: trace => traces.push(trace),
280
+ })
281
+
282
+ dispatch('bespoke-contact-dashboard', 'definition-changed', 'seven')
283
+
284
+ expect(form.getAttribute('definition-token')).toBeNull()
285
+ expect(traces).toEqual([
286
+ {
287
+ _tag: 'wire',
288
+ event: 'definition-changed',
289
+ wireId: 'w3',
290
+ delivery: { _tag: 'skipped', reason: 'no-field' },
291
+ },
292
+ ])
293
+ })
294
+
295
+ test("a payload missing the wire's field skips as no-field", () => {
296
+ const form = mount('bespoke-contact-form')
297
+ const traces: Array<WireTrace> = []
298
+ attach({
299
+ topology: { wires: DEMO_WIRES, watch: [] },
300
+ onTrace: trace => traces.push(trace),
301
+ })
302
+
303
+ dispatch('bespoke-contact-dashboard', 'definition-changed', { revision: 7 })
304
+
305
+ expect(form.getAttribute('definition-token')).toBeNull()
306
+ expect(traces).toEqual([
307
+ {
308
+ _tag: 'wire',
309
+ event: 'definition-changed',
310
+ wireId: 'w3',
311
+ delivery: { _tag: 'skipped', reason: 'no-field' },
312
+ },
313
+ ])
314
+ })
315
+
316
+ test('a value that fails its declared fieldType decode skips as no-field', () => {
317
+ const form = mount('bespoke-contact-form')
318
+ const traces: Array<WireTrace> = []
319
+ attach({
320
+ topology: { wires: DEMO_WIRES, watch: [] },
321
+ onTrace: trace => traces.push(trace),
322
+ })
323
+
324
+ dispatch('bespoke-contact-dashboard', 'definition-changed', {
325
+ version: 'seven',
326
+ })
327
+
328
+ expect(form.getAttribute('definition-token')).toBeNull()
329
+ expect(traces).toEqual([
330
+ {
331
+ _tag: 'wire',
332
+ event: 'definition-changed',
333
+ wireId: 'w3',
334
+ delivery: { _tag: 'skipped', reason: 'no-field' },
335
+ },
336
+ ])
337
+ })
338
+
339
+ test('a json field writes its JSON.stringify encoding', () => {
340
+ const fleet = mount('fleet-health')
341
+ attach({ topology: { wires: [wJson], watch: [] } })
342
+
343
+ dispatch('bespoke-contact-dashboard', 'snapshot-changed', {
344
+ snapshot: { id: 7, tags: ['a'] },
345
+ })
346
+
347
+ expect(fleet.getAttribute('snapshot')).toBe('{"id":7,"tags":["a"]}')
348
+ })
349
+
350
+ test('an unencodable json value skips its wire without killing siblings on the same event', () => {
351
+ // A postMessage structured clone legally carries cycles, and
352
+ // JSON.stringify THROWS on them — an uncaught throw inside the listener
353
+ // would drop the sibling's write and trace too.
354
+ const fleet = mount('fleet-health')
355
+ const sibling: Wire = {
356
+ ...wJson,
357
+ id: 'w-sibling',
358
+ field: 'label',
359
+ fieldType: 'string',
360
+ input: 'label',
361
+ inputType: 'string',
362
+ }
363
+ const traces: Array<WireTrace> = []
364
+ attach({
365
+ topology: { wires: [wJson, sibling], watch: [] },
366
+ onTrace: trace => traces.push(trace),
367
+ })
368
+
369
+ const cyclic: Record<string, unknown> = {}
370
+ cyclic['self'] = cyclic
371
+ dispatch('bespoke-contact-dashboard', 'snapshot-changed', {
372
+ snapshot: cyclic,
373
+ label: 'ok',
374
+ })
375
+
376
+ expect(fleet.getAttribute('snapshot')).toBeNull()
377
+ expect(fleet.getAttribute('label')).toBe('ok')
378
+ expect(traces).toEqual([
379
+ {
380
+ _tag: 'wire',
381
+ event: 'snapshot-changed',
382
+ wireId: 'w-json',
383
+ delivery: { _tag: 'skipped', reason: 'no-field' },
384
+ },
385
+ {
386
+ _tag: 'wire',
387
+ event: 'snapshot-changed',
388
+ wireId: 'w-sibling',
389
+ delivery: {
390
+ _tag: 'written',
391
+ write: { toTag: 'fleet-health', input: 'label', value: 'ok' },
392
+ },
393
+ },
394
+ ])
395
+ })
396
+
397
+ test('every mounted instance of the target tag receives the write', () => {
398
+ // A topology may place one tag in two slots; "the first in document
399
+ // order" is not what the record says, and 'written' must mean all of them.
400
+ const first = mount('fleet-health')
401
+ const second = mount('fleet-health')
402
+ attach({ topology: { wires: DEMO_WIRES, watch: [] } })
403
+
404
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
405
+
406
+ expect(first.getAttribute('region')).toBe('eu-west')
407
+ expect(second.getAttribute('region')).toBe('eu-west')
408
+ })
409
+
410
+ test('a wire whose `to` is not a valid selector is target-unmounted, and later wires still trace', () => {
411
+ // The schema admits any string as `to`; querySelectorAll('') throws, and
412
+ // an uncaught throw would abort trace emission for every remaining wire.
413
+ const fleet = mount('fleet-health')
414
+ const malformed: Wire = { ...w1, id: 'w-bad', to: '' }
415
+ const traces: Array<WireTrace> = []
416
+ attach({
417
+ topology: { wires: [malformed, w1], watch: [] },
418
+ onTrace: trace => traces.push(trace),
419
+ })
420
+
421
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
422
+
423
+ expect(fleet.getAttribute('region')).toBe('eu-west')
424
+ expect(traces).toEqual([
425
+ {
426
+ _tag: 'wire',
427
+ event: 'quote-changed',
428
+ wireId: 'w-bad',
429
+ delivery: {
430
+ _tag: 'target-unmounted',
431
+ write: { toTag: '', input: 'region', value: 'eu-west' },
432
+ },
433
+ },
434
+ {
435
+ _tag: 'wire',
436
+ event: 'quote-changed',
437
+ wireId: 'w1',
438
+ delivery: {
439
+ _tag: 'written',
440
+ write: { toTag: 'fleet-health', input: 'region', value: 'eu-west' },
441
+ },
442
+ },
443
+ ])
444
+ })
445
+
446
+ test('a wire whose `input` is not a valid attribute name fails its write, and siblings on the same event still land', () => {
447
+ // The schema admits any string as `input`; setAttribute('refresh token')
448
+ // throws InvalidCharacterError, and an uncaught throw out of the document
449
+ // listener would drop the write AND trace of every remaining wire.
450
+ const fleet = mount('fleet-health')
451
+ const hostile: Wire = { ...w1, id: 'w-hostile', input: 'refresh token' }
452
+ const traces: Array<WireTrace> = []
453
+ attach({
454
+ topology: { wires: [hostile, w1], watch: [] },
455
+ onTrace: trace => traces.push(trace),
456
+ })
457
+
458
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
459
+
460
+ expect(fleet.getAttribute('region')).toBe('eu-west')
461
+ expect(traces).toEqual([
462
+ {
463
+ _tag: 'wire',
464
+ event: 'quote-changed',
465
+ wireId: 'w-hostile',
466
+ delivery: {
467
+ _tag: 'write-failed',
468
+ write: {
469
+ toTag: 'fleet-health',
470
+ input: 'refresh token',
471
+ value: 'eu-west',
472
+ },
473
+ },
474
+ },
475
+ {
476
+ _tag: 'wire',
477
+ event: 'quote-changed',
478
+ wireId: 'w1',
479
+ delivery: {
480
+ _tag: 'written',
481
+ write: { toTag: 'fleet-health', input: 'region', value: 'eu-west' },
482
+ },
483
+ },
484
+ ])
485
+ })
486
+
487
+ test('the watch tap traces before the wire delivery when an event is both watched and wired', () => {
488
+ // Today's hosts log before they act; a wire's write must never become a
489
+ // precondition of the log line.
490
+ mount('fleet-health')
491
+ const traces: Array<WireTrace> = []
492
+ attach({
493
+ topology: { wires: DEMO_WIRES, watch: [{ event: 'quote-changed' }] },
494
+ onTrace: trace => traces.push(trace),
495
+ })
496
+
497
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
498
+
499
+ expect(traces.map(trace => trace._tag)).toEqual(['watch', 'wire'])
500
+ })
501
+
502
+ test('a held condition writes nothing and the trace names the wanted value', () => {
503
+ const fleet = mount('fleet-health')
504
+ const conditioned: Wire = {
505
+ ...w1,
506
+ id: 'w-linked',
507
+ transform: {
508
+ _tag: 'condition',
509
+ field: 'linked',
510
+ op: 'is',
511
+ value: 'true',
512
+ },
513
+ }
514
+ const traces: Array<WireTrace> = []
515
+ attach({
516
+ topology: { wires: [conditioned], watch: [] },
517
+ onTrace: trace => traces.push(trace),
518
+ })
519
+
520
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', linked: false })
521
+
522
+ expect(fleet.getAttribute('region')).toBeNull()
523
+ expect(traces).toEqual([
524
+ {
525
+ _tag: 'wire',
526
+ event: 'quote-changed',
527
+ wireId: 'w-linked',
528
+ delivery: { _tag: 'held', wanted: 'true' },
529
+ },
530
+ ])
531
+
532
+ // The condition holding is the whole difference: same event, one write.
533
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', linked: true })
534
+ expect(fleet.getAttribute('region')).toBe('EFCT')
535
+ })
536
+
537
+ test('a watch tap traces the event name and raw payload without writing', () => {
538
+ const traces: Array<WireTrace> = []
539
+ attach({
540
+ topology: { wires: [], watch: [{ event: 'progress-changed' }] },
541
+ onTrace: trace => traces.push(trace),
542
+ })
543
+
544
+ dispatch('cumulative-functional-spec-generator-panel', 'progress-changed', {
545
+ current: 3,
546
+ total: 9,
547
+ stage: 'drafting',
548
+ })
549
+
550
+ expect(traces).toEqual([
551
+ {
552
+ _tag: 'watch',
553
+ event: 'progress-changed',
554
+ payload: { current: 3, total: 9, stage: 'drafting' },
555
+ },
556
+ ])
557
+ })
558
+
559
+ test('an unmounted target is a skipped write, still traced — the poll floor covers it', () => {
560
+ const traces: Array<WireTrace> = []
561
+ attach({
562
+ topology: { wires: DEMO_WIRES, watch: [] },
563
+ onTrace: trace => traces.push(trace),
564
+ })
565
+
566
+ dispatch('bespoke-contact-form', 'submission-created', { id: 'sub-42' })
567
+
568
+ expect(traces).toEqual([
569
+ {
570
+ _tag: 'wire',
571
+ event: 'submission-created',
572
+ wireId: 'w2',
573
+ delivery: {
574
+ _tag: 'target-unmounted',
575
+ write: {
576
+ toTag: 'bespoke-contact-dashboard',
577
+ input: 'refresh-token',
578
+ value: 'sub-42',
579
+ },
580
+ },
581
+ },
582
+ ])
583
+ })
584
+
585
+ /**
586
+ * The Phase 5 defect, pinned. Under a name-only match the Pages rail's host
587
+ * picker ALSO retargeted the hidden Wiring screen through pw12, and the
588
+ * Wiring canvas's picker drove the Pages panes through pg6/pg7 — panes
589
+ * resolving a host the rail's picker was not showing, after which a route
590
+ * click sent paths from host A into panes rendering host B. All four wires
591
+ * derive `live`, so the Wiring screen showed nothing wrong.
592
+ */
593
+ test('two dots emitting the same event name drive only the wire whose `from` matches', () => {
594
+ const table = mount('wiring-table')
595
+ const canvas = mount('pages-canvas')
596
+ const inspector = mount('pages-inspector')
597
+ const traces: Array<WireTrace> = []
598
+ attach({
599
+ topology: { wires: [pw12, pg6, pg7], watch: [] },
600
+ onTrace: trace => traces.push(trace),
601
+ })
602
+
603
+ dispatch('pages-rail', 'host-changed', { hostId: 'platform-host' })
604
+
605
+ expect(canvas.getAttribute('host-id')).toBe('platform-host')
606
+ expect(inspector.getAttribute('host-id')).toBe('platform-host')
607
+ // The other screen is untouched — the whole defect in one assertion.
608
+ expect(table.getAttribute('host-id')).toBeNull()
609
+ // …and pw12 does not even appear in the trace: this is not its event.
610
+ expect(firedWireIds(traces)).toEqual(['pg6', 'pg7'])
611
+
612
+ dispatch('wiring-canvas', 'host-changed', { hostId: 'demo-host' })
613
+
614
+ expect(table.getAttribute('host-id')).toBe('demo-host')
615
+ // The Pages panes keep the host their own rail last chose.
616
+ expect(canvas.getAttribute('host-id')).toBe('platform-host')
617
+ expect(inspector.getAttribute('host-id')).toBe('platform-host')
618
+ })
619
+
620
+ test('an event with no identifiable source fires no wires and traces the source skip', () => {
621
+ const canvas = mount('pages-canvas')
622
+ const traces: Array<WireTrace> = []
623
+ attach({
624
+ topology: { wires: [pw12, pg6], watch: [] },
625
+ onTrace: trace => traces.push(trace),
626
+ })
627
+
628
+ dispatchAnonymously('host-changed', { hostId: 'platform-host' })
629
+
630
+ expect(canvas.getAttribute('host-id')).toBeNull()
631
+ expect(traces).toEqual([
632
+ {
633
+ _tag: 'wire',
634
+ event: 'host-changed',
635
+ wireId: 'pw12',
636
+ delivery: { _tag: 'skipped', reason: 'source' },
637
+ },
638
+ {
639
+ _tag: 'wire',
640
+ event: 'host-changed',
641
+ wireId: 'pg6',
642
+ delivery: { _tag: 'skipped', reason: 'source' },
643
+ },
644
+ ])
645
+ })
646
+
647
+ test('a watch tap is the host log, not a wire: it fires for every emitter and for none', () => {
648
+ // The tap is keyed on the event NAME alone, deliberately — a log that
649
+ // only recorded the one dot a wire happens to name would stop being a
650
+ // log. Three emitters of `host-changed`, three lines.
651
+ const traces: Array<WireTrace> = []
652
+ attach({
653
+ topology: { wires: [pw12], watch: [{ event: 'host-changed' }] },
654
+ onTrace: trace => traces.push(trace),
655
+ })
656
+
657
+ dispatch('pages-rail', 'host-changed', { hostId: 'a' })
658
+ dispatch('wiring-canvas', 'host-changed', { hostId: 'b' })
659
+ dispatchAnonymously('host-changed', { hostId: 'c' })
660
+
661
+ expect(tappedPayloads(traces)).toEqual([
662
+ { hostId: 'a' },
663
+ { hostId: 'b' },
664
+ { hostId: 'c' },
665
+ ])
666
+ })
667
+
668
+ test('detach removes every listener', () => {
669
+ const fleet = mount('fleet-health')
670
+ const engine = attach({ topology: { wires: DEMO_WIRES, watch: [] } })
671
+
672
+ engine.detach()
673
+ dispatch('price-ticker', 'quote-changed', { symbol: 'EFCT', cents: 12345 })
674
+
675
+ expect(fleet.getAttribute('region')).toBeNull()
676
+ })
677
+ })
678
+
679
+ // The failure mode: a host's immediate-apply path (the demo host's link
680
+ // toggle) re-applies a wire's transform OUTSIDE the engine, and a second
681
+ // implementation of the rows/fallback semantics would drift from what the
682
+ // engine writes when the event actually fires.
683
+ describe('applyTransform', () => {
684
+ test('agrees with planWires on rows, fallback, and prototype-key misses', () => {
685
+ const symbols = ['EFCT', 'ZZZZ', 'constructor']
686
+ symbols.forEach(symbol => {
687
+ const plan = planWires(
688
+ [w1],
689
+ {
690
+ name: 'quote-changed',
691
+ sourceTag: Option.some(w1.from),
692
+ payload: { symbol, cents: 1 },
693
+ },
694
+ { env: 'dev', isEnabled: () => true },
695
+ )
696
+ expect(
697
+ plan.writes.map(write => write.value),
698
+ symbol,
699
+ ).toEqual([applyTransform(w1.transform, symbol)])
700
+ })
701
+ })
702
+
703
+ test('direct and condition pass the encoded value straight through', () => {
704
+ expect(applyTransform({ _tag: 'direct' }, 'sub-42')).toBe('sub-42')
705
+ expect(
706
+ applyTransform(
707
+ { _tag: 'condition', field: 'linked', op: 'is', value: 'true' },
708
+ 'EFCT',
709
+ ),
710
+ ).toBe('EFCT')
711
+ })
712
+ })
713
+
714
+ // The failure mode: a planner that quietly grew a DOM dependency would make
715
+ // every non-DOM transport (the cross-origin bridge, a future server-side
716
+ // derivation) impossible — transport-blindness is property 2 of
717
+ // wiki/framework/composition/wire-execution-and-transforms.md.
718
+ describe('planWires', () => {
719
+ test('plans a bridge postMessage envelope with no document and no DOM event', () => {
720
+ // Shaped like apps/cross-origin-demo/src/bridge.ts's SubmissionCreated —
721
+ // a postMessage envelope, not a CustomEvent detail. The planner neither
722
+ // knows nor cares HOW the source was identified, only that it was: with
723
+ // no `event.target` to read, a non-DOM transport names the emitting tag
724
+ // in the envelope itself.
725
+ const envelope = {
726
+ version: 1,
727
+ type: 'submission-created',
728
+ from: 'bespoke-contact-form',
729
+ submissionId: 'sub-42',
730
+ }
731
+ const bridgeWire: Wire = { ...w2, field: 'submissionId' }
732
+
733
+ const plan = planWires(
734
+ [bridgeWire],
735
+ {
736
+ name: envelope.type,
737
+ sourceTag: Option.some(envelope.from),
738
+ payload: envelope,
739
+ },
740
+ { env: 'prod', isEnabled: () => true },
741
+ )
742
+
743
+ expect(plan.writes).toEqual([
744
+ {
745
+ toTag: 'bespoke-contact-dashboard',
746
+ input: 'refresh-token',
747
+ value: 'sub-42',
748
+ },
749
+ ])
750
+ })
751
+
752
+ test('an envelope naming a different sender matches nothing and traces nothing', () => {
753
+ // The same name, the same payload shape, a different dot. Not a skip:
754
+ // this event is not this wire's event at all, and a trace line for it
755
+ // would be noise in every host log that carries a shared event name.
756
+ const bridgeWire: Wire = { ...w2, field: 'submissionId' }
757
+
758
+ const plan = planWires(
759
+ [bridgeWire],
760
+ {
761
+ name: 'submission-created',
762
+ sourceTag: Option.some('some-other-form'),
763
+ payload: { submissionId: 'sub-42' },
764
+ },
765
+ { env: 'prod', isEnabled: () => true },
766
+ )
767
+
768
+ expect(plan).toEqual({ writes: [], outcomes: [] })
769
+ })
770
+
771
+ test('a source the transport could not identify skips every name-matched wire', () => {
772
+ // `None` is not a wildcard: a wire whose source cannot be confirmed must
773
+ // NOT fire, because "some dot emitted host-changed" is exactly the
774
+ // ambiguity the `from` match exists to remove. It still traces — a
775
+ // name-matched wire going quiet is what a trace has to be able to show.
776
+ const plan = planWires(
777
+ [w2, w3],
778
+ {
779
+ name: 'submission-created',
780
+ sourceTag: Option.none(),
781
+ payload: { id: 'sub-1' },
782
+ },
783
+ { env: 'prod', isEnabled: () => true },
784
+ )
785
+
786
+ expect(plan.writes).toEqual([])
787
+ expect(plan.outcomes).toEqual([
788
+ { _tag: 'skipped', wireId: 'w2', reason: 'source' },
789
+ ])
790
+ })
791
+
792
+ test('skips a wire whose envs exclude the running environment, distinctly from disabled', () => {
793
+ const devOnly: Wire = { ...w2, envs: ['dev'] }
794
+
795
+ const plan = planWires(
796
+ [devOnly],
797
+ {
798
+ name: 'submission-created',
799
+ sourceTag: Option.some('bespoke-contact-form'),
800
+ payload: { id: 'sub-1' },
801
+ },
802
+ { env: 'prod', isEnabled: () => true },
803
+ )
804
+
805
+ expect(plan.writes).toEqual([])
806
+ expect(plan.outcomes).toEqual([
807
+ { _tag: 'skipped', wireId: 'w2', reason: 'env' },
808
+ ])
809
+ })
810
+ })