@dreamboard-games/ui-sdk 0.0.43 → 0.0.45

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 (166) hide show
  1. package/dist/components/ActionButton.d.ts.map +1 -1
  2. package/dist/components/ActionButton.js +2 -1
  3. package/dist/components/Card.d.ts +1 -1
  4. package/dist/components/Card.d.ts.map +1 -1
  5. package/dist/components/DiceRoller.d.ts +3 -2
  6. package/dist/components/DiceRoller.d.ts.map +1 -1
  7. package/dist/components/DiceRoller.js +4 -13
  8. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  9. package/dist/components/ErrorBoundary.js +94 -2
  10. package/dist/components/InteractionForm.d.ts +1 -1
  11. package/dist/components/InteractionForm.d.ts.map +1 -1
  12. package/dist/components/InteractionForm.js +29 -15
  13. package/dist/components/PrimaryActionButton.d.ts.map +1 -1
  14. package/dist/components/PrimaryActionButton.js +7 -6
  15. package/dist/components/ResourceCounter.d.ts +59 -25
  16. package/dist/components/ResourceCounter.d.ts.map +1 -1
  17. package/dist/components/ResourceCounter.js +106 -115
  18. package/dist/components/Toast.d.ts +13 -6
  19. package/dist/components/Toast.d.ts.map +1 -1
  20. package/dist/components/Toast.js +10 -5
  21. package/dist/components/board/HexGrid.js +6 -6
  22. package/dist/components/board/target-layer.d.ts +18 -2
  23. package/dist/components/board/target-layer.d.ts.map +1 -1
  24. package/dist/components/board/target-layer.js +20 -3
  25. package/dist/components/index.d.ts +3 -4
  26. package/dist/components/index.d.ts.map +1 -1
  27. package/dist/components/index.js +3 -4
  28. package/dist/components/surfaces/InboxSurface.d.ts.map +1 -1
  29. package/dist/components/surfaces/InboxSurface.js +2 -6
  30. package/dist/components/surfaces/PlayerCardsSurface.js +2 -2
  31. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts +7 -0
  32. package/dist/components/surfaces/internal/CardZoneRoutedForm.d.ts.map +1 -0
  33. package/dist/components/surfaces/internal/CardZoneRoutedForm.js +9 -0
  34. package/dist/components/surfaces/internal/DefaultInteractionButton.d.ts.map +1 -1
  35. package/dist/components/surfaces/internal/DefaultInteractionButton.js +5 -8
  36. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts +2 -2
  37. package/dist/components/surfaces/internal/useCardZoneInteractions.d.ts.map +1 -1
  38. package/dist/components/surfaces/internal/useCardZoneInteractions.js +19 -43
  39. package/dist/context/InteractionDraftContext.d.ts +11 -2
  40. package/dist/context/InteractionDraftContext.d.ts.map +1 -1
  41. package/dist/context/InteractionDraftContext.js +41 -4
  42. package/dist/defaults/components.d.ts +0 -5
  43. package/dist/defaults/components.d.ts.map +1 -1
  44. package/dist/defaults/components.js +7 -11
  45. package/dist/hooks/useBoardInteractions.d.ts +35 -12
  46. package/dist/hooks/useBoardInteractions.d.ts.map +1 -1
  47. package/dist/hooks/useBoardInteractions.js +186 -82
  48. package/dist/hooks/useInteractionHandle.d.ts +1 -1
  49. package/dist/hooks/useInteractionHandle.d.ts.map +1 -1
  50. package/dist/hooks/useInteractionHandle.js +12 -27
  51. package/dist/index.d.ts +11 -17
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +5 -14
  54. package/dist/primitives/board.d.ts +53 -3
  55. package/dist/primitives/board.d.ts.map +1 -1
  56. package/dist/primitives/board.js +65 -41
  57. package/dist/primitives/dialog-lifecycle.d.ts +17 -0
  58. package/dist/primitives/dialog-lifecycle.d.ts.map +1 -0
  59. package/dist/primitives/dialog-lifecycle.js +24 -0
  60. package/dist/primitives/dice.d.ts +31 -0
  61. package/dist/primitives/dice.d.ts.map +1 -0
  62. package/dist/primitives/dice.js +33 -0
  63. package/dist/primitives/game.d.ts +55 -0
  64. package/dist/primitives/game.d.ts.map +1 -0
  65. package/dist/primitives/game.js +101 -0
  66. package/dist/primitives/index.d.ts +7 -4
  67. package/dist/primitives/index.d.ts.map +1 -1
  68. package/dist/primitives/index.js +7 -4
  69. package/dist/primitives/interaction-form-binding.d.ts +12 -0
  70. package/dist/primitives/interaction-form-binding.d.ts.map +1 -0
  71. package/dist/primitives/interaction-form-binding.js +14 -0
  72. package/dist/primitives/interaction-submit.d.ts +23 -0
  73. package/dist/primitives/interaction-submit.d.ts.map +1 -0
  74. package/dist/primitives/interaction-submit.js +41 -0
  75. package/dist/primitives/interaction.d.ts +76 -6
  76. package/dist/primitives/interaction.d.ts.map +1 -1
  77. package/dist/primitives/interaction.js +210 -26
  78. package/dist/primitives/player-roster.d.ts +2 -1
  79. package/dist/primitives/player-roster.d.ts.map +1 -1
  80. package/dist/primitives/prompt.d.ts +36 -11
  81. package/dist/primitives/prompt.d.ts.map +1 -1
  82. package/dist/primitives/prompt.js +29 -17
  83. package/dist/primitives/ui.d.ts +9 -0
  84. package/dist/primitives/ui.d.ts.map +1 -0
  85. package/dist/primitives/ui.js +7 -0
  86. package/dist/primitives/zone.d.ts +111 -5
  87. package/dist/primitives/zone.d.ts.map +1 -1
  88. package/dist/primitives/zone.js +349 -9
  89. package/dist/reducer.d.ts +2 -14
  90. package/dist/reducer.d.ts.map +1 -1
  91. package/dist/reducer.js +1 -14
  92. package/dist/runtime/createPluginRuntimeAPI.js +1 -1
  93. package/dist/types/hex-color.d.ts +7 -0
  94. package/dist/types/hex-color.d.ts.map +1 -0
  95. package/dist/types/hex-color.js +13 -0
  96. package/dist/types/player-state.d.ts +28 -14
  97. package/dist/types/player-state.d.ts.map +1 -1
  98. package/dist/types/plugin-state.d.ts +9 -3
  99. package/dist/types/plugin-state.d.ts.map +1 -1
  100. package/dist/ui-contract.d.ts +119 -14
  101. package/dist/ui-contract.d.ts.map +1 -1
  102. package/dist/ui-contract.js +4 -3
  103. package/dist/ui-sdk.d.ts +1637 -1245
  104. package/dist/utils/interaction-inputs.d.ts +8 -5
  105. package/dist/utils/interaction-inputs.d.ts.map +1 -1
  106. package/dist/utils/interaction-inputs.js +82 -14
  107. package/dist/utils/interaction-router.d.ts +31 -0
  108. package/dist/utils/interaction-router.d.ts.map +1 -0
  109. package/dist/utils/interaction-router.js +114 -0
  110. package/package.json +1 -1
  111. package/src/components/ActionButton.tsx +2 -1
  112. package/src/components/Card.tsx +1 -1
  113. package/src/components/DiceRoller.tsx +13 -22
  114. package/src/components/ErrorBoundary.test.tsx +19 -0
  115. package/src/components/ErrorBoundary.tsx +113 -24
  116. package/src/components/InteractionForm.test.tsx +24 -0
  117. package/src/components/InteractionForm.tsx +48 -23
  118. package/src/components/PrimaryActionButton.tsx +19 -5
  119. package/src/components/ResourceCounter.test.tsx +13 -13
  120. package/src/components/ResourceCounter.tsx +238 -244
  121. package/src/components/Toast.tsx +23 -10
  122. package/src/components/__fixtures__/ResourceCounter.fixture.tsx +70 -169
  123. package/src/components/board/HexGrid.tsx +6 -6
  124. package/src/components/board/target-layer.ts +44 -5
  125. package/src/components/index.ts +17 -10
  126. package/src/components/surfaces/InboxSurface.tsx +7 -5
  127. package/src/components/surfaces/PlayerCardsSurface.tsx +6 -6
  128. package/src/components/surfaces/internal/CardZoneRoutedForm.tsx +35 -0
  129. package/src/components/surfaces/internal/DefaultInteractionButton.tsx +17 -7
  130. package/src/components/surfaces/internal/useCardZoneInteractions.ts +25 -67
  131. package/src/context/InteractionDraftContext.tsx +51 -5
  132. package/src/defaults/components.tsx +12 -50
  133. package/src/defaults/defaults.test.tsx +1 -50
  134. package/src/hooks/useBoardInteractions.test.tsx +240 -17
  135. package/src/hooks/useBoardInteractions.ts +330 -105
  136. package/src/hooks/useInteractionHandle.ts +23 -28
  137. package/src/index.test.ts +60 -40
  138. package/src/index.ts +30 -36
  139. package/src/primitives/board.test.tsx +73 -0
  140. package/src/primitives/board.tsx +191 -40
  141. package/src/primitives/dialog-lifecycle.ts +58 -0
  142. package/src/primitives/dice.test.tsx +47 -0
  143. package/src/primitives/dice.tsx +79 -0
  144. package/src/primitives/game.test.tsx +98 -0
  145. package/src/primitives/game.tsx +213 -0
  146. package/src/primitives/index.ts +84 -0
  147. package/src/primitives/interaction-form-binding.tsx +56 -0
  148. package/src/primitives/interaction-submit.ts +90 -0
  149. package/src/primitives/interaction.test.tsx +396 -0
  150. package/src/primitives/interaction.tsx +451 -31
  151. package/src/primitives/player-roster.tsx +2 -1
  152. package/src/primitives/prompt.test.tsx +94 -3
  153. package/src/primitives/prompt.tsx +87 -48
  154. package/src/primitives/ui.test.tsx +131 -0
  155. package/src/primitives/ui.tsx +13 -0
  156. package/src/primitives/zone.test.tsx +305 -0
  157. package/src/primitives/zone.tsx +660 -12
  158. package/src/reducer.ts +7 -20
  159. package/src/runtime/createPluginRuntimeAPI.ts +1 -1
  160. package/src/types/hex-color.ts +20 -0
  161. package/src/types/player-state.ts +36 -18
  162. package/src/types/plugin-state.ts +10 -3
  163. package/src/ui-contract.ts +253 -21
  164. package/src/utils/interaction-inputs.test.ts +400 -0
  165. package/src/utils/interaction-inputs.ts +113 -11
  166. package/src/utils/interaction-router.ts +200 -0
@@ -64,7 +64,10 @@ function createDescriptor(
64
64
  }
65
65
  : {
66
66
  type: "choice" as const,
67
- choices: [{ value: "fallback", label: "Fallback" }],
67
+ choices: [
68
+ { value: "fallback", label: "Fallback" },
69
+ { value: "player-2", label: "Player 2" },
70
+ ],
68
71
  },
69
72
  };
70
73
  });
@@ -147,9 +150,11 @@ function render<T>(
147
150
  recorder?: RecordedSubmit[],
148
151
  setupDraft?: (store: InteractionUiStoreApi) => void,
149
152
  submitOverride?: RuntimeAPI["submitInteraction"],
153
+ storeRef?: { current?: InteractionUiStoreApi },
150
154
  ): T {
151
155
  const runtime = createRuntime(snapshot, recorder, submitOverride);
152
156
  const draftStore = createInteractionUiStore();
157
+ if (storeRef) storeRef.current = draftStore;
153
158
  setupDraft?.(draftStore);
154
159
  let captured!: T;
155
160
  function Harness() {
@@ -229,6 +234,73 @@ test("useBoardInteractions skips unavailable handles when merging eligibility",
229
234
  expect(Array.from(board.eligible.edge)).toEqual(["e-2"]);
230
235
  });
231
236
 
237
+ test("useBoardInteractions resolves dependent target eligibility from the draft", () => {
238
+ const session = createSessionState();
239
+ const snapshot = createSnapshot(session, [
240
+ createDescriptor({
241
+ interactionId: "craftAtWorkshop",
242
+ surface: "board-space",
243
+ inputs: [
244
+ {
245
+ key: "itemId",
246
+ kind: "form",
247
+ domain: {
248
+ type: "choice",
249
+ choices: [
250
+ { value: "anvil", label: "Anvil" },
251
+ { value: "loom", label: "Loom" },
252
+ ],
253
+ },
254
+ },
255
+ {
256
+ key: "cell",
257
+ kind: "board-space",
258
+ domain: {
259
+ type: "target",
260
+ targetKind: "space",
261
+ eligibleTargets: [],
262
+ dependsOn: ["itemId"],
263
+ dependentCases: [
264
+ {
265
+ when: { itemId: "anvil" },
266
+ domain: {
267
+ type: "target",
268
+ targetKind: "space",
269
+ eligibleTargets: ["workshop-a"],
270
+ },
271
+ },
272
+ {
273
+ when: { itemId: "loom" },
274
+ domain: {
275
+ type: "target",
276
+ targetKind: "space",
277
+ eligibleTargets: ["workshop-b"],
278
+ },
279
+ },
280
+ ],
281
+ },
282
+ },
283
+ ],
284
+ }),
285
+ ]);
286
+
287
+ const blocked = render(snapshot, () => useBoardInteractions());
288
+ expect(Array.from(blocked.eligible.space)).toEqual([]);
289
+ expect(blocked.isEligible("workshop-a", "space")).toBe(false);
290
+
291
+ const ready = render(
292
+ snapshot,
293
+ () => useBoardInteractions(),
294
+ undefined,
295
+ (store) => {
296
+ store.setInput("play.craftAtWorkshop", "itemId", "anvil");
297
+ },
298
+ );
299
+ expect(Array.from(ready.eligible.space)).toEqual(["workshop-a"]);
300
+ expect(ready.isEligible("workshop-a", "space")).toBe(true);
301
+ expect(ready.isEligible("workshop-b", "space")).toBe(false);
302
+ });
303
+
232
304
  test("useBoardInteractions unarmed matching interactions require an explicit route", async () => {
233
305
  const session = createSessionState();
234
306
  const snapshot = createSnapshot(session, [
@@ -288,7 +360,7 @@ test("useBoardInteractions equal-rank unarmed matches throw BoardInteractionConf
288
360
  }
289
361
  });
290
362
 
291
- test("useBoardInteractions target-kind select skips unavailable handles and returns null when nothing matches", async () => {
363
+ test("useBoardInteractions target-kind select skips unavailable handles and returns none when nothing matches", async () => {
292
364
  const session = createSessionState();
293
365
  const snapshot = createSnapshot(session, [
294
366
  createDescriptor({
@@ -304,11 +376,11 @@ test("useBoardInteractions target-kind select skips unavailable handles and retu
304
376
  const board = render(snapshot, () => useBoardInteractions(), recorder);
305
377
 
306
378
  const fired = await board.select.vertex("v-1");
307
- expect(fired).toBeNull();
379
+ expect(fired).toEqual({ status: "none" });
308
380
  expect(recorder).toEqual([]);
309
381
  });
310
382
 
311
- test("useBoardInteractions target-kind select merges extraInputs and backfills null for missing required inputs", async () => {
383
+ test("useBoardInteractions target-kind select exposes pending input instead of backfilling null for missing required inputs", async () => {
312
384
  const session = createSessionState();
313
385
  const snapshot = createSnapshot(session, [
314
386
  createDescriptor({
@@ -323,19 +395,20 @@ test("useBoardInteractions target-kind select merges extraInputs and backfills n
323
395
  const recorder: RecordedSubmit[] = [];
324
396
  const board = render(snapshot, () => useBoardInteractions(), recorder);
325
397
 
326
- await board.select.space("space-7");
327
- expect(recorder).toEqual([
328
- {
329
- playerId: "player-1",
330
- interactionId: "moveRobber",
331
- params: { spaceId: "space-7", stealFromPlayerId: null },
332
- },
333
- ]);
398
+ const pending = await board.select.space("space-7");
399
+ expect(pending.status).toBe("pending");
400
+ if (pending.status !== "pending") {
401
+ throw new Error("Expected pending board selection result");
402
+ }
403
+ expect(pending.interactionKey).toBe("play.moveRobber");
404
+ expect(pending.missingInputs).toEqual(["stealFromPlayerId"]);
405
+ expect(recorder).toEqual([]);
334
406
 
335
407
  recorder.length = 0;
336
- await board.select.space("space-7", {
408
+ const submitted = await board.select.space("space-7", {
337
409
  stealFromPlayerId: "player-2",
338
410
  });
411
+ expect(submitted.status).toBe("submitted");
339
412
  expect(recorder).toEqual([
340
413
  {
341
414
  playerId: "player-1",
@@ -345,6 +418,103 @@ test("useBoardInteractions target-kind select merges extraInputs and backfills n
345
418
  ]);
346
419
  });
347
420
 
421
+ test("useBoardInteractions pending input arms the descriptor and stores the target draft", async () => {
422
+ const session = createSessionState();
423
+ const snapshot = createSnapshot(session, [
424
+ createDescriptor({
425
+ interactionId: "moveRobber",
426
+ surface: "board-tile",
427
+ commit: { mode: "autoWhenReady" },
428
+ targets: { spaceId: ["space-7"] },
429
+ keys: ["spaceId", "stealFromPlayerId"],
430
+ }),
431
+ ]);
432
+
433
+ const storeRef: { current?: InteractionUiStoreApi } = {};
434
+ const board = render(
435
+ snapshot,
436
+ () => useBoardInteractions(),
437
+ undefined,
438
+ (store) => {
439
+ store.setInput("play.moveRobber", "spaceId", "space-7");
440
+ store.arm("board:space", "play.moveRobber");
441
+ },
442
+ undefined,
443
+ storeRef,
444
+ );
445
+
446
+ const pending = await board.select.space("space-7");
447
+ expect(pending.status).toBe("pending");
448
+ if (pending.status !== "pending") {
449
+ throw new Error("Expected pending board selection result");
450
+ }
451
+ expect(pending.interactionKey).toBe("play.moveRobber");
452
+ expect(pending.missingInputs).toEqual(["stealFromPlayerId"]);
453
+ expect(storeRef.current?.getDraft("play.moveRobber")).toEqual({
454
+ spaceId: "space-7",
455
+ });
456
+ expect(storeRef.current?.getArmed("board:space")).toBe("play.moveRobber");
457
+ });
458
+
459
+ test("useBoardInteractions explicit target selection follows the same no-null pending behavior", async () => {
460
+ const session = createSessionState();
461
+ const descriptor = createDescriptor({
462
+ interactionId: "moveRobber",
463
+ surface: "board-tile",
464
+ commit: { mode: "autoWhenReady" },
465
+ targets: { spaceId: ["space-7"] },
466
+ keys: ["spaceId", "stealFromPlayerId"],
467
+ });
468
+ const snapshot = createSnapshot(session, [descriptor]);
469
+
470
+ const recorder: RecordedSubmit[] = [];
471
+ const board = render(snapshot, () => useBoardInteractions(), recorder);
472
+
473
+ const result = await board.selectTarget(
474
+ descriptor,
475
+ "space",
476
+ "space-7",
477
+ "spaceId",
478
+ );
479
+ expect(result.status).toBe("pending");
480
+ if (result.status !== "pending") {
481
+ throw new Error("Expected pending board selection result");
482
+ }
483
+ expect(result.missingInputs).toEqual(["stealFromPlayerId"]);
484
+ expect(recorder).toEqual([]);
485
+ });
486
+
487
+ test("useBoardInteractions submitted board target clears draft and arm state", async () => {
488
+ const session = createSessionState();
489
+ const snapshot = createSnapshot(session, [
490
+ createDescriptor({
491
+ interactionId: "moveRobber",
492
+ surface: "board-tile",
493
+ commit: { mode: "autoWhenReady" },
494
+ targets: { spaceId: ["space-7"] },
495
+ keys: ["spaceId", "stealFromPlayerId"],
496
+ }),
497
+ ]);
498
+
499
+ const storeRef: { current?: InteractionUiStoreApi } = {};
500
+ const board = render(
501
+ snapshot,
502
+ () => useBoardInteractions(),
503
+ [],
504
+ (store) => {
505
+ store.setInput("play.moveRobber", "stealFromPlayerId", "player-2");
506
+ store.arm("board:space", "play.moveRobber");
507
+ },
508
+ undefined,
509
+ storeRef,
510
+ );
511
+
512
+ const result = await board.select.space("space-7");
513
+ expect(result.status).toBe("submitted");
514
+ expect(storeRef.current?.getDraft("play.moveRobber")).toEqual({});
515
+ expect(storeRef.current?.getArmed("board:space")).toBeNull();
516
+ });
517
+
348
518
  test("useBoardInteractions targetLayers expose reducer eligibility sets", () => {
349
519
  const session = createSessionState();
350
520
  const snapshot = createSnapshot(session, [
@@ -423,7 +593,7 @@ test("useBoardInteractions targetLayers apply extraInputs and onBeforeSelect", a
423
593
  let beforeCount = 0;
424
594
  const board = render(snapshot, () => useBoardInteractions(), recorder);
425
595
 
426
- await board.targetLayers
596
+ const result = await board.targetLayers
427
597
  .space({
428
598
  extraInputs: (targetId) => ({
429
599
  stealFromPlayerId: targetId === "space-7" ? "player-2" : null,
@@ -435,6 +605,7 @@ test("useBoardInteractions targetLayers apply extraInputs and onBeforeSelect", a
435
605
  .selectTargetId?.("space-7");
436
606
 
437
607
  expect(beforeCount).toBe(1);
608
+ expect(result?.status).toBe("submitted");
438
609
  expect(recorder).toEqual([
439
610
  {
440
611
  playerId: "player-1",
@@ -444,6 +615,50 @@ test("useBoardInteractions targetLayers apply extraInputs and onBeforeSelect", a
444
615
  ]);
445
616
  });
446
617
 
618
+ test("useBoardInteractions many-target board input submits only when selection minimum is satisfied", async () => {
619
+ const session = createSessionState();
620
+ const snapshot = createSnapshot(session, [
621
+ createDescriptor({
622
+ interactionId: "buildJumpGate",
623
+ surface: "board-edge",
624
+ commit: { mode: "autoWhenReady" },
625
+ inputs: [
626
+ {
627
+ key: "edgeIds",
628
+ kind: "board-edge",
629
+ domain: {
630
+ type: "target",
631
+ targetKind: "edge",
632
+ eligibleTargets: ["e-1", "e-2"],
633
+ selection: { mode: "many", min: 2, max: 2, distinct: true },
634
+ },
635
+ },
636
+ ],
637
+ }),
638
+ ]);
639
+
640
+ const recorder: RecordedSubmit[] = [];
641
+ const board = render(snapshot, () => useBoardInteractions(), recorder);
642
+
643
+ const first = await board.select.edge("e-1");
644
+ expect(first.status).toBe("pending");
645
+ if (first.status !== "pending") {
646
+ throw new Error("Expected pending board selection result");
647
+ }
648
+ expect(first.missingInputs).toEqual(["edgeIds"]);
649
+ expect(recorder).toEqual([]);
650
+
651
+ const second = await board.select.edge("e-2");
652
+ expect(second.status).toBe("submitted");
653
+ expect(recorder).toEqual([
654
+ {
655
+ playerId: "player-1",
656
+ interactionId: "buildJumpGate",
657
+ params: { edgeIds: ["e-1", "e-2"] },
658
+ },
659
+ ]);
660
+ });
661
+
447
662
  test("useBoardInteractions targetLayers route submit failures to onError", async () => {
448
663
  const session = createSessionState();
449
664
  const snapshot = createSnapshot(session, [
@@ -570,7 +785,11 @@ test("useBoardInteractions only exposes hand-card board targets after the card a
570
785
  expect(Array.from(armed.eligible.space)).toEqual(["space-7"]);
571
786
 
572
787
  const fired = await armed.select.space("space-7");
573
- expect(fired).toBe("play.playKnight");
788
+ expect(fired.status).toBe("submitted");
789
+ if (fired.status !== "submitted") {
790
+ throw new Error("Expected submitted board selection result");
791
+ }
792
+ expect(fired.interactionKey).toBe("play.playKnight");
574
793
  expect(recorder).toEqual([
575
794
  {
576
795
  playerId: "player-1",
@@ -580,7 +799,7 @@ test("useBoardInteractions only exposes hand-card board targets after the card a
580
799
  ]);
581
800
  });
582
801
 
583
- test("useBoardInteractions armed follow-up beats unarmed ambient board action", async () => {
802
+ test("useBoardInteractions armed pending input beats unarmed ambient board action", async () => {
584
803
  const session = createSessionState();
585
804
  const snapshot = createSnapshot(session, [
586
805
  createDescriptor({
@@ -611,7 +830,11 @@ test("useBoardInteractions armed follow-up beats unarmed ambient board action",
611
830
  );
612
831
 
613
832
  const fired = await board.select.edge("e-shared");
614
- expect(fired).toBe("play.playRoadBuilding");
833
+ expect(fired.status).toBe("submitted");
834
+ if (fired.status !== "submitted") {
835
+ throw new Error("Expected submitted board selection result");
836
+ }
837
+ expect(fired.interactionKey).toBe("play.playRoadBuilding");
615
838
  expect(recorder).toEqual([
616
839
  {
617
840
  playerId: "player-1",