@dreamboard-games/sdk 0.2.0 → 0.2.1-alpha.1

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 (90) hide show
  1. package/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-BBMVT3KG.d.ts} +1 -1
  2. package/dist/attributes-BeRyboMS.d.ts +279 -0
  3. package/dist/browser-interaction.d.ts +708 -0
  4. package/dist/browser-interaction.js +106 -0
  5. package/dist/browser-interaction.js.map +1 -0
  6. package/dist/{bundle-TIZcw8LB.d.ts → bundle-CDd5FKeD.d.ts} +3 -1
  7. package/dist/{chunk-U5C6BONG.js → chunk-326PGVAA.js} +2 -2
  8. package/dist/{chunk-VFTAA4WO.js → chunk-MKXPVOUT.js} +4 -2
  9. package/dist/chunk-MKXPVOUT.js.map +1 -0
  10. package/dist/{chunk-GKKBPPSW.js → chunk-MZNVHMJ5.js} +4 -4
  11. package/dist/{chunk-KAELH4KC.js → chunk-NKCRKGR2.js} +2 -2
  12. package/dist/{chunk-WN74KVNY.js → chunk-PEI3FIL2.js} +2 -2
  13. package/dist/chunk-PEI3FIL2.js.map +1 -0
  14. package/dist/chunk-QLG6VEMW.js +1691 -0
  15. package/dist/chunk-QLG6VEMW.js.map +1 -0
  16. package/dist/{chunk-WYPQ3GG5.js → chunk-WG4JQL3S.js} +4 -1
  17. package/dist/{chunk-WYPQ3GG5.js.map → chunk-WG4JQL3S.js.map} +1 -1
  18. package/dist/{chunk-7YAHLYBR.js → chunk-XV6D3ET4.js} +8 -4
  19. package/dist/{chunk-7YAHLYBR.js.map → chunk-XV6D3ET4.js.map} +1 -1
  20. package/dist/{chunk-TDSWKVZ4.js → chunk-ZABVH7AO.js} +1236 -17
  21. package/dist/chunk-ZABVH7AO.js.map +1 -0
  22. package/dist/{components-D5ZRE2Hl.d.ts → components-BoiVSYqx.d.ts} +1 -1
  23. package/dist/generated/runtime/primitives.d.ts +5 -4
  24. package/dist/generated/runtime/primitives.js +4 -3
  25. package/dist/generated/runtime-api.d.ts +1 -1
  26. package/dist/generated/runtime.d.ts +5 -4
  27. package/dist/generated/runtime.js +7 -6
  28. package/dist/generated/workspace-contract.d.ts +5 -4
  29. package/dist/generated/workspace-contract.js +6 -5
  30. package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-1iAyJRFn.d.ts} +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/infrastructure/reducer-bundle-abi.d.ts +113 -113
  33. package/dist/infrastructure/reducer-bundle-abi.js +1 -1
  34. package/dist/package-set.d.ts +2 -2
  35. package/dist/package-set.js +1 -1
  36. package/dist/reducer.d.ts +1 -1
  37. package/dist/reducer.js +305 -12
  38. package/dist/reducer.js.map +1 -1
  39. package/dist/runtime/primitives.d.ts +6 -5
  40. package/dist/runtime/primitives.js +4 -3
  41. package/dist/runtime/workspace-contract.d.ts +6 -5
  42. package/dist/runtime/workspace-contract.js +6 -5
  43. package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-CPLm_XDG.d.ts} +6 -0
  44. package/dist/runtime.d.ts +5 -4
  45. package/dist/runtime.js +6 -5
  46. package/dist/testing.d.ts +2 -2
  47. package/dist/ui/components.d.ts +2 -2
  48. package/dist/ui/components.js +1 -1
  49. package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-rzKBwOLC.d.ts} +5 -3
  50. package/dist/ui.d.ts +5 -5
  51. package/dist/ui.js +2 -2
  52. package/package.json +15 -9
  53. package/src/browser-interaction/attributes.ts +211 -0
  54. package/src/browser-interaction/canonical.ts +77 -0
  55. package/src/browser-interaction/constants.ts +77 -0
  56. package/src/browser-interaction/effects.ts +176 -0
  57. package/src/browser-interaction/index.ts +111 -0
  58. package/src/browser-interaction/normalize.ts +997 -0
  59. package/src/browser-interaction/registry.ts +70 -0
  60. package/src/browser-interaction/resolve.ts +596 -0
  61. package/src/browser-interaction/schemas.ts +152 -0
  62. package/src/browser-interaction/types.ts +304 -0
  63. package/src/browser-interaction.ts +1 -0
  64. package/src/generated/reducer-contract/wire.ts +1 -1
  65. package/src/generated/reducer-contract/zod.ts +3 -1
  66. package/src/package-set.ts +1 -1
  67. package/src/reducer/bundle/ingress-bundle.ts +1 -1
  68. package/src/reducer/bundle/trusted/interaction-types.ts +3 -0
  69. package/src/reducer/bundle/trusted/projection-builder.ts +337 -13
  70. package/src/reducer/ingress/input-codec.ts +1 -1
  71. package/src/reducer/ingress/session-codec.ts +1 -1
  72. package/src/runtime-internal/components/InteractionForm.tsx +345 -7
  73. package/src/runtime-internal/components/PluginRuntime.tsx +2 -0
  74. package/src/runtime-internal/components/board/target-layer.ts +2 -0
  75. package/src/runtime-internal/context/PluginStateContext.tsx +41 -0
  76. package/src/runtime-internal/hooks/useBoardInteractions.ts +73 -11
  77. package/src/runtime-internal/primitives/board.tsx +71 -0
  78. package/src/runtime-internal/primitives/interaction.tsx +160 -1
  79. package/src/runtime-internal/types/plugin-state.ts +6 -0
  80. package/src/runtime-internal/utils/browser-interaction-effects.ts +240 -0
  81. package/src/runtime-internal/utils/interaction-draft-digest.ts +252 -0
  82. package/src/runtime-internal/utils/semantic-projection-digest.ts +407 -0
  83. package/src/ui/components/board/HexGrid.tsx +3 -0
  84. package/src/ui/components/board/target-layer.ts +1 -0
  85. package/dist/chunk-TDSWKVZ4.js.map +0 -1
  86. package/dist/chunk-VFTAA4WO.js.map +0 -1
  87. package/dist/chunk-WN74KVNY.js.map +0 -1
  88. /package/dist/{chunk-U5C6BONG.js.map → chunk-326PGVAA.js.map} +0 -0
  89. /package/dist/{chunk-GKKBPPSW.js.map → chunk-MZNVHMJ5.js.map} +0 -0
  90. /package/dist/{chunk-KAELH4KC.js.map → chunk-NKCRKGR2.js.map} +0 -0
@@ -29,10 +29,18 @@ import {
29
29
 
30
30
  type ProjectionMode = "full" | "actionsOnly";
31
31
  type DescriptorRegistry = {
32
- add(descriptor: InteractionDescriptorShape): string;
32
+ add(descriptor: InteractionDescriptorShape, actorSeat: number): string;
33
33
  entries(): Record<string, InteractionDescriptorShape>;
34
34
  };
35
35
 
36
+ type CanonicalJson =
37
+ | null
38
+ | boolean
39
+ | number
40
+ | string
41
+ | CanonicalJson[]
42
+ | { [key: string]: CanonicalJson };
43
+
36
44
  type InteractionResolverFor<
37
45
  Contract extends ReducerGameContractLike,
38
46
  Definitions extends PhaseMapOf<Contract>,
@@ -57,18 +65,22 @@ export function createProjectionBuilder<
57
65
  const byRef: Record<string, InteractionDescriptorShape> = {};
58
66
  const byHash = new Map<string, string>();
59
67
  return {
60
- add(descriptor) {
61
- const fingerprint = stableStringify(descriptor);
68
+ add(descriptor, actorSeat) {
69
+ const enriched = descriptorWithBrowserReplayDigests(
70
+ descriptor,
71
+ actorSeat,
72
+ );
73
+ const fingerprint = stableStringify(enriched);
62
74
  const existing = byHash.get(fingerprint);
63
75
  if (existing) return existing;
64
76
  const base =
65
- typeof descriptor.interactionId === "string" &&
66
- descriptor.interactionId.length > 0
67
- ? descriptor.interactionId
77
+ typeof enriched.interactionId === "string" &&
78
+ enriched.interactionId.length > 0
79
+ ? enriched.interactionId
68
80
  : "interaction";
69
81
  const ref = `${base}:${fnv1a64(fingerprint)}`;
70
82
  byHash.set(fingerprint, ref);
71
- byRef[ref] = descriptor;
83
+ byRef[ref] = enriched;
72
84
  return ref;
73
85
  },
74
86
  entries() {
@@ -80,6 +92,7 @@ export function createProjectionBuilder<
80
92
  function resolveZoneHandlesFor(
81
93
  combinedState: State,
82
94
  playerId: PlayerId,
95
+ actorSeat: number,
83
96
  projection: ProjectionContext<DomainState, State>,
84
97
  registry: DescriptorRegistry,
85
98
  ) {
@@ -165,10 +178,13 @@ export function createProjectionBuilder<
165
178
  continue;
166
179
  }
167
180
  perCard.push(
168
- registry.add({
169
- ...decision.descriptor,
170
- zoneId,
171
- }),
181
+ registry.add(
182
+ {
183
+ ...decision.descriptor,
184
+ zoneId,
185
+ },
186
+ actorSeat,
187
+ ),
172
188
  );
173
189
  }
174
190
  playableByCardId[cardId] = perCard;
@@ -274,12 +290,12 @@ export function createProjectionBuilder<
274
290
  zones?: ReturnType<typeof resolveZoneHandlesFor>;
275
291
  };
276
292
  const seats: Record<string, SeatProjection> = {};
277
- for (const playerId of playerIds) {
293
+ for (const [actorSeat, playerId] of playerIds.entries()) {
278
294
  const availableInteractionRefs = interactions
279
295
  .resolveAvailableInteractionsFor(combinedState, playerId, {
280
296
  projection,
281
297
  })
282
- .map((descriptor) => registry.add(descriptor));
298
+ .map((descriptor) => registry.add(descriptor, actorSeat));
283
299
  seats[playerId as unknown as string] = {
284
300
  ...(projectionMode === "full"
285
301
  ? {
@@ -287,6 +303,7 @@ export function createProjectionBuilder<
287
303
  zones: resolveZoneHandlesFor(
288
304
  combinedState,
289
305
  playerId,
306
+ actorSeat,
290
307
  projection,
291
308
  registry,
292
309
  ),
@@ -331,6 +348,173 @@ export function createProjectionBuilder<
331
348
  };
332
349
  }
333
350
 
351
+ function descriptorWithBrowserReplayDigests(
352
+ descriptor: InteractionDescriptorShape,
353
+ actorSeat: number,
354
+ ): InteractionDescriptorShape {
355
+ const descriptorDigestValue =
356
+ descriptor.descriptorDigest ?? interactionDescriptorDigest(descriptor);
357
+ return {
358
+ ...descriptor,
359
+ descriptorDigest: descriptorDigestValue,
360
+ actorSeat,
361
+ draftDigest:
362
+ descriptor.draftDigest ??
363
+ interactionDraftDigest({
364
+ actorSeat,
365
+ descriptor,
366
+ descriptorDigest: descriptorDigestValue,
367
+ }),
368
+ };
369
+ }
370
+
371
+ function interactionDescriptorDigest(
372
+ descriptor: InteractionDescriptorShape,
373
+ ): string {
374
+ return hashJson({
375
+ commitMode: descriptor.commit.mode,
376
+ defaults: toDescriptorDigestJson(defaultsForDescriptor(descriptor)),
377
+ inputKeys: descriptor.inputs.map((input) => input.key),
378
+ inputs: descriptor.inputs.map((input) => ({
379
+ key: input.key,
380
+ kind: input.kind,
381
+ domain: toDescriptorDigestJson(input.domain),
382
+ defaultValue:
383
+ input.defaultValue === undefined
384
+ ? null
385
+ : toDescriptorDigestJson(input.defaultValue),
386
+ })),
387
+ interactionId: descriptor.interactionId,
388
+ interactionKey: descriptor.interactionKey,
389
+ stableIdentity: `${descriptor.interactionKey}:${descriptor.interactionId}`,
390
+ });
391
+ }
392
+
393
+ function interactionDraftDigest({
394
+ actorSeat,
395
+ descriptor,
396
+ descriptorDigest,
397
+ }: {
398
+ actorSeat: number;
399
+ descriptor: InteractionDescriptorShape;
400
+ descriptorDigest: string;
401
+ }): string {
402
+ return hashJson({
403
+ digestVersion: "interaction-draft@2",
404
+ actorSeat,
405
+ descriptorDigest,
406
+ emitted: false,
407
+ interactionId: descriptor.interactionId,
408
+ interactionKey: descriptor.interactionKey,
409
+ values: defaultsForDescriptor(descriptor),
410
+ });
411
+ }
412
+
413
+ function toDescriptorDigestJson(value: unknown): CanonicalJson {
414
+ const canonical = toCanonicalJson(value);
415
+ return normalizeOrderInsensitiveDescriptorFields(canonical);
416
+ }
417
+
418
+ function normalizeOrderInsensitiveDescriptorFields(
419
+ value: CanonicalJson,
420
+ ): CanonicalJson {
421
+ if (Array.isArray(value)) {
422
+ return value.map(normalizeOrderInsensitiveDescriptorFields);
423
+ }
424
+ if (value === null || typeof value !== "object") {
425
+ return value;
426
+ }
427
+ return Object.fromEntries(
428
+ Object.entries(value).map(([key, item]) => {
429
+ const normalized = normalizeOrderInsensitiveDescriptorFields(item);
430
+ if (
431
+ (key === "eligibleTargets" || key === "dependentCases") &&
432
+ Array.isArray(normalized)
433
+ ) {
434
+ return [
435
+ key,
436
+ [...normalized].sort((left, right) =>
437
+ compareCanonicalJson(canonicalJson(left), canonicalJson(right)),
438
+ ),
439
+ ];
440
+ }
441
+ return [key, normalized];
442
+ }),
443
+ );
444
+ }
445
+
446
+ function defaultsForDescriptor(
447
+ descriptor: InteractionDescriptorShape,
448
+ ): Record<string, CanonicalJson> {
449
+ return Object.fromEntries(
450
+ descriptor.inputs.flatMap((input) =>
451
+ input.defaultValue === undefined
452
+ ? []
453
+ : [[input.key, toCanonicalJson(input.defaultValue)]],
454
+ ),
455
+ );
456
+ }
457
+
458
+ function toCanonicalJson(value: unknown): CanonicalJson {
459
+ if (
460
+ value === null ||
461
+ typeof value === "boolean" ||
462
+ typeof value === "string"
463
+ ) {
464
+ return value;
465
+ }
466
+ if (typeof value === "number") {
467
+ return value;
468
+ }
469
+ if (Array.isArray(value)) {
470
+ return value.map((item) => toCanonicalJson(item));
471
+ }
472
+ if (typeof value === "object") {
473
+ return Object.fromEntries(
474
+ Object.entries(value as Record<string, unknown>)
475
+ .filter(([, item]) => item !== undefined)
476
+ .map(([key, item]) => [key, toCanonicalJson(item)]),
477
+ );
478
+ }
479
+ return null;
480
+ }
481
+
482
+ function hashJson(value: CanonicalJson): string {
483
+ return `sha256:${sha256Hex(canonicalJson(value))}`;
484
+ }
485
+
486
+ function canonicalJson(value: CanonicalJson): string {
487
+ return JSON.stringify(canonicalizeJson(value));
488
+ }
489
+
490
+ function canonicalizeJson(value: CanonicalJson): CanonicalJson {
491
+ if (
492
+ value === null ||
493
+ typeof value === "boolean" ||
494
+ typeof value === "string"
495
+ ) {
496
+ return value;
497
+ }
498
+ if (typeof value === "number") {
499
+ if (!Number.isFinite(value)) {
500
+ throw new Error("canonical JSON contains a non-finite number");
501
+ }
502
+ return value;
503
+ }
504
+ if (Array.isArray(value)) {
505
+ return value.map((item) => canonicalizeJson(item));
506
+ }
507
+ return Object.fromEntries(
508
+ Object.entries(value)
509
+ .sort(([left], [right]) => left.localeCompare(right))
510
+ .map(([key, item]) => [key, canonicalizeJson(item)]),
511
+ );
512
+ }
513
+
514
+ function compareCanonicalJson(left: string, right: string): number {
515
+ return left < right ? -1 : left > right ? 1 : 0;
516
+ }
517
+
334
518
  function stableStringify(value: unknown): string {
335
519
  if (value === null || typeof value !== "object") {
336
520
  return JSON.stringify(value);
@@ -354,3 +538,143 @@ function fnv1a64(value: string): string {
354
538
  }
355
539
  return hash.toString(16).padStart(16, "0");
356
540
  }
541
+
542
+ const SHA256_K = [
543
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
544
+ 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
545
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
546
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
547
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
548
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
549
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
550
+ 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
551
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
552
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
553
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
554
+ ];
555
+
556
+ function sha256Hex(input: string): string {
557
+ const bytes = utf8Bytes(input);
558
+ const bitLength = bytes.length * 8;
559
+ bytes.push(0x80);
560
+ while (bytes.length % 64 !== 56) {
561
+ bytes.push(0);
562
+ }
563
+ for (let shift = 56; shift >= 0; shift -= 8) {
564
+ bytes.push(Math.floor(bitLength / 2 ** shift) & 0xff);
565
+ }
566
+
567
+ let h0 = 0x6a09e667;
568
+ let h1 = 0xbb67ae85;
569
+ let h2 = 0x3c6ef372;
570
+ let h3 = 0xa54ff53a;
571
+ let h4 = 0x510e527f;
572
+ let h5 = 0x9b05688c;
573
+ let h6 = 0x1f83d9ab;
574
+ let h7 = 0x5be0cd19;
575
+ const words = new Array<number>(64);
576
+
577
+ for (let offset = 0; offset < bytes.length; offset += 64) {
578
+ for (let index = 0; index < 16; index += 1) {
579
+ const base = offset + index * 4;
580
+ words[index] =
581
+ ((bytes[base] << 24) |
582
+ (bytes[base + 1] << 16) |
583
+ (bytes[base + 2] << 8) |
584
+ bytes[base + 3]) >>>
585
+ 0;
586
+ }
587
+ for (let index = 16; index < 64; index += 1) {
588
+ const s0 =
589
+ rotateRight(words[index - 15], 7) ^
590
+ rotateRight(words[index - 15], 18) ^
591
+ (words[index - 15] >>> 3);
592
+ const s1 =
593
+ rotateRight(words[index - 2], 17) ^
594
+ rotateRight(words[index - 2], 19) ^
595
+ (words[index - 2] >>> 10);
596
+ words[index] = (words[index - 16] + s0 + words[index - 7] + s1) >>> 0;
597
+ }
598
+
599
+ let a = h0;
600
+ let b = h1;
601
+ let c = h2;
602
+ let d = h3;
603
+ let e = h4;
604
+ let f = h5;
605
+ let g = h6;
606
+ let h = h7;
607
+
608
+ for (let index = 0; index < 64; index += 1) {
609
+ const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
610
+ const ch = (e & f) ^ (~e & g);
611
+ const temp1 = (h + s1 + ch + SHA256_K[index] + words[index]) >>> 0;
612
+ const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
613
+ const maj = (a & b) ^ (a & c) ^ (b & c);
614
+ const temp2 = (s0 + maj) >>> 0;
615
+
616
+ h = g;
617
+ g = f;
618
+ f = e;
619
+ e = (d + temp1) >>> 0;
620
+ d = c;
621
+ c = b;
622
+ b = a;
623
+ a = (temp1 + temp2) >>> 0;
624
+ }
625
+
626
+ h0 = (h0 + a) >>> 0;
627
+ h1 = (h1 + b) >>> 0;
628
+ h2 = (h2 + c) >>> 0;
629
+ h3 = (h3 + d) >>> 0;
630
+ h4 = (h4 + e) >>> 0;
631
+ h5 = (h5 + f) >>> 0;
632
+ h6 = (h6 + g) >>> 0;
633
+ h7 = (h7 + h) >>> 0;
634
+ }
635
+
636
+ return [h0, h1, h2, h3, h4, h5, h6, h7]
637
+ .map((word) => word.toString(16).padStart(8, "0"))
638
+ .join("");
639
+ }
640
+
641
+ function rotateRight(value: number, shift: number): number {
642
+ return (value >>> shift) | (value << (32 - shift));
643
+ }
644
+
645
+ function utf8Bytes(input: string): number[] {
646
+ const bytes: number[] = [];
647
+ for (let index = 0; index < input.length; index += 1) {
648
+ let codePoint = input.charCodeAt(index);
649
+ if (
650
+ codePoint >= 0xd800 &&
651
+ codePoint <= 0xdbff &&
652
+ index + 1 < input.length
653
+ ) {
654
+ const low = input.charCodeAt(index + 1);
655
+ if (low >= 0xdc00 && low <= 0xdfff) {
656
+ codePoint = 0x10000 + ((codePoint - 0xd800) << 10) + (low - 0xdc00);
657
+ index += 1;
658
+ }
659
+ }
660
+ if (codePoint < 0x80) {
661
+ bytes.push(codePoint);
662
+ } else if (codePoint < 0x800) {
663
+ bytes.push(0xc0 | (codePoint >>> 6), 0x80 | (codePoint & 0x3f));
664
+ } else if (codePoint < 0x10000) {
665
+ bytes.push(
666
+ 0xe0 | (codePoint >>> 12),
667
+ 0x80 | ((codePoint >>> 6) & 0x3f),
668
+ 0x80 | (codePoint & 0x3f),
669
+ );
670
+ } else {
671
+ bytes.push(
672
+ 0xf0 | (codePoint >>> 18),
673
+ 0x80 | ((codePoint >>> 12) & 0x3f),
674
+ 0x80 | ((codePoint >>> 6) & 0x3f),
675
+ 0x80 | (codePoint & 0x3f),
676
+ );
677
+ }
678
+ }
679
+ return bytes;
680
+ }
@@ -14,5 +14,5 @@ export function createRuntimeInputParser<PlayerId extends string>(
14
14
  });
15
15
 
16
16
  return (rawInput: unknown) =>
17
- safeParseOrThrow(rawRuntimeInputSchema, rawInput, "input");
17
+ safeParseOrThrow(rawRuntimeInputSchema, rawInput, "input") as RawRuntimeInput;
18
18
  }
@@ -380,7 +380,7 @@ export function createIngressRuntimeCodec<
380
380
  return {
381
381
  domain: { ...state.domain },
382
382
  runtime: state.runtime,
383
- } as RawReducerSessionState;
383
+ } as unknown as RawReducerSessionState;
384
384
  },
385
385
  parsePlayerId(rawPlayerId: string) {
386
386
  return safeParseOrThrow(playerIdSchema, rawPlayerId, "playerId");