@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,626 +0,0 @@
1
- import {
2
- GameplayAuthorityRecoveringError,
3
- connectGameplayAuthority,
4
- type CommandAcceptedFrame,
5
- type GameplayAuthorityClient,
6
- type GameplayAuthorityWebSocketFactory,
7
- type ServerGameplayFrame,
8
- } from "@dreamboard-games/gameplay-authority-client";
9
- import fastJsonPatch, { type Operation } from "fast-json-patch";
10
- import {
11
- createGameplayCapability,
12
- type CreateGameplayCapabilityData,
13
- type GameplayCapabilityResponse,
14
- type HostPlayerGameplayView,
15
- type HostSessionContext,
16
- } from "@dreamboard-games/api-client";
17
- import {
18
- defaultHostSessionTransport,
19
- type HostSessionTransport,
20
- } from "./host-session-transport.js";
21
- import type {
22
- HostActionSubmitWireResponse,
23
- HostSessionWireSnapshot,
24
- HostSessionWireEvent,
25
- } from "./session-ingress.js";
26
- import type { GameplayViewport, SessionContext } from "./session-model.js";
27
-
28
- export type GameplayCapabilityRequester = (
29
- options: Parameters<typeof createGameplayCapability>[0],
30
- ) => ReturnType<typeof createGameplayCapability>;
31
-
32
- export type GameplayAuthorityAcceptedMapper = (input: {
33
- frame: CommandAcceptedFrame;
34
- capability: GameplayCapabilityResponse;
35
- submit: SubmitAuthorityInteractionInput;
36
- wireContext?: HostSessionContext | null;
37
- }) => HostActionSubmitWireResponse | Promise<HostActionSubmitWireResponse>;
38
-
39
- export interface SubmitAuthorityInteractionInput {
40
- sessionId: string;
41
- playerId: string;
42
- interactionId: string;
43
- expectedVersion: number;
44
- actionSetVersion: string;
45
- params: unknown;
46
- clientActionId?: string | null;
47
- }
48
-
49
- export interface GameplayAuthorityTransportOptions {
50
- fallbackTransport?: HostSessionTransport;
51
- capabilityRequester?: GameplayCapabilityRequester;
52
- webSocketFactory?: GameplayAuthorityWebSocketFactory;
53
- mapAcceptedFrame?: GameplayAuthorityAcceptedMapper;
54
- getCurrentSessionContext?: () => SessionContext | null;
55
- getCurrentGameplay?: () => GameplayViewport | null;
56
- openTimeoutMs?: number;
57
- requestTimeoutMs?: number;
58
- randomClientActionId?: () => string;
59
- }
60
-
61
- const DEFAULT_OPEN_TIMEOUT_MS = 5_000;
62
- const DEFAULT_REQUEST_TIMEOUT_MS = 10_000;
63
- const { applyPatch, deepClone } = fastJsonPatch;
64
-
65
- export function createGameplayAuthorityTransport(
66
- options: GameplayAuthorityTransportOptions = {},
67
- ): HostSessionTransport {
68
- const fallback = options.fallbackTransport ?? defaultHostSessionTransport;
69
- const requester = options.capabilityRequester ?? createGameplayCapability;
70
- const webSocketFactory = options.webSocketFactory;
71
- const openTimeoutMs = options.openTimeoutMs ?? DEFAULT_OPEN_TIMEOUT_MS;
72
- const requestTimeoutMs =
73
- options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
74
- const connections = createGameplayAuthorityConnectionRegistry({
75
- requester,
76
- webSocketFactory,
77
- openTimeoutMs,
78
- requestTimeoutMs,
79
- });
80
-
81
- return {
82
- ...fallback,
83
- async submitInteraction(input) {
84
- const clientActionId =
85
- input.clientActionId ??
86
- options.randomClientActionId?.() ??
87
- crypto.randomUUID();
88
- for (let attempt = 0; attempt < 2; attempt += 1) {
89
- const connection = await connections.get({
90
- sessionId: input.sessionId,
91
- playerId: input.playerId,
92
- });
93
- try {
94
- const frame = await connection.client
95
- .submitCommand({
96
- clientActionId,
97
- expectedVersion: input.expectedVersion,
98
- actionSetVersion: input.actionSetVersion,
99
- interactionId: input.interactionId,
100
- inputs: asInputs(input.params),
101
- })
102
- .catch((error: unknown) => {
103
- throw mapGameplayAuthorityRecoveringError(error);
104
- });
105
- if (frame.type === "command.rejected") {
106
- return {
107
- success: false,
108
- accepted: false,
109
- version: frame.currentVersion ?? input.expectedVersion,
110
- actionSetVersion: input.actionSetVersion,
111
- errorCode: frame.errorCode,
112
- message: frame.message,
113
- clientActionId: frame.clientActionId ?? clientActionId,
114
- };
115
- }
116
- if (frame.type !== "command.accepted") {
117
- throw new Error("Unexpected gameplay authority frame.");
118
- }
119
-
120
- const mapAcceptedFrame =
121
- options.mapAcceptedFrame ??
122
- (options.getCurrentSessionContext
123
- ? async (
124
- mapperInput: Parameters<GameplayAuthorityAcceptedMapper>[0],
125
- ) =>
126
- mapGameplayAuthorityAcceptedFrame({
127
- ...mapperInput,
128
- context: options.getCurrentSessionContext?.() ?? null,
129
- wireContext: await loadFreshAuthorityContext({
130
- fallback,
131
- sessionId: input.sessionId,
132
- playerId: input.playerId,
133
- }),
134
- })
135
- : null);
136
- if (!mapAcceptedFrame) {
137
- throw new Error(
138
- "Gameplay authority accepted frame mapping is not configured.",
139
- );
140
- }
141
-
142
- return mapAcceptedFrame({
143
- frame,
144
- capability: connection.capability,
145
- submit: { ...input, clientActionId },
146
- });
147
- } catch (error) {
148
- connections.close(connection);
149
- if (attempt === 0 && isGameplaySocketClosedError(error)) {
150
- continue;
151
- }
152
- throw error;
153
- }
154
- }
155
- throw new Error("Gameplay authority submit retry exhausted.");
156
- },
157
- async subscribeToSessionEvents(input) {
158
- if (!input.playerId) {
159
- return fallback.subscribeToSessionEvents(input);
160
- }
161
- return {
162
- stream: streamGameplayAuthorityEvents({
163
- input,
164
- connections,
165
- getCurrentSessionContext: options.getCurrentSessionContext,
166
- fallback,
167
- }),
168
- };
169
- },
170
- async restoreHistory(input) {
171
- const context = options.getCurrentSessionContext?.() ?? null;
172
- const gameplay = options.getCurrentGameplay?.() ?? null;
173
- const entry = context?.history?.entries.find(
174
- (candidate) => candidate.id === input.entryId,
175
- );
176
- if (!context) {
177
- throw new Error("Cannot restore history without session context.");
178
- }
179
- const playerId = context.switchablePlayerIds[0];
180
- if (!gameplay) {
181
- throw new Error("Cannot restore history without current gameplay.");
182
- }
183
- if (!playerId) {
184
- throw new Error("Cannot restore history without a session player.");
185
- }
186
- if (!entry) {
187
- throw new Error("Cannot restore unknown history entry.");
188
- }
189
-
190
- const restoreId = options.randomClientActionId?.() ?? crypto.randomUUID();
191
- const connection = await connections.get({
192
- sessionId: input.sessionId,
193
- playerId,
194
- });
195
-
196
- try {
197
- const frame = await connection.client
198
- .restoreHistory({
199
- restoreId,
200
- targetGeneration: entry.generation,
201
- targetVersion: entry.version,
202
- })
203
- .catch((error: unknown) => {
204
- throw mapGameplayAuthorityRecoveringError(error);
205
- });
206
- if (frame.type === "history.restoreRejected") {
207
- throw new Error(frame.message);
208
- }
209
- if (frame.type !== "history.restored") {
210
- throw new Error("Unexpected gameplay authority restore frame.");
211
- }
212
- return mapGameplayAuthorityRestoredFrame({
213
- frame,
214
- context,
215
- wireContext: await loadFreshAuthorityContext({
216
- fallback,
217
- sessionId: input.sessionId,
218
- playerId,
219
- }),
220
- });
221
- } catch (error) {
222
- connections.close(connection);
223
- throw error;
224
- }
225
- },
226
- async validateInteraction(input) {
227
- if (!options.getCurrentGameplay) {
228
- return fallback.validateInteraction(input);
229
- }
230
- return validateFromCurrentGameplay({
231
- input,
232
- gameplay: options.getCurrentGameplay(),
233
- });
234
- },
235
- async disconnectSessionEvents(input) {
236
- if (input.playerId) {
237
- connections.closeByScope(input.sessionId, input.playerId);
238
- return;
239
- }
240
- connections.closeByScope(input.sessionId);
241
- await fallback.disconnectSessionEvents(input);
242
- },
243
- };
244
- }
245
-
246
- interface GameplayAuthorityConnection {
247
- key: string;
248
- sessionId: string;
249
- playerId: string;
250
- capability: GameplayCapabilityResponse;
251
- client: GameplayAuthorityClient;
252
- }
253
-
254
- function createGameplayAuthorityConnectionRegistry(options: {
255
- requester: GameplayCapabilityRequester;
256
- webSocketFactory?: GameplayAuthorityWebSocketFactory;
257
- openTimeoutMs: number;
258
- requestTimeoutMs: number;
259
- }) {
260
- const connections = new Map<string, Promise<GameplayAuthorityConnection>>();
261
-
262
- return {
263
- async get(input: {
264
- sessionId: string;
265
- playerId: string;
266
- }): Promise<GameplayAuthorityConnection> {
267
- const key = gameplayAuthorityConnectionKey(input);
268
- const existing = connections.get(key);
269
- if (existing) {
270
- return existing;
271
- }
272
-
273
- const pending = openGameplayAuthorityConnection({
274
- ...input,
275
- key,
276
- ...options,
277
- });
278
- connections.set(key, pending);
279
- try {
280
- return await pending;
281
- } catch (error) {
282
- connections.delete(key);
283
- throw error;
284
- }
285
- },
286
- close(connection: GameplayAuthorityConnection): void {
287
- connections.delete(connection.key);
288
- connection.client.close();
289
- },
290
- closeByScope(sessionId: string, playerId?: string): void {
291
- for (const [key, pending] of connections) {
292
- if (!keyMatchesScope(key, sessionId, playerId)) continue;
293
- connections.delete(key);
294
- void pending
295
- .then((connection) => connection.client.close())
296
- .catch(() => undefined);
297
- }
298
- },
299
- };
300
- }
301
-
302
- function isGameplaySocketClosedError(error: unknown): boolean {
303
- return error instanceof Error && error.message === "gameplay socket closed";
304
- }
305
-
306
- async function openGameplayAuthorityConnection(input: {
307
- key: string;
308
- sessionId: string;
309
- playerId: string;
310
- requester: GameplayCapabilityRequester;
311
- webSocketFactory?: GameplayAuthorityWebSocketFactory;
312
- openTimeoutMs: number;
313
- requestTimeoutMs: number;
314
- }): Promise<GameplayAuthorityConnection> {
315
- const capability = await requestCapability(input.requester, {
316
- path: { sessionId: input.sessionId, playerId: input.playerId },
317
- });
318
- const client = await connectGameplayAuthority({
319
- websocketUrl: capability.websocketUrl,
320
- capabilityToken: capability.token,
321
- webSocketFactory: input.webSocketFactory,
322
- openTimeoutMs: input.openTimeoutMs,
323
- requestTimeoutMs: input.requestTimeoutMs,
324
- });
325
- return {
326
- key: input.key,
327
- sessionId: input.sessionId,
328
- playerId: input.playerId,
329
- capability,
330
- client,
331
- };
332
- }
333
-
334
- function gameplayAuthorityConnectionKey(input: {
335
- sessionId: string;
336
- playerId: string;
337
- }): string {
338
- return `${input.sessionId}\u0000${input.playerId}`;
339
- }
340
-
341
- function keyMatchesScope(
342
- key: string,
343
- sessionId: string,
344
- playerId?: string,
345
- ): boolean {
346
- const [keySessionId, keyPlayerId] = key.split("\u0000");
347
- return keySessionId === sessionId && (!playerId || keyPlayerId === playerId);
348
- }
349
-
350
- export function mapGameplayAuthorityAcceptedFrame(input: {
351
- frame: CommandAcceptedFrame;
352
- capability: GameplayCapabilityResponse;
353
- submit: SubmitAuthorityInteractionInput;
354
- context: SessionContext | null;
355
- wireContext?: HostSessionContext | null;
356
- }): HostActionSubmitWireResponse {
357
- if (!input.context) {
358
- throw new Error("Cannot map authority update without session context.");
359
- }
360
- if (input.frame.update.kind !== "snapshot") {
361
- throw new Error("Authority delta updates are not supported yet.");
362
- }
363
- const gameplay = input.frame.update.view as HostPlayerGameplayView;
364
- return {
365
- success: true,
366
- accepted: true,
367
- durabilityStatus: "COMMITTED",
368
- version: input.frame.version,
369
- actionSetVersion: gameplay.actionSetVersion,
370
- clientActionId: input.frame.clientActionId,
371
- update: {
372
- type: "session.gameplayUpdated",
373
- context: input.wireContext ?? toHostSessionContext(input.context),
374
- gameplay,
375
- causation: { clientActionId: input.frame.clientActionId },
376
- },
377
- };
378
- }
379
-
380
- function toHostSessionContext(context: SessionContext): HostSessionContext {
381
- return {
382
- sessionId: context.identity.sessionId,
383
- shortCode: context.identity.shortCode,
384
- phase: "gameplay",
385
- status: context.status,
386
- hostActor: context.hostActor,
387
- gameSource: context.gameSource,
388
- setupProfileId: context.setupProfileId ?? undefined,
389
- switchablePlayerIds: context.switchablePlayerIds,
390
- history: context.history ?? undefined,
391
- };
392
- }
393
-
394
- async function* streamGameplayAuthorityEvents(options: {
395
- input: Parameters<HostSessionTransport["subscribeToSessionEvents"]>[0];
396
- connections: ReturnType<typeof createGameplayAuthorityConnectionRegistry>;
397
- getCurrentSessionContext?: () => SessionContext | null;
398
- fallback: HostSessionTransport;
399
- }): AsyncGenerator<HostSessionWireEvent | null | undefined> {
400
- const playerId = options.input.playerId;
401
- if (!playerId) return;
402
- const connection = await options.connections.get({
403
- sessionId: options.input.sessionId,
404
- playerId,
405
- });
406
- const frameIterator = connection.client.frames(options.input.signal);
407
- let lastProjectedView: HostPlayerGameplayView | null = null;
408
- let lastGeneration: number | null = null;
409
- let lastVersion: number | null = null;
410
-
411
- try {
412
- connection.client.resume();
413
-
414
- for await (const frame of frameIterator) {
415
- if (frame.type === "authority.recovering") {
416
- throw recoveringProblem(frame.message, frame.retryAfterMs);
417
- }
418
- if (frame.type === "session.snapshot") {
419
- lastProjectedView = frame.update.view as HostPlayerGameplayView;
420
- lastGeneration = frame.generation;
421
- lastVersion = frame.version;
422
- yield mapGameplayAuthoritySnapshotFrame({
423
- frame,
424
- context: options.getCurrentSessionContext?.() ?? null,
425
- wireContext: await loadFreshAuthorityContext({
426
- fallback: options.fallback,
427
- sessionId: options.input.sessionId,
428
- playerId,
429
- }),
430
- });
431
- continue;
432
- }
433
- if (frame.type === "session.delta") {
434
- const delta = applyGameplayAuthorityDelta({
435
- frame,
436
- previousView: lastProjectedView,
437
- previousGeneration: lastGeneration,
438
- previousVersion: lastVersion,
439
- });
440
- lastProjectedView = delta.view;
441
- lastGeneration = delta.generation;
442
- lastVersion = delta.version;
443
- yield gameplayUpdatedEvent({
444
- context: options.getCurrentSessionContext?.() ?? null,
445
- wireContext: await loadFreshAuthorityContext({
446
- fallback: options.fallback,
447
- sessionId: options.input.sessionId,
448
- playerId,
449
- }),
450
- gameplay: delta.view,
451
- });
452
- }
453
- }
454
- } finally {
455
- await frameIterator.return?.(undefined);
456
- options.connections.close(connection);
457
- }
458
- }
459
-
460
- function mapGameplayAuthoritySnapshotFrame(input: {
461
- frame: Extract<ServerGameplayFrame, { type: "session.snapshot" }>;
462
- context: SessionContext | null;
463
- wireContext?: HostSessionContext | null;
464
- }): HostSessionWireEvent {
465
- if (!input.context) {
466
- throw new Error("Cannot map authority snapshot without session context.");
467
- }
468
- return gameplayUpdatedEvent({
469
- context: input.context,
470
- wireContext: input.wireContext,
471
- gameplay: input.frame.update.view as HostPlayerGameplayView,
472
- });
473
- }
474
-
475
- function mapGameplayAuthorityRestoredFrame(input: {
476
- frame: Extract<ServerGameplayFrame, { type: "history.restored" }>;
477
- context: SessionContext | null;
478
- wireContext?: HostSessionContext | null;
479
- }): HostSessionWireEvent {
480
- if (!input.context) {
481
- throw new Error("Cannot map authority restore without session context.");
482
- }
483
- return gameplayUpdatedEvent({
484
- context: input.context,
485
- wireContext: input.wireContext,
486
- gameplay: input.frame.update.view as HostPlayerGameplayView,
487
- });
488
- }
489
-
490
- function gameplayUpdatedEvent(input: {
491
- context: SessionContext | null;
492
- wireContext?: HostSessionContext | null;
493
- gameplay: HostPlayerGameplayView;
494
- }): HostSessionWireEvent {
495
- if (!input.context) {
496
- throw new Error(
497
- "Cannot map authority gameplay update without session context.",
498
- );
499
- }
500
- return {
501
- type: "session.gameplayUpdated",
502
- context: input.wireContext ?? toHostSessionContext(input.context),
503
- gameplay: input.gameplay,
504
- };
505
- }
506
-
507
- async function loadFreshAuthorityContext(input: {
508
- fallback: HostSessionTransport;
509
- sessionId: string;
510
- playerId: string;
511
- }): Promise<HostSessionWireSnapshot["context"]> {
512
- const snapshot = await input.fallback.loadSessionSnapshot({
513
- sessionId: input.sessionId,
514
- requestedPlayerId: input.playerId,
515
- });
516
- return snapshot.context;
517
- }
518
-
519
- function applyGameplayAuthorityDelta(input: {
520
- frame: Extract<ServerGameplayFrame, { type: "session.delta" }>;
521
- previousView: HostPlayerGameplayView | null;
522
- previousGeneration: number | null;
523
- previousVersion: number | null;
524
- }): {
525
- generation: number;
526
- version: number;
527
- view: HostPlayerGameplayView;
528
- } {
529
- const update = input.frame.update;
530
- if (!input.previousView) {
531
- throw new Error("Authority delta arrived before a base snapshot.");
532
- }
533
- if (
534
- input.previousGeneration !== update.generation ||
535
- input.previousVersion !== update.baseVersion
536
- ) {
537
- throw new Error(
538
- "Authority delta base does not match current gameplay snapshot.",
539
- );
540
- }
541
-
542
- return {
543
- generation: update.generation,
544
- version: update.version,
545
- view: applyPatch(
546
- deepClone(input.previousView),
547
- update.patch as Operation[],
548
- true,
549
- false,
550
- true,
551
- ).newDocument as HostPlayerGameplayView,
552
- };
553
- }
554
-
555
- function validateFromCurrentGameplay(input: {
556
- input: Parameters<HostSessionTransport["validateInteraction"]>[0];
557
- gameplay: GameplayViewport | null;
558
- }) {
559
- const gameplay = input.gameplay;
560
- if (!gameplay) {
561
- return {
562
- valid: false,
563
- errorCode: "runtime-unavailable",
564
- message: "No renderable gameplay snapshot is available.",
565
- };
566
- }
567
-
568
- const descriptor = gameplay.availableInteractions.find(
569
- (candidate) =>
570
- candidate.interactionId === input.input.interactionId ||
571
- candidate.interactionKey === input.input.interactionId,
572
- );
573
- if (!descriptor) {
574
- return {
575
- valid: false,
576
- errorCode: "interaction-unavailable",
577
- message: "Interaction is not available.",
578
- };
579
- }
580
- if (descriptor.availability.status !== "available") {
581
- return {
582
- valid: false,
583
- errorCode: "interaction-unavailable",
584
- message:
585
- descriptor.availability.reason ?? "Interaction is not available.",
586
- };
587
- }
588
- return { valid: true };
589
- }
590
-
591
- async function requestCapability(
592
- requester: GameplayCapabilityRequester,
593
- options: Pick<CreateGameplayCapabilityData, "path">,
594
- ): Promise<GameplayCapabilityResponse> {
595
- const result = await requester(options);
596
- if ("error" in result && result.error) {
597
- throw result.error;
598
- }
599
- if (!result.data) {
600
- throw new Error("Failed to create gameplay capability.");
601
- }
602
- return result.data;
603
- }
604
-
605
- function asInputs(params: unknown): Record<string, unknown> {
606
- return params && typeof params === "object" && !Array.isArray(params)
607
- ? (params as Record<string, unknown>)
608
- : {};
609
- }
610
-
611
- function recoveringProblem(detail: string, retryAfterMs: number) {
612
- return {
613
- title: "Session recovering",
614
- status: 503,
615
- detail,
616
- retryable: true,
617
- context: { retryAfterMs },
618
- };
619
- }
620
-
621
- function mapGameplayAuthorityRecoveringError(error: unknown): unknown {
622
- if (error instanceof GameplayAuthorityRecoveringError) {
623
- return recoveringProblem(error.message, error.retryAfterMs);
624
- }
625
- return error;
626
- }
@@ -1 +0,0 @@
1
- export * from "./components/host-controls.js";
@@ -1 +0,0 @@
1
- export * from "./components/host-feedback.js";