@dreamboard-games/cli 0.1.30-alpha.1 → 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-C6UAT6EH.js → chunk-N7XPNNUI.js} +9 -12
  3. package/dist/chunk-N7XPNNUI.js.map +1 -0
  4. package/dist/chunk-SEGVTWSK.js +44 -0
  5. package/dist/{chunk-RS7UXJZV.js → chunk-TAQKH67O.js} +21300 -35881
  6. package/dist/chunk-TAQKH67O.js.map +1 -0
  7. package/dist/{global-config-AGFBDFYD.js → global-config-S4ZIPECE.js} +3 -3
  8. package/dist/index.js +415 -37
  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-2QMNAVV4.mjs +0 -14522
  27. package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
  28. package/dist/agent-verifier/chunk-4WD3YU2E.mjs +0 -166
  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-7E65UQLY.mjs +0 -38
  34. package/dist/agent-verifier/chunk-BVVNBJM4.mjs +0 -221
  35. package/dist/agent-verifier/chunk-CEDUHGNH.mjs +0 -74
  36. package/dist/agent-verifier/chunk-CEQ2VJWN.mjs +0 -149
  37. package/dist/agent-verifier/chunk-CFU5EWIC.mjs +0 -69
  38. package/dist/agent-verifier/chunk-CJEEA6NJ.mjs +0 -730
  39. package/dist/agent-verifier/chunk-EIQWDQWJ.mjs +0 -186
  40. package/dist/agent-verifier/chunk-EOQIV6PS.mjs +0 -649
  41. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  42. package/dist/agent-verifier/chunk-HJFQDSTU.mjs +0 -225
  43. package/dist/agent-verifier/chunk-JH22JNYD.mjs +0 -1681
  44. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  45. package/dist/agent-verifier/chunk-LM3OZLZG.mjs +0 -48
  46. package/dist/agent-verifier/chunk-MINCYHXN.mjs +0 -106
  47. package/dist/agent-verifier/chunk-MRCUP5SW.mjs +0 -128
  48. package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
  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-5QSPIOUT.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-FKALAE2T.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-AJMZZQWS.mjs +0 -28
  64. package/dist/agent-verifier/sync-3DUQH32H.mjs +0 -594
  65. package/dist/agent-verifier/test-P4U5INTD.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-7FOO4AJI.js +0 -50
  71. package/dist/chunk-7FOO4AJI.js.map +0 -1
  72. package/dist/chunk-C6UAT6EH.js.map +0 -1
  73. package/dist/chunk-RS7UXJZV.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-AGFBDFYD.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,416 +0,0 @@
1
- import type { HostSessionTransport } from "./host-session-transport.js";
2
- import { defaultHostSessionTransport } from "./host-session-transport.js";
3
- import type { LoggerLike } from "./logger.js";
4
- import { consoleLogger } from "./logger.js";
5
- import { PERF_MARK_NAMES, findActionIdByVersion, recordMark } from "./perf.js";
6
- import type { HostSessionWireEvent } from "./session-ingress.js";
7
- import {
8
- getSessionRecoveryDetails,
9
- type SessionRecoveryDetails,
10
- } from "./session-recovery.js";
11
-
12
- type HostSessionEventType = HostSessionWireEvent["type"];
13
-
14
- type ExtractHostSessionEvent<T extends HostSessionEventType> = Extract<
15
- HostSessionWireEvent,
16
- { type: T }
17
- >;
18
-
19
- type HostSessionEventHandler<
20
- T extends HostSessionEventType = HostSessionEventType,
21
- > = (data: ExtractHostSessionEvent<T>) => void;
22
- type ConnectionEventHandler = () => void;
23
- type ErrorEventHandler = (error: unknown) => void;
24
- type RecoveryEventHandler = (details: LongPollRecoveryEvent) => void;
25
- type AnyHostSessionEventHandler = (data: HostSessionWireEvent) => void;
26
- type EventHandler =
27
- | AnyHostSessionEventHandler
28
- | ConnectionEventHandler
29
- | ErrorEventHandler
30
- | RecoveryEventHandler;
31
-
32
- export interface LongPollRecoveryEvent extends SessionRecoveryDetails {
33
- attempt: number;
34
- }
35
-
36
- export type SSERecoveryEvent = LongPollRecoveryEvent;
37
-
38
- export interface LongPollSessionManagerOptions {
39
- clientId?: string;
40
- logger?: LoggerLike;
41
- recoveryRetryMaxAttempts?: number;
42
- recoveryRetrySleep?: (ms: number) => Promise<void>;
43
- transport?: Pick<
44
- HostSessionTransport,
45
- "subscribeToSessionEvents" | "disconnectSessionEvents"
46
- >;
47
- }
48
-
49
- export type SSEManagerOptions = LongPollSessionManagerOptions;
50
-
51
- export interface SSEConnectOptions {
52
- source?: string;
53
- playerId?: string;
54
- }
55
-
56
- interface ActiveConnection {
57
- connectionAttemptId: string;
58
- sessionId: string;
59
- playerId?: string;
60
- source?: string;
61
- }
62
-
63
- const TAB_ID_SESSION_STORAGE_KEY = "dreamboard.sse.tab-id";
64
-
65
- function randomId(): string {
66
- if (
67
- typeof crypto !== "undefined" &&
68
- typeof crypto.randomUUID === "function"
69
- ) {
70
- return crypto.randomUUID();
71
- }
72
- return `sse-${Date.now()}-${Math.random().toString(36).slice(2)}`;
73
- }
74
-
75
- function getOrCreateTabId(): string {
76
- if (typeof window === "undefined") {
77
- return randomId();
78
- }
79
-
80
- try {
81
- const existing = window.sessionStorage.getItem(TAB_ID_SESSION_STORAGE_KEY);
82
- if (existing) {
83
- return existing;
84
- }
85
-
86
- const created = randomId();
87
- window.sessionStorage.setItem(TAB_ID_SESSION_STORAGE_KEY, created);
88
- return created;
89
- } catch {
90
- return randomId();
91
- }
92
- }
93
-
94
- function isAbortLikeError(error: unknown): boolean {
95
- return error instanceof Error && error.name === "AbortError";
96
- }
97
-
98
- export class LongPollSessionManager {
99
- private abortController: AbortController | null = null;
100
- private activeConnection: ActiveConnection | null = null;
101
- private readonly clientId: string;
102
- private readonly transport: Pick<
103
- HostSessionTransport,
104
- "subscribeToSessionEvents" | "disconnectSessionEvents"
105
- >;
106
- private handlers: Map<string, Set<EventHandler>> = new Map();
107
- private anyMessageHandlers: Set<AnyHostSessionEventHandler> = new Set();
108
- private isConnected = false;
109
- private messageBuffer: HostSessionWireEvent[] = [];
110
- private logger: LoggerLike;
111
- private pageHideHandler: (() => void) | null = null;
112
- private readonly recoveryRetryMaxAttempts: number;
113
- private readonly recoveryRetrySleep: (ms: number) => Promise<void>;
114
-
115
- constructor(options: LongPollSessionManagerOptions = {}) {
116
- this.clientId = options.clientId ?? getOrCreateTabId();
117
- this.logger = options.logger ?? consoleLogger;
118
- this.transport = options.transport ?? defaultHostSessionTransport;
119
- this.recoveryRetryMaxAttempts = options.recoveryRetryMaxAttempts ?? 60;
120
- this.recoveryRetrySleep =
121
- options.recoveryRetrySleep ??
122
- ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
123
- }
124
-
125
- async connect(sessionId: string, options: SSEConnectOptions) {
126
- if (this.abortController) {
127
- this.disconnect();
128
- }
129
-
130
- this.abortController = new AbortController();
131
- const connection: ActiveConnection = {
132
- sessionId,
133
- playerId: options.playerId,
134
- connectionAttemptId: randomId(),
135
- source: options.source,
136
- };
137
- this.activeConnection = connection;
138
- this.registerPageHideHandler();
139
-
140
- try {
141
- let recoveryAttempt = 0;
142
-
143
- while (!this.abortController.signal.aborted) {
144
- let connected = false;
145
- let streamError: unknown = null;
146
- let stream: AsyncIterable<HostSessionWireEvent | null | undefined>;
147
-
148
- try {
149
- stream = (
150
- await this.transport.subscribeToSessionEvents({
151
- sessionId,
152
- clientId: this.clientId,
153
- connectionAttemptId: connection.connectionAttemptId,
154
- clientSource: options.source,
155
- playerId: connection.playerId,
156
- signal: this.abortController.signal,
157
- onSseError: (error) => {
158
- if (
159
- this.abortController?.signal.aborted ||
160
- isAbortLikeError(error)
161
- ) {
162
- return;
163
- }
164
- streamError = error;
165
- },
166
- })
167
- ).stream;
168
- } catch (error) {
169
- const recovered = await this.retryRecovery(error, ++recoveryAttempt);
170
- if (recovered) {
171
- continue;
172
- }
173
- throw error;
174
- }
175
-
176
- try {
177
- for await (const event of stream) {
178
- if (!event) continue;
179
- if (!connected) {
180
- connected = true;
181
- recoveryAttempt = 0;
182
- this.isConnected = true;
183
- this.emit("connected", null);
184
- }
185
-
186
- const eventType = event.type;
187
-
188
- if (eventType) {
189
- // Tier-0 perf: record `t4_sse_received` the moment the SSE
190
- // stream yields a gameplay.updated for a version we submitted.
191
- // Stitching is done via the version -> actionId map populated
192
- // at t3 in runtime-api.
193
- if (eventType === "session.gameplayUpdated") {
194
- const version = event.gameplay?.version;
195
- if (typeof version === "number") {
196
- const actionId = findActionIdByVersion(version);
197
- if (actionId) {
198
- recordMark(actionId, PERF_MARK_NAMES.T4_SSE_RECEIVED, {
199
- extra: { version },
200
- });
201
- }
202
- }
203
- }
204
- this.emit(eventType, event);
205
- } else {
206
- this.logger.error(
207
- "[LongPollSessionManager] Received event without type:",
208
- event,
209
- );
210
- }
211
- }
212
- } catch (error) {
213
- streamError = streamError ?? error;
214
- }
215
-
216
- if (streamError) {
217
- const recovered = await this.retryRecovery(
218
- streamError,
219
- ++recoveryAttempt,
220
- );
221
- if (recovered) {
222
- continue;
223
- }
224
- this.logger.error(
225
- "[LongPollSessionManager] stream error:",
226
- streamError,
227
- );
228
- this.emit("error", streamError);
229
- this.isConnected = false;
230
- this.emit("disconnected", null);
231
- return;
232
- }
233
-
234
- this.isConnected = false;
235
- this.emit("disconnected", null);
236
- return;
237
- }
238
-
239
- this.isConnected = false;
240
- this.emit("disconnected", null);
241
- } catch (error) {
242
- if (isAbortLikeError(error)) {
243
- return;
244
- }
245
-
246
- this.logger.error("[LongPollSessionManager] stream error:", error);
247
- this.emit("error", error);
248
- this.isConnected = false;
249
- this.emit("disconnected", null);
250
- } finally {
251
- if (
252
- this.activeConnection?.connectionAttemptId ===
253
- connection.connectionAttemptId
254
- ) {
255
- this.activeConnection = null;
256
- this.unregisterPageHideHandler();
257
- }
258
- }
259
- }
260
-
261
- on<T extends HostSessionEventType>(
262
- eventType: T,
263
- handler: HostSessionEventHandler<T>,
264
- ): () => void;
265
- on(
266
- eventType: "connected" | "disconnected",
267
- handler: ConnectionEventHandler,
268
- ): () => void;
269
- on(eventType: "error", handler: ErrorEventHandler): () => void;
270
- on(eventType: "recovering", handler: RecoveryEventHandler): () => void;
271
- on(eventType: string, handler: EventHandler): () => void {
272
- if (!this.handlers.has(eventType)) {
273
- this.handlers.set(eventType, new Set());
274
- }
275
- const handlers = this.handlers.get(eventType);
276
- if (handlers) {
277
- handlers.add(handler);
278
- }
279
-
280
- return () => {
281
- this.handlers.get(eventType)?.delete(handler);
282
- };
283
- }
284
-
285
- onAnyMessage(handler: AnyHostSessionEventHandler): () => void {
286
- this.anyMessageHandlers.add(handler);
287
-
288
- if (this.messageBuffer.length > 0) {
289
- this.messageBuffer.forEach((message) => {
290
- handler(message);
291
- });
292
- }
293
-
294
- return () => {
295
- this.anyMessageHandlers.delete(handler);
296
- };
297
- }
298
-
299
- private emit(eventType: string, data: HostSessionWireEvent): void;
300
- private emit(eventType: "connected" | "disconnected", data: null): void;
301
- private emit(eventType: "error", error: unknown): void;
302
- private emit(eventType: "recovering", data: LongPollRecoveryEvent): void;
303
- private emit(
304
- eventType: string,
305
- data: HostSessionWireEvent | null | unknown,
306
- ): void {
307
- const handlers = this.handlers.get(eventType);
308
- if (handlers) {
309
- handlers.forEach((handler) => {
310
- if (eventType === "connected" || eventType === "disconnected") {
311
- (handler as ConnectionEventHandler)();
312
- } else if (eventType === "error") {
313
- (handler as ErrorEventHandler)(data);
314
- } else if (eventType === "recovering") {
315
- (handler as RecoveryEventHandler)(data as LongPollRecoveryEvent);
316
- } else {
317
- (handler as AnyHostSessionEventHandler)(data as HostSessionWireEvent);
318
- }
319
- });
320
- }
321
-
322
- if (
323
- eventType !== "connected" &&
324
- eventType !== "disconnected" &&
325
- eventType !== "error" &&
326
- eventType !== "recovering"
327
- ) {
328
- this.messageBuffer.push(data as HostSessionWireEvent);
329
-
330
- this.anyMessageHandlers.forEach((handler) => {
331
- handler(data as HostSessionWireEvent);
332
- });
333
- }
334
- }
335
-
336
- disconnect() {
337
- const activeConnection = this.activeConnection;
338
- if (activeConnection) {
339
- this.notifyDisconnect(activeConnection, true);
340
- }
341
-
342
- if (this.abortController) {
343
- this.abortController.abort();
344
- this.abortController = null;
345
- }
346
- this.activeConnection = null;
347
- this.unregisterPageHideHandler();
348
- this.isConnected = false;
349
- this.handlers.clear();
350
- this.anyMessageHandlers.clear();
351
- this.messageBuffer = [];
352
- }
353
-
354
- isStreamConnected(): boolean {
355
- return this.isConnected;
356
- }
357
-
358
- private async retryRecovery(
359
- error: unknown,
360
- attempt: number,
361
- ): Promise<boolean> {
362
- const recovery = getSessionRecoveryDetails(error);
363
- if (!recovery || attempt > this.recoveryRetryMaxAttempts) {
364
- return false;
365
- }
366
- this.isConnected = false;
367
- this.emit("disconnected", null);
368
- this.emit("recovering", { ...recovery, attempt });
369
- await this.recoveryRetrySleep(recovery.retryAfterMs);
370
- return this.abortController?.signal.aborted !== true;
371
- }
372
-
373
- private notifyDisconnect(
374
- connection: ActiveConnection,
375
- _keepalive: boolean,
376
- ): void {
377
- const request = this.transport.disconnectSessionEvents({
378
- sessionId: connection.sessionId,
379
- clientId: this.clientId,
380
- connectionAttemptId: connection.connectionAttemptId,
381
- playerId: connection.playerId,
382
- });
383
-
384
- void request.catch((error) => {
385
- this.logger.error(
386
- "[LongPollSessionManager] Failed to notify disconnect:",
387
- error,
388
- );
389
- });
390
- }
391
-
392
- private registerPageHideHandler(): void {
393
- if (typeof window === "undefined" || this.pageHideHandler) {
394
- return;
395
- }
396
-
397
- this.pageHideHandler = () => {
398
- if (this.activeConnection) {
399
- this.notifyDisconnect(this.activeConnection, true);
400
- }
401
- };
402
-
403
- window.addEventListener("pagehide", this.pageHideHandler);
404
- }
405
-
406
- private unregisterPageHideHandler(): void {
407
- if (typeof window === "undefined" || !this.pageHideHandler) {
408
- return;
409
- }
410
-
411
- window.removeEventListener("pagehide", this.pageHideHandler);
412
- this.pageHideHandler = null;
413
- }
414
- }
415
-
416
- export { LongPollSessionManager as SSEManager };
@@ -1,184 +0,0 @@
1
- import { createStore } from "zustand/vanilla";
2
- import { subscribeWithSelector } from "zustand/middleware";
3
- import type { StoreApi } from "zustand/vanilla";
4
- import type { PluginStateSnapshot as UiSdkPluginStateSnapshot } from "@dreamboard-games/sdk/runtime/runtime-api";
5
- import type { LoggerLike } from "./logger.js";
6
- import { consoleLogger } from "./logger.js";
7
- import type { HostSessionTransport } from "./host-session-transport.js";
8
- import { defaultHostSessionTransport } from "./host-session-transport.js";
9
- import type { GameplayViewport } from "./session-model.js";
10
- import {
11
- getGameplayViewport,
12
- getSessionContext,
13
- resolveControllablePlayerIds,
14
- } from "./session-model.js";
15
- import type { SSEManagerLike } from "./session-live-runtime.js";
16
- import { createSessionIngressController } from "./session-ingress-controller.js";
17
- import type { SessionIngressControllerActions } from "./session-ingress-controller.js";
18
- import {
19
- createInitialUnifiedSessionState,
20
- type UnifiedSessionState,
21
- } from "./session-state-reducer.js";
22
- import {
23
- selectBootstrapStatus,
24
- selectGameplayViewModel,
25
- selectGameplayViewport,
26
- selectHistory,
27
- selectLobbyViewModel,
28
- selectPluginSnapshot,
29
- selectSessionContext,
30
- selectSessionError,
31
- selectSessionType,
32
- } from "./session-projection.js";
33
-
34
- export type {
35
- GameplayViewport,
36
- HistoryState,
37
- SessionContext,
38
- SessionIdentity,
39
- SessionPhase,
40
- UnifiedSessionModel,
41
- } from "./session-model.js";
42
- export type {
43
- BootstrapStatus,
44
- GameplayViewModel,
45
- LobbyViewModel,
46
- } from "./session-projection.js";
47
- export type { SSEManagerLike } from "./session-live-runtime.js";
48
- export type {
49
- ActivityState,
50
- ConnectionState,
51
- ConnectionRecoveryState,
52
- DebugState,
53
- HostFeedback,
54
- HostFeedbackPayload,
55
- HostFeedbackType,
56
- Notification,
57
- NotificationPayload,
58
- NotificationType,
59
- SSEEventEntry,
60
- UnifiedSessionState,
61
- } from "./session-state-reducer.js";
62
-
63
- export type PluginStateSnapshot = UiSdkPluginStateSnapshot;
64
- export type GameplayState = GameplayViewport;
65
-
66
- export interface UnifiedSessionActions extends SessionIngressControllerActions {
67
- getPluginSnapshot: () => PluginStateSnapshot;
68
- getRenderableGameplay: () => GameplayViewport | null;
69
- }
70
-
71
- export type UnifiedSessionStore = UnifiedSessionState & UnifiedSessionActions;
72
-
73
- export interface CreateUnifiedSessionStoreOptions {
74
- createSseManager: () => SSEManagerLike;
75
- transport?: HostSessionTransport;
76
- logger?: LoggerLike;
77
- fallbackToAllSeatsWhenUserIdMissing?: boolean;
78
- }
79
-
80
- let sseEventIdCounter = 0;
81
- let notificationIdCounter = 0;
82
- const EMPTY_ARRAY: never[] = [];
83
-
84
- function generateNotificationId(): string {
85
- return `notif-${++notificationIdCounter}-${Date.now()}`;
86
- }
87
-
88
- export function createUnifiedSessionStore(
89
- options: CreateUnifiedSessionStoreOptions,
90
- ): StoreApi<UnifiedSessionStore> {
91
- const logger = options.logger ?? consoleLogger;
92
- const transport = options.transport ?? defaultHostSessionTransport;
93
- const fallbackToAllSeatsWhenUserIdMissing =
94
- options.fallbackToAllSeatsWhenUserIdMissing ?? false;
95
-
96
- return createStore<UnifiedSessionStore>()(
97
- subscribeWithSelector((_, get, store) => {
98
- const controller = createSessionIngressController({
99
- store,
100
- createSseManager: options.createSseManager,
101
- transport,
102
- logger,
103
- fallbackToAllSeatsWhenUserIdMissing,
104
- reducerEnvironment: {
105
- fallbackToAllSeatsWhenUserIdMissing,
106
- nextEventId: () => ++sseEventIdCounter,
107
- nextNotificationId: generateNotificationId,
108
- nowMs: () => Date.now(),
109
- nowIso: () => new Date().toISOString(),
110
- },
111
- });
112
-
113
- return {
114
- ...createInitialUnifiedSessionState(),
115
- ...controller,
116
- getPluginSnapshot: () =>
117
- selectPluginSnapshot(get(), fallbackToAllSeatsWhenUserIdMissing),
118
- getRenderableGameplay: () => getGameplayViewport(get().session),
119
- };
120
- }),
121
- );
122
- }
123
-
124
- export const unifiedSessionSelectors = {
125
- sessionType: selectSessionType,
126
- bootstrapStatus: selectBootstrapStatus,
127
- sessionContext: selectSessionContext,
128
- lobby: selectLobbyViewModel,
129
- gameplay: selectGameplayViewModel,
130
- gameplayViewport: selectGameplayViewport,
131
- hasGameplayPayload: (s: UnifiedSessionStore) =>
132
- getGameplayViewport(s.session) !== null,
133
- history: selectHistory,
134
- isConnected: (s: UnifiedSessionStore) => s.connection.isConnected,
135
- connectionError: (s: UnifiedSessionStore) => s.connection.error,
136
- connectionRecovery: (s: UnifiedSessionStore) => s.connection.recovery,
137
- isLoading: (s: UnifiedSessionStore) =>
138
- s.session.type === "loading" ||
139
- s.session.type === "gameplayLoading" ||
140
- s.connection.recovery.active,
141
- error: selectSessionError,
142
- sessionId: (s: UnifiedSessionStore) =>
143
- getSessionContext(s.session)?.identity.sessionId ?? null,
144
- shortCode: (s: UnifiedSessionStore) =>
145
- getSessionContext(s.session)?.identity.shortCode ?? "",
146
- gameId: (s: UnifiedSessionStore) =>
147
- getSessionContext(s.session)?.identity.gameId ?? null,
148
- seats: (s: UnifiedSessionStore) =>
149
- getSessionContext(s.session)?.seats ?? EMPTY_ARRAY,
150
- canStart: (s: UnifiedSessionStore) =>
151
- getSessionContext(s.session)?.canStart ?? false,
152
- hostActor: (s: UnifiedSessionStore) =>
153
- getSessionContext(s.session)?.hostActor ?? null,
154
- isSessionHost: (s: UnifiedSessionStore) =>
155
- selectLobbyViewModel(s)?.isSessionHost ?? false,
156
- controllablePlayerIds: (s: UnifiedSessionStore) => {
157
- const context = getSessionContext(s.session);
158
- return context
159
- ? resolveControllablePlayerIds(
160
- context.switchablePlayerIds,
161
- context.seats,
162
- context.userId,
163
- )
164
- : EMPTY_ARRAY;
165
- },
166
- controllingPlayerId: (s: UnifiedSessionStore) =>
167
- s.session.type === "gameplay" ? s.session.perspective.playerId : null,
168
- currentPlayerId: (s: UnifiedSessionStore) =>
169
- s.session.type === "gameplay"
170
- ? s.session.perspective.playerId
171
- : s.session.type === "gameplayLoading"
172
- ? s.session.requestedPlayerId
173
- : s.session.type === "lobby"
174
- ? s.session.preferredPlayerId
175
- : null,
176
- userId: (s: UnifiedSessionStore) =>
177
- getSessionContext(s.session)?.userId ?? null,
178
- notifications: (s: UnifiedSessionStore) => s.activity.notifications,
179
- hostFeedback: (s: UnifiedSessionStore) => s.activity.hostFeedback,
180
- sseEvents: (s: UnifiedSessionStore) => s.debug.sseEvents,
181
- syncId: (s: UnifiedSessionStore) => s.activity.syncId,
182
- activity: (s: UnifiedSessionStore) => s.activity,
183
- debug: (s: UnifiedSessionStore) => s.debug,
184
- };