@dreamboard-games/cli 0.1.30-alpha.1 → 0.1.30-alpha.3

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 (163) hide show
  1. package/README.md +179 -22
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +30 -30
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
  4. package/dist/agent-verifier/{chunk-JH22JNYD.mjs → chunk-3UKQVWLV.mjs} +82 -19
  5. package/dist/agent-verifier/chunk-3UKQVWLV.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-4WD3YU2E.mjs → chunk-776W3UGV.mjs} +4 -3
  7. package/dist/agent-verifier/chunk-776W3UGV.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-CJEEA6NJ.mjs → chunk-7WWGFAAU.mjs} +9 -10
  9. package/dist/agent-verifier/chunk-7WWGFAAU.mjs.map +1 -0
  10. package/dist/agent-verifier/{chunk-2SZHMP6F.mjs → chunk-A64ZZUZV.mjs} +6 -9
  11. package/dist/agent-verifier/chunk-A64ZZUZV.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-6A5HRJMQ.mjs → chunk-E7SSWJXJ.mjs} +62 -99
  13. package/dist/agent-verifier/chunk-E7SSWJXJ.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-2GBBP27W.mjs → chunk-F2DIOJJZ.mjs} +1 -0
  15. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-CFU5EWIC.mjs → chunk-G42BGGG2.mjs} +7 -6
  17. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
  18. package/dist/agent-verifier/{chunk-SHUMAVAP.mjs → chunk-H76MT5UR.mjs} +7 -9
  19. package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +1 -0
  20. package/dist/agent-verifier/{chunk-7E65UQLY.mjs → chunk-HGMUAL33.mjs} +3 -2
  21. package/dist/agent-verifier/chunk-HGMUAL33.mjs.map +1 -0
  22. package/dist/agent-verifier/{chunk-LM3OZLZG.mjs → chunk-IAYRNVUC.mjs} +1 -0
  23. package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
  24. package/dist/agent-verifier/{chunk-VYJTHSYR.mjs → chunk-JGT4P4UD.mjs} +2 -1
  25. package/dist/agent-verifier/chunk-JGT4P4UD.mjs.map +1 -0
  26. package/dist/agent-verifier/{chunk-CEDUHGNH.mjs → chunk-LUZ7KE6H.mjs} +8 -3
  27. package/dist/agent-verifier/chunk-LUZ7KE6H.mjs.map +1 -0
  28. package/dist/agent-verifier/{chunk-2E5P5NWG.mjs → chunk-NAK77WXW.mjs} +58 -126
  29. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
  30. package/dist/agent-verifier/{chunk-SYPLYRGB.mjs → chunk-O4YCPU7C.mjs} +116 -15
  31. package/dist/agent-verifier/chunk-O4YCPU7C.mjs.map +1 -0
  32. package/dist/agent-verifier/{chunk-BVVNBJM4.mjs → chunk-S34FRJHS.mjs} +2 -1
  33. package/dist/agent-verifier/chunk-S34FRJHS.mjs.map +1 -0
  34. package/dist/agent-verifier/{chunk-HJFQDSTU.mjs → chunk-SH5JKYOB.mjs} +6 -5
  35. package/dist/agent-verifier/chunk-SH5JKYOB.mjs.map +1 -0
  36. package/dist/agent-verifier/chunk-SKI2ESE5.mjs +44 -0
  37. package/dist/agent-verifier/{chunk-MINCYHXN.mjs → chunk-TAEQKBJB.mjs} +1 -0
  38. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
  39. package/dist/agent-verifier/{chunk-CEQ2VJWN.mjs → chunk-UIOLGH4A.mjs} +2 -1
  40. package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +1 -0
  41. package/dist/agent-verifier/chunk-UIZNWRM6.mjs +2432 -0
  42. package/dist/agent-verifier/chunk-UIZNWRM6.mjs.map +1 -0
  43. package/dist/agent-verifier/{chunk-2QMNAVV4.mjs → chunk-VS573ERH.mjs} +2 -1
  44. package/dist/agent-verifier/chunk-VS573ERH.mjs.map +1 -0
  45. package/dist/agent-verifier/{chunk-EOQIV6PS.mjs → chunk-W3N3QJ4V.mjs} +75 -100
  46. package/dist/agent-verifier/chunk-W3N3QJ4V.mjs.map +1 -0
  47. package/dist/agent-verifier/{chunk-EIQWDQWJ.mjs → chunk-XGWCY624.mjs} +11 -12
  48. package/dist/agent-verifier/chunk-XGWCY624.mjs.map +1 -0
  49. package/dist/agent-verifier/{chunk-7653FPGJ.mjs → chunk-XQXDOBYB.mjs} +3 -2
  50. package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +1 -0
  51. package/dist/agent-verifier/{chunk-MRCUP5SW.mjs → chunk-YE7UAO3T.mjs} +1 -0
  52. package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
  53. package/dist/agent-verifier/{chunk-RBDDIIPM.mjs → chunk-ZEELHSY3.mjs} +1 -0
  54. package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
  55. package/dist/agent-verifier/{compile-5QSPIOUT.mjs → compile-TEQVA46V.mjs} +24 -25
  56. package/dist/agent-verifier/compile-TEQVA46V.mjs.map +1 -0
  57. package/dist/agent-verifier/{global-config-WX3ZZIVU.mjs → global-config-Y2NTSK4R.mjs} +6 -5
  58. package/dist/{keychain-backend-JHTXAKWC.js → agent-verifier/keychain-backend-SPQWGKZN.mjs} +2 -2
  59. package/dist/agent-verifier/keychain-backend-SPQWGKZN.mjs.map +1 -0
  60. package/dist/agent-verifier/{local-files-MTPLP62S.mjs → local-files-JFOQQZDL.mjs} +10 -11
  61. package/dist/agent-verifier/local-files-JFOQQZDL.mjs.map +1 -0
  62. package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs +10 -0
  63. package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs.map +1 -0
  64. package/dist/agent-verifier/{materialize-workspace-FKALAE2T.mjs → materialize-workspace-ZAVGQQSF.mjs} +17 -18
  65. package/dist/agent-verifier/materialize-workspace-ZAVGQQSF.mjs.map +1 -0
  66. package/dist/agent-verifier/{project-state-7GR6BQTQ.mjs → project-state-K576C2TE.mjs} +3 -2
  67. package/dist/agent-verifier/project-state-K576C2TE.mjs.map +1 -0
  68. package/dist/{prompt-GMZABCJC.js → agent-verifier/prompt-MJRJMOGQ.mjs} +2 -2
  69. package/dist/agent-verifier/prompt-MJRJMOGQ.mjs.map +1 -0
  70. package/dist/agent-verifier/{reducer-bundle-preflight-C73LEXI2.mjs → reducer-bundle-preflight-LXNJUBKL.mjs} +6 -9
  71. package/dist/agent-verifier/reducer-bundle-preflight-LXNJUBKL.mjs.map +1 -0
  72. package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs +11 -0
  73. package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs.map +1 -0
  74. package/dist/agent-verifier/{reducer-native-test-harness-GMWBUISX.mjs → reducer-native-test-harness-CHX5MBL5.mjs} +14 -17
  75. package/dist/agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs.map +1 -0
  76. package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs +27 -0
  77. package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs.map +1 -0
  78. package/dist/agent-verifier/{sync-3DUQH32H.mjs → sync-THAI546U.mjs} +31 -37
  79. package/dist/agent-verifier/sync-THAI546U.mjs.map +1 -0
  80. package/dist/agent-verifier/{test-P4U5INTD.mjs → test-AFAQFKOB.mjs} +28 -31
  81. package/dist/agent-verifier/test-AFAQFKOB.mjs.map +1 -0
  82. package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs +10 -0
  83. package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs.map +1 -0
  84. package/dist/agent-verifier/{workspace-dependencies-HZ6VVS4G.mjs → workspace-dependencies-NOOQBK6I.mjs} +5 -4
  85. package/dist/agent-verifier/workspace-dependencies-NOOQBK6I.mjs.map +1 -0
  86. package/dist/{chunk-C6UAT6EH.js → chunk-N7XPNNUI.js} +9 -12
  87. package/dist/chunk-N7XPNNUI.js.map +1 -0
  88. package/dist/chunk-SEGVTWSK.js +44 -0
  89. package/dist/chunk-SEGVTWSK.js.map +1 -0
  90. package/dist/{chunk-RS7UXJZV.js → chunk-TAQKH67O.js} +21300 -35881
  91. package/dist/chunk-TAQKH67O.js.map +1 -0
  92. package/dist/{global-config-AGFBDFYD.js → global-config-S4ZIPECE.js} +3 -3
  93. package/dist/global-config-S4ZIPECE.js.map +1 -0
  94. package/dist/index.js +415 -37
  95. package/dist/index.js.map +1 -1
  96. package/dist/internal.js +3 -4
  97. package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
  98. package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
  99. package/package.json +8 -7
  100. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  101. package/skills/dreamboard/references/cli.md +104 -0
  102. package/skills/dreamboard/references/game-interface.md +548 -0
  103. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  104. package/skills/dreamboard/references/quickstart.md +66 -0
  105. package/skills/dreamboard/references/reducer.md +864 -0
  106. package/skills/dreamboard/references/rule-authoring.md +147 -0
  107. package/skills/dreamboard/references/testing.md +249 -0
  108. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  109. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  110. package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
  111. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  112. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  113. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  114. package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
  115. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  116. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  117. package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
  118. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  119. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  120. package/dist/chunk-2H7UOFLK.js +0 -11
  121. package/dist/chunk-7FOO4AJI.js +0 -50
  122. package/dist/chunk-7FOO4AJI.js.map +0 -1
  123. package/dist/chunk-C6UAT6EH.js.map +0 -1
  124. package/dist/chunk-RS7UXJZV.js.map +0 -1
  125. package/dist/internal.d.ts +0 -311
  126. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  127. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  128. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  129. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  130. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  131. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  132. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  133. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  134. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  135. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  136. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  137. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  138. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  139. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  140. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  141. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  142. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  143. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  144. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  145. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  146. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  147. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  148. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  149. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  150. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  151. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  152. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  153. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  154. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  155. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  156. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  157. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  158. package/dist/testing-KLSV6CPJ.js +0 -674
  159. package/dist/testing-KLSV6CPJ.js.map +0 -1
  160. /package/dist/{chunk-2H7UOFLK.js.map → agent-verifier/chunk-SKI2ESE5.mjs.map} +0 -0
  161. /package/dist/{global-config-AGFBDFYD.js.map → agent-verifier/global-config-Y2NTSK4R.mjs.map} +0 -0
  162. /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
  163. /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
- };