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

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