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

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 (156) hide show
  1. package/README.md +179 -22
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +31 -30
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
  4. package/dist/agent-verifier/{chunk-4WD3YU2E.mjs → chunk-3IJBOLGT.mjs} +4 -12
  5. package/dist/agent-verifier/chunk-3IJBOLGT.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-6A5HRJMQ.mjs → chunk-4GU3PCHV.mjs} +62 -99
  7. package/dist/agent-verifier/chunk-4GU3PCHV.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-SYPLYRGB.mjs → chunk-6XRC5PWB.mjs} +119 -310
  9. package/dist/agent-verifier/chunk-6XRC5PWB.mjs.map +1 -0
  10. package/dist/agent-verifier/{chunk-BVVNBJM4.mjs → chunk-COB56ESI.mjs} +2 -1
  11. package/dist/agent-verifier/chunk-COB56ESI.mjs.map +1 -0
  12. package/dist/agent-verifier/{chunk-2GBBP27W.mjs → chunk-F2DIOJJZ.mjs} +1 -0
  13. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
  14. package/dist/agent-verifier/{chunk-CFU5EWIC.mjs → chunk-G42BGGG2.mjs} +7 -6
  15. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-XYDL7GY6.mjs → chunk-H6XDQJ3N.mjs} +1 -0
  17. package/dist/agent-verifier/{chunk-LM3OZLZG.mjs → chunk-IAYRNVUC.mjs} +1 -0
  18. package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
  19. package/dist/agent-verifier/{chunk-2QMNAVV4.mjs → chunk-JZTH3EMV.mjs} +2 -1
  20. package/dist/agent-verifier/chunk-JZTH3EMV.mjs.map +1 -0
  21. package/dist/agent-verifier/chunk-KK47X7RV.mjs +14 -0
  22. package/dist/agent-verifier/chunk-KK47X7RV.mjs.map +1 -0
  23. package/dist/agent-verifier/{chunk-SHUMAVAP.mjs → chunk-M7UVBANQ.mjs} +8 -9
  24. package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
  25. package/dist/agent-verifier/{chunk-JH22JNYD.mjs → chunk-MGXX4WFR.mjs} +87 -22
  26. package/dist/agent-verifier/chunk-MGXX4WFR.mjs.map +1 -0
  27. package/dist/agent-verifier/{chunk-2E5P5NWG.mjs → chunk-NAK77WXW.mjs} +58 -126
  28. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
  29. package/dist/agent-verifier/{chunk-CEQ2VJWN.mjs → chunk-POBFNXD4.mjs} +2 -1
  30. package/dist/agent-verifier/chunk-POBFNXD4.mjs.map +1 -0
  31. package/dist/agent-verifier/{chunk-6UUJEYDV.mjs → chunk-QBAF7EYR.mjs} +1 -0
  32. package/dist/agent-verifier/chunk-QBAF7EYR.mjs.map +1 -0
  33. package/dist/agent-verifier/{chunk-7653FPGJ.mjs → chunk-RHI6S4SU.mjs} +3 -2
  34. package/dist/agent-verifier/chunk-RHI6S4SU.mjs.map +1 -0
  35. package/dist/agent-verifier/{chunk-MINCYHXN.mjs → chunk-TAEQKBJB.mjs} +1 -0
  36. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
  37. package/dist/agent-verifier/{chunk-7E65UQLY.mjs → chunk-TLYGTHXU.mjs} +3 -2
  38. package/dist/agent-verifier/chunk-TLYGTHXU.mjs.map +1 -0
  39. package/dist/agent-verifier/{chunk-EIQWDQWJ.mjs → chunk-UWJIZML3.mjs} +13 -14
  40. package/dist/agent-verifier/chunk-UWJIZML3.mjs.map +1 -0
  41. package/dist/agent-verifier/{chunk-CJEEA6NJ.mjs → chunk-VLOIZDR6.mjs} +15 -31
  42. package/dist/agent-verifier/chunk-VLOIZDR6.mjs.map +1 -0
  43. package/dist/agent-verifier/{chunk-HJFQDSTU.mjs → chunk-W2MDP5ZN.mjs} +6 -5
  44. package/dist/agent-verifier/chunk-W2MDP5ZN.mjs.map +1 -0
  45. package/dist/agent-verifier/{chunk-CEDUHGNH.mjs → chunk-XKCJBIRY.mjs} +2 -1
  46. package/dist/agent-verifier/chunk-XKCJBIRY.mjs.map +1 -0
  47. package/dist/agent-verifier/{chunk-VYJTHSYR.mjs → chunk-YDIOW2BO.mjs} +2 -1
  48. package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +1 -0
  49. package/dist/agent-verifier/{chunk-MRCUP5SW.mjs → chunk-YE7UAO3T.mjs} +1 -0
  50. package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
  51. package/dist/agent-verifier/{chunk-EOQIV6PS.mjs → chunk-YR664DJX.mjs} +111 -116
  52. package/dist/agent-verifier/chunk-YR664DJX.mjs.map +1 -0
  53. package/dist/agent-verifier/{chunk-2SZHMP6F.mjs → chunk-Z6OZWUIZ.mjs} +6 -9
  54. package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +1 -0
  55. package/dist/agent-verifier/{chunk-RBDDIIPM.mjs → chunk-ZEELHSY3.mjs} +1 -0
  56. package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
  57. package/dist/agent-verifier/{compile-5QSPIOUT.mjs → compile-C2VIP6VC.mjs} +27 -27
  58. package/dist/agent-verifier/compile-C2VIP6VC.mjs.map +1 -0
  59. package/dist/agent-verifier/{global-config-WX3ZZIVU.mjs → global-config-XHL7BCKN.mjs} +6 -5
  60. package/dist/agent-verifier/global-config-XHL7BCKN.mjs.map +1 -0
  61. package/dist/agent-verifier/{keychain-backend-TNOPQV3Z.mjs → keychain-backend-A3MRWLPF.mjs} +2 -1
  62. package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +1 -0
  63. package/dist/agent-verifier/{local-files-MTPLP62S.mjs → local-files-ZW52HSVT.mjs} +10 -11
  64. package/dist/agent-verifier/local-files-ZW52HSVT.mjs.map +1 -0
  65. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs +10 -0
  66. package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs.map +1 -0
  67. package/dist/agent-verifier/{materialize-workspace-FKALAE2T.mjs → materialize-workspace-BKZLLFI4.mjs} +20 -20
  68. package/dist/agent-verifier/materialize-workspace-BKZLLFI4.mjs.map +1 -0
  69. package/dist/agent-verifier/{project-state-7GR6BQTQ.mjs → project-state-XKUSCFSV.mjs} +3 -2
  70. package/dist/agent-verifier/project-state-XKUSCFSV.mjs.map +1 -0
  71. package/dist/agent-verifier/{prompt-3BAINGAQ.mjs → prompt-VKHMCQT6.mjs} +2 -1
  72. package/dist/agent-verifier/prompt-VKHMCQT6.mjs.map +1 -0
  73. package/dist/agent-verifier/{reducer-bundle-preflight-C73LEXI2.mjs → reducer-bundle-preflight-7NYZF5ZT.mjs} +6 -9
  74. package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs.map +1 -0
  75. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +11 -0
  76. package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs.map +1 -0
  77. package/dist/agent-verifier/{reducer-native-test-harness-GMWBUISX.mjs → reducer-native-test-harness-D4VWPIAC.mjs} +14 -17
  78. package/dist/agent-verifier/reducer-native-test-harness-D4VWPIAC.mjs.map +1 -0
  79. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs +26 -0
  80. package/dist/agent-verifier/static-scaffold-JCRBDKEH.mjs.map +1 -0
  81. package/dist/agent-verifier/{sync-3DUQH32H.mjs → sync-UTL2IIZV.mjs} +35 -39
  82. package/dist/agent-verifier/sync-UTL2IIZV.mjs.map +1 -0
  83. package/dist/agent-verifier/{test-P4U5INTD.mjs → test-H26XCBFA.mjs} +29 -31
  84. package/dist/agent-verifier/test-H26XCBFA.mjs.map +1 -0
  85. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs +10 -0
  86. package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +1 -0
  87. package/dist/agent-verifier/{workspace-dependencies-HZ6VVS4G.mjs → workspace-dependencies-ULZZZPNX.mjs} +5 -4
  88. package/dist/agent-verifier/workspace-dependencies-ULZZZPNX.mjs.map +1 -0
  89. package/dist/{chunk-C6UAT6EH.js → chunk-GXM7RRZJ.js} +9 -11
  90. package/dist/chunk-GXM7RRZJ.js.map +1 -0
  91. package/dist/{chunk-RS7UXJZV.js → chunk-P5TITCD3.js} +790 -17875
  92. package/dist/chunk-P5TITCD3.js.map +1 -0
  93. package/dist/{global-config-AGFBDFYD.js → global-config-WPJRXVDO.js} +2 -2
  94. package/dist/global-config-WPJRXVDO.js.map +1 -0
  95. package/dist/index.js +437 -52
  96. package/dist/index.js.map +1 -1
  97. package/dist/internal.js +2 -3
  98. package/package.json +8 -7
  99. package/skills/dreamboard/references/building-your-first-game.md +510 -0
  100. package/skills/dreamboard/references/cli.md +104 -0
  101. package/skills/dreamboard/references/game-interface.md +548 -0
  102. package/skills/dreamboard/references/manifest-authoring.md +597 -0
  103. package/skills/dreamboard/references/quickstart.md +66 -0
  104. package/skills/dreamboard/references/reducer.md +864 -0
  105. package/skills/dreamboard/references/rule-authoring.md +147 -0
  106. package/skills/dreamboard/references/testing.md +249 -0
  107. package/skills/dreamboard/scripts/events-extract.mjs +218 -0
  108. package/dist/agent-verifier/chunk-54TAYXUD.mjs +0 -12
  109. package/dist/agent-verifier/chunk-HBNDKQT5.mjs +0 -8381
  110. package/dist/agent-verifier/chunk-LI3ZR3BI.mjs +0 -41
  111. package/dist/agent-verifier/chunk-U6OJN7XS.mjs +0 -8092
  112. package/dist/agent-verifier/local-typecheck-QFYYAZOK.mjs +0 -9
  113. package/dist/agent-verifier/reducer-contract-preflight-22X7DSZW.mjs +0 -10
  114. package/dist/agent-verifier/static-scaffold-AJMZZQWS.mjs +0 -28
  115. package/dist/agent-verifier/testing-5K2BJYF2.mjs +0 -674
  116. package/dist/agent-verifier/workspace-codegen-JDZJRSDV.mjs +0 -11
  117. package/dist/chunk-7FOO4AJI.js +0 -50
  118. package/dist/chunk-7FOO4AJI.js.map +0 -1
  119. package/dist/chunk-C6UAT6EH.js.map +0 -1
  120. package/dist/chunk-RS7UXJZV.js.map +0 -1
  121. package/dist/internal.d.ts +0 -311
  122. package/dist/runtime-packages/ui-host-runtime/src/actor-principal.ts +0 -71
  123. package/dist/runtime-packages/ui-host-runtime/src/browser-interaction.ts +0 -139
  124. package/dist/runtime-packages/ui-host-runtime/src/components/host-controls.tsx +0 -374
  125. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback-toaster.tsx +0 -266
  126. package/dist/runtime-packages/ui-host-runtime/src/components/host-feedback.tsx +0 -212
  127. package/dist/runtime-packages/ui-host-runtime/src/components/host-primitives.tsx +0 -271
  128. package/dist/runtime-packages/ui-host-runtime/src/components/host-session-metadata.tsx +0 -135
  129. package/dist/runtime-packages/ui-host-runtime/src/components/index.ts +0 -5
  130. package/dist/runtime-packages/ui-host-runtime/src/components/perf-overlay.tsx +0 -194
  131. package/dist/runtime-packages/ui-host-runtime/src/gameplay-authority-transport.ts +0 -626
  132. package/dist/runtime-packages/ui-host-runtime/src/host-controls.tsx +0 -1
  133. package/dist/runtime-packages/ui-host-runtime/src/host-feedback.tsx +0 -1
  134. package/dist/runtime-packages/ui-host-runtime/src/host-session-transport.ts +0 -294
  135. package/dist/runtime-packages/ui-host-runtime/src/index.ts +0 -3
  136. package/dist/runtime-packages/ui-host-runtime/src/logger.ts +0 -11
  137. package/dist/runtime-packages/ui-host-runtime/src/perf.ts +0 -324
  138. package/dist/runtime-packages/ui-host-runtime/src/plugin-bridge.ts +0 -195
  139. package/dist/runtime-packages/ui-host-runtime/src/plugin-health-check.ts +0 -138
  140. package/dist/runtime-packages/ui-host-runtime/src/plugin-messages.ts +0 -159
  141. package/dist/runtime-packages/ui-host-runtime/src/plugin-session-gateway.ts +0 -551
  142. package/dist/runtime-packages/ui-host-runtime/src/runtime/index.ts +0 -13
  143. package/dist/runtime-packages/ui-host-runtime/src/screenshot/projection-to-snapshot.ts +0 -122
  144. package/dist/runtime-packages/ui-host-runtime/src/screenshot/static-store-api.ts +0 -26
  145. package/dist/runtime-packages/ui-host-runtime/src/session-ingress-controller.ts +0 -583
  146. package/dist/runtime-packages/ui-host-runtime/src/session-ingress.ts +0 -219
  147. package/dist/runtime-packages/ui-host-runtime/src/session-live-runtime.ts +0 -117
  148. package/dist/runtime-packages/ui-host-runtime/src/session-model.ts +0 -431
  149. package/dist/runtime-packages/ui-host-runtime/src/session-projection.ts +0 -211
  150. package/dist/runtime-packages/ui-host-runtime/src/session-recovery.ts +0 -80
  151. package/dist/runtime-packages/ui-host-runtime/src/session-state-reducer.ts +0 -1034
  152. package/dist/runtime-packages/ui-host-runtime/src/sse-manager.ts +0 -416
  153. package/dist/runtime-packages/ui-host-runtime/src/unified-session-store.ts +0 -184
  154. package/dist/testing-KLSV6CPJ.js +0 -674
  155. package/dist/testing-KLSV6CPJ.js.map +0 -1
  156. /package/dist/{global-config-AGFBDFYD.js.map → agent-verifier/chunk-H6XDQJ3N.mjs.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";