@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.
- package/README.md +179 -22
- package/dist/{chunk-C6UAT6EH.js → chunk-N7XPNNUI.js} +9 -12
- package/dist/chunk-N7XPNNUI.js.map +1 -0
- package/dist/chunk-SEGVTWSK.js +44 -0
- package/dist/{chunk-RS7UXJZV.js → chunk-TAQKH67O.js} +21300 -35881
- package/dist/chunk-TAQKH67O.js.map +1 -0
- package/dist/{global-config-AGFBDFYD.js → global-config-S4ZIPECE.js} +3 -3
- package/dist/index.js +415 -37
- package/dist/index.js.map +1 -1
- package/dist/internal.js +3 -4
- package/dist/{agent-verifier/keychain-backend-TNOPQV3Z.mjs → keychain-backend-HDF4TZDL.js} +2 -1
- package/dist/{agent-verifier/prompt-3BAINGAQ.mjs → prompt-NDV3AE5L.js} +2 -1
- package/package.json +6 -6
- package/skills/dreamboard/references/building-your-first-game.md +510 -0
- package/skills/dreamboard/references/cli.md +104 -0
- package/skills/dreamboard/references/game-interface.md +548 -0
- package/skills/dreamboard/references/manifest-authoring.md +597 -0
- package/skills/dreamboard/references/quickstart.md +66 -0
- package/skills/dreamboard/references/reducer.md +864 -0
- package/skills/dreamboard/references/rule-authoring.md +147 -0
- package/skills/dreamboard/references/testing.md +249 -0
- package/skills/dreamboard/scripts/events-extract.mjs +218 -0
- package/dist/agent-verifier/agent-workspace-verifier.mjs +0 -227
- package/dist/agent-verifier/chunk-2E5P5NWG.mjs +0 -835
- package/dist/agent-verifier/chunk-2GBBP27W.mjs +0 -301
- package/dist/agent-verifier/chunk-2QMNAVV4.mjs +0 -14522
- package/dist/agent-verifier/chunk-2SZHMP6F.mjs +0 -264
- package/dist/agent-verifier/chunk-4WD3YU2E.mjs +0 -166
- package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
- package/dist/agent-verifier/chunk-6A5HRJMQ.mjs +0 -3174
- package/dist/agent-verifier/chunk-6UUJEYDV.mjs +0 -213
- package/dist/agent-verifier/chunk-7653FPGJ.mjs +0 -381
- package/dist/agent-verifier/chunk-7E65UQLY.mjs +0 -38
- package/dist/agent-verifier/chunk-BVVNBJM4.mjs +0 -221
- package/dist/agent-verifier/chunk-CEDUHGNH.mjs +0 -74
- package/dist/agent-verifier/chunk-CEQ2VJWN.mjs +0 -149
- package/dist/agent-verifier/chunk-CFU5EWIC.mjs +0 -69
- package/dist/agent-verifier/chunk-CJEEA6NJ.mjs +0 -730
- package/dist/agent-verifier/chunk-EIQWDQWJ.mjs +0 -186
- package/dist/agent-verifier/chunk-EOQIV6PS.mjs +0 -649
- package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
- package/dist/agent-verifier/chunk-HJFQDSTU.mjs +0 -225
- package/dist/agent-verifier/chunk-JH22JNYD.mjs +0 -1681
- package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
- package/dist/agent-verifier/chunk-LM3OZLZG.mjs +0 -48
- package/dist/agent-verifier/chunk-MINCYHXN.mjs +0 -106
- package/dist/agent-verifier/chunk-MRCUP5SW.mjs +0 -128
- package/dist/agent-verifier/chunk-RBDDIIPM.mjs +0 -19
- package/dist/agent-verifier/chunk-SHUMAVAP.mjs +0 -59
- package/dist/agent-verifier/chunk-SYPLYRGB.mjs +0 -2812
- package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
- package/dist/agent-verifier/chunk-VYJTHSYR.mjs +0 -44
- package/dist/agent-verifier/chunk-XYDL7GY6.mjs +0 -10
- package/dist/agent-verifier/compile-5QSPIOUT.mjs +0 -313
- package/dist/agent-verifier/global-config-WX3ZZIVU.mjs +0 -17
- package/dist/agent-verifier/local-files-MTPLP62S.mjs +0 -46
- package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
- package/dist/agent-verifier/materialize-workspace-FKALAE2T.mjs +0 -90
- package/dist/agent-verifier/project-state-7GR6BQTQ.mjs +0 -32
- package/dist/agent-verifier/reducer-bundle-preflight-C73LEXI2.mjs +0 -23
- package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
- package/dist/agent-verifier/reducer-native-test-harness-GMWBUISX.mjs +0 -53
- package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
- package/dist/agent-verifier/sync-3DUQH32H.mjs +0 -594
- package/dist/agent-verifier/test-P4U5INTD.mjs +0 -356
- package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
- package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
- package/dist/agent-verifier/workspace-dependencies-HZ6VVS4G.mjs +0 -14
- package/dist/chunk-2H7UOFLK.js +0 -11
- package/dist/chunk-7FOO4AJI.js +0 -50
- package/dist/chunk-7FOO4AJI.js.map +0 -1
- package/dist/chunk-C6UAT6EH.js.map +0 -1
- package/dist/chunk-RS7UXJZV.js.map +0 -1
- package/dist/internal.d.ts +0 -311
- package/dist/keychain-backend-JHTXAKWC.js +0 -135
- package/dist/prompt-GMZABCJC.js +0 -756
- package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
- package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
- package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
- package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
- package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
- package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
- package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
- package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
- package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
- package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
- package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
- package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
- package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
- package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
- package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
- package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
- package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
- package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
- package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
- package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
- package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
- package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
- package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
- package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
- package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
- package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
- package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
- package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
- package/dist/testing-KLSV6CPJ.js +0 -674
- package/dist/testing-KLSV6CPJ.js.map +0 -1
- /package/dist/{chunk-2H7UOFLK.js.map → chunk-SEGVTWSK.js.map} +0 -0
- /package/dist/{global-config-AGFBDFYD.js.map → global-config-S4ZIPECE.js.map} +0 -0
- /package/dist/{keychain-backend-JHTXAKWC.js.map → keychain-backend-HDF4TZDL.js.map} +0 -0
- /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
|
-
};
|