@dreamboard-games/cli 0.1.30-alpha.0 → 0.1.30-alpha.2

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 (114) hide show
  1. package/README.md +179 -22
  2. package/dist/{chunk-TSJVWTJO.js → chunk-N7XPNNUI.js} +14 -12
  3. package/dist/chunk-N7XPNNUI.js.map +1 -0
  4. package/dist/chunk-SEGVTWSK.js +44 -0
  5. package/dist/{chunk-3XNJT3RK.js → chunk-TAQKH67O.js} +21279 -35845
  6. package/dist/chunk-TAQKH67O.js.map +1 -0
  7. package/dist/{global-config-UKSWNDTX.js → global-config-S4ZIPECE.js} +3 -3
  8. package/dist/index.js +955 -230
  9. package/dist/index.js.map +1 -1
  10. package/dist/internal.js +3 -4
  11. package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
  12. package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
  13. package/package.json +6 -6
  14. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  15. package/skills/dreamboard/references/cli.md +104 -0
  16. package/skills/dreamboard/references/game-interface.md +548 -0
  17. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  18. package/skills/dreamboard/references/quickstart.md +66 -0
  19. package/skills/dreamboard/references/reducer.md +864 -0
  20. package/skills/dreamboard/references/rule-authoring.md +147 -0
  21. package/skills/dreamboard/references/testing.md +249 -0
  22. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  23. package/dist/agent-verifier/agent-workspace-verifier.mjs +0 -227
  24. package/dist/agent-verifier/chunk-2E5P5NWG.mjs +0 -835
  25. package/dist/agent-verifier/chunk-2GBBP27W.mjs +0 -301
  26. package/dist/agent-verifier/chunk-2NZNKIND.mjs +0 -166
  27. package/dist/agent-verifier/chunk-2QMNAVV4.mjs +0 -14522
  28. package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
  29. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  30. package/dist/agent-verifier/chunk-6A5HRJMQ.mjs +0 -3174
  31. package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
  32. package/dist/agent-verifier/chunk-7653FPGJ.mjs +0 -381
  33. package/dist/agent-verifier/chunk-BVVNBJM4.mjs +0 -221
  34. package/dist/agent-verifier/chunk-CEDUHGNH.mjs +0 -74
  35. package/dist/agent-verifier/chunk-CEQ2VJWN.mjs +0 -149
  36. package/dist/agent-verifier/chunk-CFU5EWIC.mjs +0 -69
  37. package/dist/agent-verifier/chunk-DTMJCPS4.mjs +0 -730
  38. package/dist/agent-verifier/chunk-EIQWDQWJ.mjs +0 -186
  39. package/dist/agent-verifier/chunk-EOQIV6PS.mjs +0 -649
  40. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  41. package/dist/agent-verifier/chunk-HJFQDSTU.mjs +0 -225
  42. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  43. package/dist/agent-verifier/chunk-LM3OZLZG.mjs +0 -48
  44. package/dist/agent-verifier/chunk-MINCYHXN.mjs +0 -106
  45. package/dist/agent-verifier/chunk-MRCUP5SW.mjs +0 -128
  46. package/dist/agent-verifier/chunk-PM3SVG6R.mjs +0 -38
  47. package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
  48. package/dist/agent-verifier/chunk-RJBLBYHX.mjs +0 -1681
  49. package/dist/agent-verifier/chunk-SHUMAVAP.mjs +0 -59
  50. package/dist/agent-verifier/chunk-SYPLYRGB.mjs +0 -2812
  51. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  52. package/dist/agent-verifier/chunk-VYJTHSYR.mjs +0 -44
  53. package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
  54. package/dist/agent-verifier/compile-WNCQQVOF.mjs +0 -313
  55. package/dist/agent-verifier/global-config-WX3ZZIVU.mjs +0 -17
  56. package/dist/agent-verifier/local-files-MTPLP62S.mjs +0 -46
  57. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  58. package/dist/agent-verifier/materialize-workspace-EWGZIVOY.mjs +0 -90
  59. package/dist/agent-verifier/project-state-7GR6BQTQ.mjs +0 -32
  60. package/dist/agent-verifier/reducer-bundle-preflight-C73LEXI2.mjs +0 -23
  61. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  62. package/dist/agent-verifier/reducer-native-test-harness-GMWBUISX.mjs +0 -53
  63. package/dist/agent-verifier/static-scaffold-4YEQME5N.mjs +0 -28
  64. package/dist/agent-verifier/sync-LOQAH4RC.mjs +0 -594
  65. package/dist/agent-verifier/test-YOJERVHN.mjs +0 -356
  66. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  67. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  68. package/dist/agent-verifier/workspace-dependencies-HZ6VVS4G.mjs +0 -14
  69. package/dist/chunk-2H7UOFLK.js +0 -11
  70. package/dist/chunk-3XNJT3RK.js.map +0 -1
  71. package/dist/chunk-7FOO4AJI.js +0 -50
  72. package/dist/chunk-7FOO4AJI.js.map +0 -1
  73. package/dist/chunk-TSJVWTJO.js.map +0 -1
  74. package/dist/internal.d.ts +0 -311
  75. package/dist/keychain-backend-JHTXAKWC.js +0 -135
  76. package/dist/prompt-GMZABCJC.js +0 -756
  77. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  78. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  79. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  80. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  81. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  82. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  83. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  84. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  85. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  86. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  87. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  88. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  89. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  90. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  91. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  92. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  93. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  94. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  95. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  96. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  97. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  98. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  99. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  100. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  101. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  102. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  103. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  104. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  105. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  106. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  107. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  108. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  109. package/dist/testing-KLSV6CPJ.js +0 -674
  110. package/dist/testing-KLSV6CPJ.js.map +0 -1
  111. /package/dist/{chunk-2H7UOFLK.js.map → chunk-SEGVTWSK.js.map} +0 -0
  112. /package/dist/{global-config-UKSWNDTX.js.map → global-config-S4ZIPECE.js.map} +0 -0
  113. /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
  114. /package/dist/{prompt-GMZABCJC.js.map → prompt-NDV3AE5L.js.map} +0 -0
@@ -1,431 +0,0 @@
1
- import type {
2
- HistoryEntrySummary,
3
- HostGameplaySeatView,
4
- HostSessionStatus,
5
- InteractionDescriptor,
6
- SeatAssignment,
7
- SessionActor,
8
- SessionGameSource,
9
- ZoneHandles as WireZoneHandles,
10
- } from "@dreamboard-games/api-client";
11
- import type {
12
- PluginStateSnapshot,
13
- ZoneHandlesSnapshot,
14
- } from "@dreamboard-games/sdk/runtime/runtime-api";
15
- import {
16
- seatControlledByPrincipal,
17
- gameIdFromGameSource,
18
- } from "./actor-principal.js";
19
-
20
- type LayoutConfig = unknown;
21
- type CardDisplayConfig = unknown;
22
-
23
- export interface SessionIdentity {
24
- sessionId: string;
25
- shortCode: string;
26
- gameId: string;
27
- }
28
-
29
- export interface HistoryState {
30
- entries: HistoryEntrySummary[];
31
- currentIndex: number;
32
- canGoBack: boolean;
33
- canGoForward: boolean;
34
- }
35
-
36
- export interface SessionContextIngress {
37
- sessionId: string;
38
- shortCode: string;
39
- status: HostSessionStatus;
40
- gameSource: SessionGameSource;
41
- setupProfileId?: string | null;
42
- hostActor: SessionActor;
43
- switchablePlayerIds: string[];
44
- history?: HistoryState | null;
45
- }
46
-
47
- export interface LobbyIngress {
48
- seats: SeatAssignment[];
49
- canStart: boolean;
50
- }
51
-
52
- export interface GameplayViewportIngress {
53
- version: number;
54
- actionSetVersion: string;
55
- perspectivePlayerId: string;
56
- controllablePlayerIds: string[];
57
- shared: {
58
- activePlayers: string[];
59
- currentPhase: string | null;
60
- currentStage?: string | null;
61
- stageSeats?: string[];
62
- simultaneousPhase?: SimultaneousPhaseSnapshot | null;
63
- boardStatic?: string | null;
64
- boardStaticHash?: string | null;
65
- };
66
- interactionsByRef: Record<string, InteractionDescriptor | null | undefined>;
67
- seats: Record<string, HostGameplaySeatView | null | undefined>;
68
- }
69
-
70
- export interface SessionContext {
71
- identity: SessionIdentity;
72
- userId: string | null;
73
- seats: SeatAssignment[];
74
- canStart: boolean;
75
- status: HostSessionStatus;
76
- gameSource: SessionGameSource;
77
- setupProfileId?: string | null;
78
- hostActor: SessionActor;
79
- switchablePlayerIds: string[];
80
- history: HistoryState | null;
81
- }
82
-
83
- export interface GameplayPerspective {
84
- playerId: string;
85
- }
86
-
87
- export interface GameplayViewport {
88
- version: number;
89
- actionSetVersion: string;
90
- activePlayers: string[];
91
- currentPhase: string | null;
92
- currentStage: string | null;
93
- stageSeats: string[];
94
- simultaneousPhase: SimultaneousPhaseSnapshot | null;
95
- view: PluginStateSnapshot["view"];
96
- availableInteractions: InteractionDescriptor[];
97
- zones: Record<string, ZoneHandlesSnapshot>;
98
- boardStatic: Record<string, unknown> | null;
99
- boardStaticHash: string | null;
100
- layout?: LayoutConfig | null;
101
- cardDisplayConfigs?: CardDisplayConfig[] | null;
102
- seatProjectionsByPlayerId: Record<string, GameplaySeatProjection>;
103
- }
104
-
105
- export interface GameplaySeatProjection {
106
- version: number;
107
- actionSetVersion: string;
108
- view: PluginStateSnapshot["view"];
109
- availableInteractions: InteractionDescriptor[];
110
- zones: Record<string, ZoneHandlesSnapshot>;
111
- }
112
-
113
- export interface SimultaneousPhaseSnapshot {
114
- phaseName: string;
115
- interactionId: string;
116
- actorIds: string[];
117
- sealedPlayerIds: string[];
118
- pendingPlayerIds: string[];
119
- }
120
-
121
- export type UnifiedSessionModel =
122
- | { type: "idle" }
123
- | { type: "loading"; target?: { sessionId?: string; shortCode?: string } }
124
- | { type: "lobby"; context: SessionContext; preferredPlayerId: string | null }
125
- | {
126
- type: "gameplayLoading";
127
- context: SessionContext;
128
- requestedPlayerId: string;
129
- }
130
- | {
131
- type: "gameplay";
132
- context: SessionContext;
133
- perspective: GameplayPerspective;
134
- gameplay: GameplayViewport;
135
- }
136
- | { type: "ended"; context: SessionContext }
137
- | { type: "error"; message: string; context?: SessionContext };
138
-
139
- export type SessionPhase = UnifiedSessionModel["type"];
140
-
141
- export function toHistoryState(
142
- history: SessionContextIngress["history"] | undefined,
143
- ): HistoryState | null {
144
- return history
145
- ? {
146
- entries: history.entries,
147
- currentIndex: history.currentIndex,
148
- canGoBack: history.canGoBack,
149
- canGoForward: history.canGoForward,
150
- }
151
- : null;
152
- }
153
-
154
- export function identityFromHostContext(
155
- context: SessionContextIngress,
156
- ): SessionIdentity {
157
- return {
158
- sessionId: context.sessionId,
159
- shortCode: context.shortCode,
160
- gameId: gameIdFromGameSource(context.gameSource),
161
- };
162
- }
163
-
164
- export function deriveControlledPlayerIdsFromSeats(
165
- seats: SeatAssignment[],
166
- userId: string | null,
167
- fallbackToAllSeatsWhenUserIdMissing = false,
168
- ): string[] {
169
- if (!userId) {
170
- return fallbackToAllSeatsWhenUserIdMissing
171
- ? seats.map((seat) => seat.playerId)
172
- : [];
173
- }
174
- return seats
175
- .filter((seat) => seatControlledByPrincipal(seat, userId))
176
- .map((seat) => seat.playerId);
177
- }
178
-
179
- export function resolveControllablePlayerIds(
180
- preferredPlayerIds: string[] | null | undefined,
181
- seats: SeatAssignment[],
182
- userId: string | null,
183
- fallbackToAllSeatsWhenUserIdMissing = false,
184
- ): string[] {
185
- return preferredPlayerIds && preferredPlayerIds.length > 0
186
- ? preferredPlayerIds
187
- : deriveControlledPlayerIdsFromSeats(
188
- seats,
189
- userId,
190
- fallbackToAllSeatsWhenUserIdMissing,
191
- );
192
- }
193
-
194
- export function contextFromHostParts(
195
- wireContext: SessionContextIngress,
196
- lobby: LobbyIngress,
197
- userId: string | null,
198
- ): SessionContext {
199
- return {
200
- identity: identityFromHostContext(wireContext),
201
- userId,
202
- seats: lobby.seats,
203
- canStart: lobby.canStart,
204
- status: wireContext.status,
205
- gameSource: wireContext.gameSource,
206
- setupProfileId: wireContext.setupProfileId ?? null,
207
- hostActor: wireContext.hostActor,
208
- switchablePlayerIds: wireContext.switchablePlayerIds,
209
- history: toHistoryState(wireContext.history),
210
- };
211
- }
212
-
213
- export function contextFromHostSnapshot(
214
- snapshot: { context: SessionContextIngress; lobby: LobbyIngress },
215
- userId: string | null,
216
- ): SessionContext {
217
- return contextFromHostParts(snapshot.context, snapshot.lobby, userId);
218
- }
219
-
220
- export function contextFromHostEventContext(
221
- context: SessionContext,
222
- wireContext: SessionContextIngress,
223
- lobby?: LobbyIngress,
224
- ): SessionContext {
225
- return {
226
- ...context,
227
- identity: identityFromHostContext(wireContext),
228
- status: wireContext.status,
229
- gameSource: wireContext.gameSource,
230
- setupProfileId: wireContext.setupProfileId ?? null,
231
- seats: lobby?.seats ?? context.seats,
232
- canStart: lobby?.canStart ?? context.canStart,
233
- hostActor: wireContext.hostActor,
234
- switchablePlayerIds: wireContext.switchablePlayerIds,
235
- history:
236
- wireContext.history !== undefined
237
- ? toHistoryState(wireContext.history)
238
- : context.history,
239
- };
240
- }
241
-
242
- export function contextWithHistory(
243
- context: SessionContext,
244
- history: SessionContextIngress["history"],
245
- ): SessionContext {
246
- return { ...context, history: toHistoryState(history) };
247
- }
248
-
249
- export function safeJsonParseValue(jsonString: string): unknown {
250
- try {
251
- return JSON.parse(jsonString);
252
- } catch {
253
- return null;
254
- }
255
- }
256
-
257
- export function transformView(
258
- serializedView: string | null | undefined,
259
- ): PluginStateSnapshot["view"] {
260
- return serializedView
261
- ? (safeJsonParseValue(serializedView) as PluginStateSnapshot["view"])
262
- : null;
263
- }
264
-
265
- export function gameplayViewportFromSnapshot(
266
- snapshot: GameplayViewportIngress,
267
- previous?: GameplayViewport | null,
268
- ): GameplayViewport {
269
- const incomingBoardStatic = snapshot.shared.boardStatic
270
- ? (safeJsonParseValue(snapshot.shared.boardStatic) as Record<
271
- string,
272
- unknown
273
- > | null)
274
- : null;
275
- const incomingBoardStaticHash = snapshot.shared.boardStaticHash ?? null;
276
- const hashDisagrees =
277
- previous?.boardStaticHash !== null &&
278
- previous?.boardStaticHash !== undefined &&
279
- incomingBoardStaticHash !== null &&
280
- incomingBoardStaticHash !== previous.boardStaticHash;
281
- const interactionsByRef = snapshot.interactionsByRef ?? {};
282
- const hydrateInteractionRefs = (
283
- refs: readonly string[] | null | undefined,
284
- ): InteractionDescriptor[] =>
285
- (refs ?? [])
286
- .map((ref) => interactionsByRef[ref])
287
- .filter((descriptor): descriptor is InteractionDescriptor =>
288
- Boolean(descriptor),
289
- );
290
- const incomingSeatProjections = Object.fromEntries(
291
- Object.entries(snapshot.seats ?? {}).flatMap(([playerId, seat]) => {
292
- if (!seat) return [];
293
- return [
294
- [
295
- playerId,
296
- {
297
- version: snapshot.version,
298
- actionSetVersion:
299
- seat.actionSetVersion ?? snapshot.actionSetVersion,
300
- view: transformView(seat.view),
301
- availableInteractions: hydrateInteractionRefs(
302
- seat.availableInteractionRefs,
303
- ),
304
- zones: hydrateZones(seat.zones, hydrateInteractionRefs),
305
- } satisfies GameplaySeatProjection,
306
- ],
307
- ];
308
- }),
309
- );
310
- const seatProjectionsByPlayerId =
311
- previous?.version === snapshot.version
312
- ? {
313
- ...previous.seatProjectionsByPlayerId,
314
- ...incomingSeatProjections,
315
- }
316
- : incomingSeatProjections;
317
- const perspectiveSeat =
318
- seatProjectionsByPlayerId[snapshot.perspectivePlayerId] ?? null;
319
-
320
- return {
321
- version: snapshot.version,
322
- actionSetVersion:
323
- perspectiveSeat?.actionSetVersion ?? snapshot.actionSetVersion,
324
- activePlayers: snapshot.shared.activePlayers,
325
- currentPhase: snapshot.shared.currentPhase,
326
- currentStage:
327
- snapshot.shared.currentStage ?? previous?.currentStage ?? null,
328
- stageSeats: snapshot.shared.stageSeats ?? previous?.stageSeats ?? [],
329
- simultaneousPhase: snapshot.shared.simultaneousPhase ?? null,
330
- view: perspectiveSeat?.view ?? null,
331
- availableInteractions: perspectiveSeat?.availableInteractions ?? [],
332
- zones: perspectiveSeat?.zones ?? {},
333
- boardStatic: hashDisagrees
334
- ? (previous?.boardStatic ?? null)
335
- : (incomingBoardStatic ?? previous?.boardStatic ?? null),
336
- boardStaticHash:
337
- incomingBoardStaticHash ?? previous?.boardStaticHash ?? null,
338
- layout: previous?.layout ?? null,
339
- cardDisplayConfigs: previous?.cardDisplayConfigs ?? null,
340
- seatProjectionsByPlayerId,
341
- };
342
- }
343
-
344
- export function gameplayViewportForPlayer(
345
- gameplay: GameplayViewport,
346
- playerId: string,
347
- ): GameplayViewport | null {
348
- const seatProjection = gameplay.seatProjectionsByPlayerId[playerId];
349
- if (!seatProjection || seatProjection.version !== gameplay.version) {
350
- return null;
351
- }
352
- return {
353
- ...gameplay,
354
- actionSetVersion: seatProjection.actionSetVersion,
355
- view: seatProjection.view,
356
- availableInteractions: seatProjection.availableInteractions,
357
- zones: seatProjection.zones,
358
- };
359
- }
360
-
361
- function hydrateZones(
362
- zones: Record<string, WireZoneHandles | null | undefined> | null | undefined,
363
- hydrateInteractionRefs: (
364
- refs: readonly string[] | null | undefined,
365
- ) => InteractionDescriptor[],
366
- ): Record<string, ZoneHandlesSnapshot> {
367
- return Object.fromEntries(
368
- Object.entries(zones ?? {}).flatMap(([zoneId, zone]) => {
369
- if (!zone) return [];
370
- return [
371
- [
372
- zoneId,
373
- {
374
- cardIds: zone.cardIds,
375
- cardViewsById: zone.cardViewsById,
376
- playableByCardId: Object.fromEntries(
377
- Object.entries(zone.playableByCardId).map(([cardId, refs]) => [
378
- cardId,
379
- hydrateInteractionRefs(refs ?? []),
380
- ]),
381
- ),
382
- },
383
- ],
384
- ];
385
- }),
386
- );
387
- }
388
-
389
- export function getSessionContext(
390
- session: UnifiedSessionModel,
391
- ): SessionContext | null {
392
- switch (session.type) {
393
- case "lobby":
394
- case "gameplayLoading":
395
- case "gameplay":
396
- case "ended":
397
- return session.context;
398
- case "error":
399
- return session.context ?? null;
400
- case "idle":
401
- case "loading":
402
- return null;
403
- }
404
- }
405
-
406
- export function getGameplayViewport(
407
- session: UnifiedSessionModel,
408
- ): GameplayViewport | null {
409
- return session.type === "gameplay" ? session.gameplay : null;
410
- }
411
-
412
- export function withContext(
413
- session: UnifiedSessionModel,
414
- context: SessionContext,
415
- ): UnifiedSessionModel {
416
- switch (session.type) {
417
- case "lobby":
418
- return { ...session, context };
419
- case "gameplayLoading":
420
- return { ...session, context };
421
- case "gameplay":
422
- return { ...session, context };
423
- case "ended":
424
- return { type: "ended", context };
425
- case "error":
426
- return { type: "error", message: session.message, context };
427
- case "idle":
428
- case "loading":
429
- return { type: "lobby", context, preferredPlayerId: null };
430
- }
431
- }
@@ -1,211 +0,0 @@
1
- import type { InteractionDescriptor } from "@dreamboard-games/api-client";
2
- import type {
3
- PluginStateSnapshot,
4
- ZoneHandlesSnapshot,
5
- } from "@dreamboard-games/sdk/runtime/runtime-api";
6
- import {
7
- principalKey,
8
- principalMatchesActor,
9
- seatsForPluginSnapshot,
10
- } from "./actor-principal.js";
11
- import type {
12
- GameplayViewport,
13
- HistoryState,
14
- SessionContext,
15
- SessionIdentity,
16
- UnifiedSessionModel,
17
- } from "./session-model.js";
18
- import {
19
- getGameplayViewport,
20
- getSessionContext,
21
- resolveControllablePlayerIds,
22
- } from "./session-model.js";
23
-
24
- export interface NotificationProjectionState {
25
- notifications: PluginStateSnapshot["notifications"];
26
- syncId: number;
27
- }
28
-
29
- export interface ProjectionState {
30
- session: UnifiedSessionModel;
31
- activity: NotificationProjectionState;
32
- }
33
-
34
- export interface LobbyViewModel {
35
- identity: SessionIdentity;
36
- seats: SessionContext["seats"];
37
- canStart: boolean;
38
- hostActor: SessionContext["hostActor"];
39
- shortCode: string;
40
- isSessionHost: boolean;
41
- }
42
-
43
- export interface GameplayViewModel {
44
- context: SessionContext;
45
- perspective: { playerId: string };
46
- gameplay: GameplayViewport;
47
- }
48
-
49
- export type BootstrapStatus = "loading" | "lobby" | "renderable" | "error";
50
-
51
- const lobbyViewModelCache = new WeakMap<SessionContext, LobbyViewModel>();
52
- const gameplayViewModelCache = new WeakMap<
53
- GameplayViewport,
54
- { context: SessionContext; value: GameplayViewModel }
55
- >();
56
-
57
- export function selectSessionContext(
58
- state: Pick<ProjectionState, "session">,
59
- ): SessionContext | null {
60
- return getSessionContext(state.session);
61
- }
62
-
63
- export function selectGameplayViewport(
64
- state: Pick<ProjectionState, "session">,
65
- ): GameplayViewport | null {
66
- return getGameplayViewport(state.session);
67
- }
68
-
69
- export function selectSessionType(
70
- state: Pick<ProjectionState, "session">,
71
- ): UnifiedSessionModel["type"] {
72
- return state.session.type;
73
- }
74
-
75
- export function selectBootstrapStatus(
76
- state: Pick<ProjectionState, "session">,
77
- ): BootstrapStatus {
78
- switch (state.session.type) {
79
- case "idle":
80
- case "loading":
81
- case "gameplayLoading":
82
- return "loading";
83
- case "lobby":
84
- case "ended":
85
- return "lobby";
86
- case "gameplay":
87
- return "renderable";
88
- case "error":
89
- return "error";
90
- }
91
- }
92
-
93
- export function selectSessionError(
94
- state: Pick<ProjectionState, "session">,
95
- ): string | null {
96
- return state.session.type === "error" ? state.session.message : null;
97
- }
98
-
99
- export function selectLobbyViewModel(
100
- state: Pick<ProjectionState, "session">,
101
- ): LobbyViewModel | null {
102
- const context = getSessionContext(state.session);
103
- if (!context) return null;
104
- const cached = lobbyViewModelCache.get(context);
105
- if (cached) return cached;
106
- const viewModel = {
107
- identity: context.identity,
108
- seats: context.seats,
109
- canStart: context.canStart,
110
- hostActor: context.hostActor,
111
- shortCode: context.identity.shortCode,
112
- isSessionHost:
113
- Boolean(context.userId) &&
114
- principalMatchesActor(context.userId, context.hostActor),
115
- };
116
- lobbyViewModelCache.set(context, viewModel);
117
- return viewModel;
118
- }
119
-
120
- export function selectGameplayViewModel(
121
- state: Pick<ProjectionState, "session">,
122
- ): GameplayViewModel | null {
123
- if (state.session.type !== "gameplay") return null;
124
- const cached = gameplayViewModelCache.get(state.session.gameplay);
125
- if (cached?.context === state.session.context) return cached.value;
126
- const value = {
127
- context: state.session.context,
128
- perspective: state.session.perspective,
129
- gameplay: state.session.gameplay,
130
- };
131
- gameplayViewModelCache.set(state.session.gameplay, {
132
- context: state.session.context,
133
- value,
134
- });
135
- return value;
136
- }
137
-
138
- export function selectPluginSnapshot(
139
- state: ProjectionState,
140
- fallbackToAllSeatsWhenUserIdMissing = false,
141
- ): PluginStateSnapshot {
142
- const context = getSessionContext(state.session);
143
- const gameplay = getGameplayViewport(state.session);
144
- const perspectivePlayerId =
145
- state.session.type === "gameplay"
146
- ? state.session.perspective.playerId
147
- : null;
148
- const controllablePlayerIds = context
149
- ? resolveControllablePlayerIds(
150
- context.switchablePlayerIds,
151
- context.seats,
152
- context.userId,
153
- fallbackToAllSeatsWhenUserIdMissing,
154
- )
155
- : [];
156
- const controllingPlayerId = perspectivePlayerId;
157
- const gameplayIsRenderable =
158
- gameplay !== null && perspectivePlayerId !== null;
159
- const visibleInteractions: InteractionDescriptor[] = gameplayIsRenderable
160
- ? gameplay.availableInteractions
161
- : [];
162
- const seatView = gameplayIsRenderable ? gameplay.view : null;
163
- const boardStatic = gameplay?.boardStatic ?? null;
164
- const view: PluginStateSnapshot["view"] =
165
- boardStatic && seatView && typeof seatView === "object"
166
- ? ({
167
- ...(seatView as Record<string, unknown>),
168
- board: {
169
- ...((seatView as { board?: Record<string, unknown> }).board ?? {}),
170
- ...boardStatic,
171
- },
172
- } as PluginStateSnapshot["view"])
173
- : boardStatic && !seatView
174
- ? ({ board: boardStatic } as unknown as PluginStateSnapshot["view"])
175
- : seatView;
176
- const visibleZones: Record<string, ZoneHandlesSnapshot> = gameplayIsRenderable
177
- ? gameplay.zones
178
- : {};
179
-
180
- return {
181
- view,
182
- gameplay: {
183
- currentPhase: gameplay?.currentPhase ?? null,
184
- currentStage: gameplay?.currentStage ?? null,
185
- activePlayers: gameplay?.activePlayers ?? [],
186
- simultaneousPhase: gameplay?.simultaneousPhase ?? null,
187
- availableInteractions: visibleInteractions,
188
- zones: visibleZones,
189
- },
190
- lobby: {
191
- seats: seatsForPluginSnapshot(context?.seats ?? []),
192
- canStart: context?.canStart ?? false,
193
- hostUserId: context ? principalKey(context.hostActor) : "",
194
- },
195
- notifications: state.activity.notifications,
196
- session: {
197
- sessionId: context?.identity.sessionId ?? null,
198
- controllablePlayerIds,
199
- controllingPlayerId,
200
- userId: context?.userId ?? null,
201
- },
202
- history: context?.history ?? null,
203
- syncId: state.activity.syncId,
204
- };
205
- }
206
-
207
- export function selectHistory(
208
- state: Pick<ProjectionState, "session">,
209
- ): HistoryState | null {
210
- return getSessionContext(state.session)?.history ?? null;
211
- }
@@ -1,80 +0,0 @@
1
- export interface SessionRecoveryDetails {
2
- message: string;
3
- retryAfterMs: number;
4
- }
5
-
6
- const DEFAULT_RECOVERY_RETRY_AFTER_MS = 2_000;
7
-
8
- function asRecord(value: unknown): Record<string, unknown> | null {
9
- return value && typeof value === "object"
10
- ? (value as Record<string, unknown>)
11
- : null;
12
- }
13
-
14
- function parseRetryAfterMs(value: unknown): number | null {
15
- if (typeof value === "number" && Number.isFinite(value) && value > 0) {
16
- return value;
17
- }
18
- if (typeof value !== "string") {
19
- return null;
20
- }
21
- const parsed = Number.parseInt(value, 10);
22
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
23
- }
24
-
25
- function getProblemPayload(error: unknown): Record<string, unknown> | null {
26
- const direct = asRecord(error);
27
- if (!direct) {
28
- return null;
29
- }
30
- if (typeof direct.title === "string" && typeof direct.status === "number") {
31
- return direct;
32
- }
33
- for (const key of ["error", "data", "body", "cause"]) {
34
- const nested = asRecord(direct[key]);
35
- if (
36
- nested &&
37
- typeof nested.title === "string" &&
38
- typeof nested.status === "number"
39
- ) {
40
- return nested;
41
- }
42
- }
43
- return null;
44
- }
45
-
46
- export function getSessionRecoveryDetails(
47
- error: unknown,
48
- ): SessionRecoveryDetails | null {
49
- const problem = getProblemPayload(error);
50
- if (problem) {
51
- const title = typeof problem.title === "string" ? problem.title : "";
52
- const detail = typeof problem.detail === "string" ? problem.detail : "";
53
- const retryable = problem.retryable === true;
54
- const isRecovery =
55
- problem.status === 503 &&
56
- retryable &&
57
- (title === "Session recovering" ||
58
- detail.toLowerCase().includes("recovering"));
59
- if (!isRecovery) {
60
- return null;
61
- }
62
- const context = asRecord(problem.context);
63
- return {
64
- message:
65
- detail.trim() || "Session state is recovering. Retrying shortly.",
66
- retryAfterMs:
67
- parseRetryAfterMs(context?.retryAfterMs) ??
68
- DEFAULT_RECOVERY_RETRY_AFTER_MS,
69
- };
70
- }
71
-
72
- if (error instanceof Error && /SSE failed:\s*503\b/i.test(error.message)) {
73
- return {
74
- message: "Session state is recovering. Retrying shortly.",
75
- retryAfterMs: DEFAULT_RECOVERY_RETRY_AFTER_MS,
76
- };
77
- }
78
-
79
- return null;
80
- }