@rydr/game-sdk 1.7.0

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 (82) hide show
  1. package/README.md +220 -0
  2. package/dist/client/HardwareStore.d.ts +40 -0
  3. package/dist/client/HardwareStore.d.ts.map +1 -0
  4. package/dist/client/HardwareStore.js +36 -0
  5. package/dist/client/HardwareStore.js.map +1 -0
  6. package/dist/client/PlatformClient.d.ts +182 -0
  7. package/dist/client/PlatformClient.d.ts.map +1 -0
  8. package/dist/client/PlatformClient.js +374 -0
  9. package/dist/client/PlatformClient.js.map +1 -0
  10. package/dist/client/Room.d.ts +127 -0
  11. package/dist/client/Room.d.ts.map +1 -0
  12. package/dist/client/Room.js +235 -0
  13. package/dist/client/Room.js.map +1 -0
  14. package/dist/client/adminContent.d.ts +65 -0
  15. package/dist/client/adminContent.d.ts.map +1 -0
  16. package/dist/client/adminContent.js +75 -0
  17. package/dist/client/adminContent.js.map +1 -0
  18. package/dist/client/index.d.ts +7 -0
  19. package/dist/client/index.d.ts.map +1 -0
  20. package/dist/client/index.js +7 -0
  21. package/dist/client/index.js.map +1 -0
  22. package/dist/client/replayCodec.d.ts +27 -0
  23. package/dist/client/replayCodec.d.ts.map +1 -0
  24. package/dist/client/replayCodec.js +74 -0
  25. package/dist/client/replayCodec.js.map +1 -0
  26. package/dist/host/PlatformHost.d.ts +169 -0
  27. package/dist/host/PlatformHost.d.ts.map +1 -0
  28. package/dist/host/PlatformHost.js +248 -0
  29. package/dist/host/PlatformHost.js.map +1 -0
  30. package/dist/host/index.d.ts +3 -0
  31. package/dist/host/index.d.ts.map +1 -0
  32. package/dist/host/index.js +3 -0
  33. package/dist/host/index.js.map +1 -0
  34. package/dist/index.d.ts +12 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +12 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/protocol/boards.d.ts +61 -0
  39. package/dist/protocol/boards.d.ts.map +1 -0
  40. package/dist/protocol/boards.js +53 -0
  41. package/dist/protocol/boards.js.map +1 -0
  42. package/dist/protocol/buttons.d.ts +13 -0
  43. package/dist/protocol/buttons.d.ts.map +1 -0
  44. package/dist/protocol/buttons.js +2 -0
  45. package/dist/protocol/buttons.js.map +1 -0
  46. package/dist/protocol/capabilities.d.ts +23 -0
  47. package/dist/protocol/capabilities.d.ts.map +1 -0
  48. package/dist/protocol/capabilities.js +10 -0
  49. package/dist/protocol/capabilities.js.map +1 -0
  50. package/dist/protocol/gamedata.d.ts +23 -0
  51. package/dist/protocol/gamedata.d.ts.map +1 -0
  52. package/dist/protocol/gamedata.js +2 -0
  53. package/dist/protocol/gamedata.js.map +1 -0
  54. package/dist/protocol/guards.d.ts +15 -0
  55. package/dist/protocol/guards.d.ts.map +1 -0
  56. package/dist/protocol/guards.js +45 -0
  57. package/dist/protocol/guards.js.map +1 -0
  58. package/dist/protocol/identity.d.ts +21 -0
  59. package/dist/protocol/identity.d.ts.map +1 -0
  60. package/dist/protocol/identity.js +2 -0
  61. package/dist/protocol/identity.js.map +1 -0
  62. package/dist/protocol/index.d.ts +12 -0
  63. package/dist/protocol/index.d.ts.map +1 -0
  64. package/dist/protocol/index.js +12 -0
  65. package/dist/protocol/index.js.map +1 -0
  66. package/dist/protocol/messages.d.ts +407 -0
  67. package/dist/protocol/messages.d.ts.map +1 -0
  68. package/dist/protocol/messages.js +2 -0
  69. package/dist/protocol/messages.js.map +1 -0
  70. package/dist/protocol/replays.d.ts +67 -0
  71. package/dist/protocol/replays.d.ts.map +1 -0
  72. package/dist/protocol/replays.js +14 -0
  73. package/dist/protocol/replays.js.map +1 -0
  74. package/dist/protocol/room.d.ts +55 -0
  75. package/dist/protocol/room.d.ts.map +1 -0
  76. package/dist/protocol/room.js +13 -0
  77. package/dist/protocol/room.js.map +1 -0
  78. package/dist/protocol/version.d.ts +12 -0
  79. package/dist/protocol/version.d.ts.map +1 -0
  80. package/dist/protocol/version.js +12 -0
  81. package/dist/protocol/version.js.map +1 -0
  82. package/package.json +37 -0
@@ -0,0 +1,235 @@
1
+ function makeListeners() {
2
+ return {
3
+ message: new Set(),
4
+ presence: new Set(),
5
+ state: new Set(),
6
+ telemetry: new Set(),
7
+ event: new Set(),
8
+ open: new Set(),
9
+ close: new Set(),
10
+ };
11
+ }
12
+ function wsProtocol(host) {
13
+ return /^(localhost|127\.|0\.0\.0\.0|192\.168\.|10\.)/.test(host) ? "ws" : "wss";
14
+ }
15
+ /**
16
+ * Open a room WebSocket directly — used by the SHELL (the trusted socket owner). Returns a
17
+ * {@link ShellRoomHandle} so the shell can also inject telemetry. Games do NOT call this; they use
18
+ * `session.joinRoom()`, which relays through the shell.
19
+ */
20
+ export function createRoom(opts) {
21
+ const room = `${opts.gameId}:${opts.roomId}`;
22
+ const params = new URLSearchParams({ playerId: opts.playerId, name: opts.name ?? "" });
23
+ if (opts.max && opts.max > 0)
24
+ params.set("max", String(opts.max));
25
+ const url = `${wsProtocol(opts.host)}://${opts.host}/parties/room/${encodeURIComponent(room)}?${params}`;
26
+ const ws = new WebSocket(url);
27
+ let members = [];
28
+ let state = {};
29
+ const listeners = makeListeners();
30
+ const fire = (event, ...args) => {
31
+ for (const cb of listeners[event] ?? [])
32
+ cb(...args);
33
+ };
34
+ ws.addEventListener("open", () => fire("open"));
35
+ ws.addEventListener("close", () => fire("close"));
36
+ ws.addEventListener("message", (e) => {
37
+ let msg;
38
+ try {
39
+ msg = JSON.parse(typeof e.data === "string" ? e.data : "");
40
+ }
41
+ catch {
42
+ return;
43
+ }
44
+ switch (msg.t) {
45
+ case "hello":
46
+ state = msg.state ?? {};
47
+ members = msg.members ?? [];
48
+ fire("presence", members);
49
+ fire("state", state);
50
+ break;
51
+ case "presence":
52
+ members = msg.members ?? [];
53
+ fire("presence", members);
54
+ break;
55
+ case "state":
56
+ state = msg.state ?? {};
57
+ fire("state", state);
58
+ break;
59
+ case "msg":
60
+ fire("message", msg.data, msg.from);
61
+ break;
62
+ case "telemetry":
63
+ fire("telemetry", {
64
+ playerId: msg.from,
65
+ power: msg.power,
66
+ cadence: msg.cadence,
67
+ heartRate: msg.heartRate,
68
+ t: msg.ts ?? 0,
69
+ });
70
+ break;
71
+ case "event":
72
+ fire("event", {
73
+ name: msg.name,
74
+ payload: msg.payload,
75
+ at: msg.at ?? 0,
76
+ from: msg.from,
77
+ });
78
+ break;
79
+ case "full":
80
+ // Room at capacity — the server closes the socket; surface it as a close.
81
+ fire("close");
82
+ break;
83
+ }
84
+ });
85
+ const sendRaw = (obj) => {
86
+ if (ws.readyState === WebSocket.OPEN)
87
+ ws.send(JSON.stringify(obj));
88
+ else
89
+ ws.addEventListener("open", () => ws.send(JSON.stringify(obj)), { once: true });
90
+ };
91
+ return {
92
+ send: (data) => sendRaw({ t: "msg", data }),
93
+ setState: (patch) => sendRaw({ t: "state", patch }),
94
+ sendTelemetry: (reading) => sendRaw({
95
+ t: "telemetry",
96
+ power: reading.power,
97
+ cadence: reading.cadence,
98
+ heartRate: reading.heartRate,
99
+ ts: reading.t,
100
+ }),
101
+ scheduleEvent: (name, payload, at) => sendRaw({ t: "event", name, payload, at }),
102
+ get members() {
103
+ return members;
104
+ },
105
+ get state() {
106
+ return state;
107
+ },
108
+ on: ((event, cb) => {
109
+ listeners[event]?.add(cb);
110
+ return () => listeners[event]?.delete(cb);
111
+ }),
112
+ leave: () => ws.close(),
113
+ };
114
+ }
115
+ /**
116
+ * Game-side room over the shell relay. The handle posts `rydr/room.*` frames to the shell; incoming
117
+ * room events are fed in via the returned `dispatch` (the SDK routes them by `roomId`). This is what
118
+ * `session.joinRoom()` returns when embedded.
119
+ */
120
+ export function createRelayRoom(opts) {
121
+ let members = [];
122
+ let state = {};
123
+ const listeners = makeListeners();
124
+ const fire = (event, ...args) => {
125
+ for (const cb of listeners[event] ?? [])
126
+ cb(...args);
127
+ };
128
+ opts.post({ rydr: true, type: "rydr/room.join", roomId: opts.roomId });
129
+ const dispatch = (event) => {
130
+ switch (event.type) {
131
+ case "rydr/room.opened":
132
+ fire("open");
133
+ break;
134
+ case "rydr/room.closed":
135
+ fire("close");
136
+ break;
137
+ case "rydr/room.presence":
138
+ members = event.members ?? [];
139
+ fire("presence", members);
140
+ break;
141
+ case "rydr/room.state":
142
+ state = event.state ?? {};
143
+ fire("state", state);
144
+ break;
145
+ case "rydr/room.message":
146
+ fire("message", event.data, event.from);
147
+ break;
148
+ case "rydr/room.telemetry":
149
+ fire("telemetry", {
150
+ playerId: event.from,
151
+ power: event.power,
152
+ cadence: event.cadence,
153
+ heartRate: event.heartRate,
154
+ t: event.t,
155
+ });
156
+ break;
157
+ case "rydr/room.event":
158
+ fire("event", {
159
+ name: event.name,
160
+ payload: event.payload,
161
+ at: event.at,
162
+ from: event.from,
163
+ });
164
+ break;
165
+ }
166
+ };
167
+ const handle = {
168
+ send: (data) => opts.post({ rydr: true, type: "rydr/room.send", roomId: opts.roomId, data }),
169
+ setState: (patch) => opts.post({ rydr: true, type: "rydr/room.setState", roomId: opts.roomId, patch }),
170
+ scheduleEvent: (name, payload, at) => opts.post({ rydr: true, type: "rydr/room.scheduleEvent", roomId: opts.roomId, name, payload, at }),
171
+ get members() {
172
+ return members;
173
+ },
174
+ get state() {
175
+ return state;
176
+ },
177
+ on: ((event, cb) => {
178
+ listeners[event]?.add(cb);
179
+ return () => listeners[event]?.delete(cb);
180
+ }),
181
+ leave: () => {
182
+ opts.post({ rydr: true, type: "rydr/room.leave", roomId: opts.roomId });
183
+ opts.onLeave();
184
+ fire("close");
185
+ },
186
+ };
187
+ return { handle, dispatch };
188
+ }
189
+ /**
190
+ * A backend-less loopback room for standalone dev (a session with no `dataHost`): a single local
191
+ * member that echoes `send` back as a `message` and reflects `setState`. Lets a game build its
192
+ * realtime UI with no shell/server. No telemetry is emitted (there are no peers).
193
+ */
194
+ export function createLoopbackRoom(playerId, name) {
195
+ const self = { playerId, name };
196
+ let state = {};
197
+ const listeners = makeListeners();
198
+ const fire = (event, ...args) => {
199
+ for (const cb of listeners[event] ?? [])
200
+ cb(...args);
201
+ };
202
+ // Defer the initial events so the caller can attach `.on(...)` first.
203
+ setTimeout(() => {
204
+ fire("open");
205
+ fire("presence", [self]);
206
+ fire("state", state);
207
+ }, 0);
208
+ return {
209
+ send: (data) => setTimeout(() => fire("message", data, playerId), 0),
210
+ setState: (patch) => {
211
+ state = { ...state, ...patch };
212
+ fire("state", state);
213
+ },
214
+ scheduleEvent: (name, payload, at) => {
215
+ // No server: stamp locally and echo back as an event — immediately, or via a real timer for
216
+ // a future `at` — so a solo dev still drives countdowns/phases.
217
+ const now = Date.now();
218
+ const fireEvent = () => fire("event", { name, payload, at: at ?? now, from: playerId });
219
+ const delay = at && at > now ? at - now : 0;
220
+ setTimeout(fireEvent, delay);
221
+ },
222
+ get members() {
223
+ return [self];
224
+ },
225
+ get state() {
226
+ return state;
227
+ },
228
+ on: ((event, cb) => {
229
+ listeners[event]?.add(cb);
230
+ return () => listeners[event]?.delete(cb);
231
+ }),
232
+ leave: () => fire("close"),
233
+ };
234
+ }
235
+ //# sourceMappingURL=Room.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Room.js","sourceRoot":"","sources":["../../src/client/Room.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Admin content backend — the authoring-time mirror of the player-facing
3
+ * content API ({@link PlatformSession.getContent} / `saveContent`), for
4
+ * standalone in-game editor pages.
5
+ *
6
+ * A standalone editor page (e.g. `run-editor.html`, `track-editor.html`) has no
7
+ * platform session, so it authenticates with the platform owner's
8
+ * `ADMIN_SECRET` (Bearer) instead of the author allowlist. This factory wraps
9
+ * the generic gamedata party's HTTP contract for one `gameId` so editors don't
10
+ * hand-roll `fetch` + headers:
11
+ *
12
+ * - read (public): `GET {host}/parties/gamedata/{gameId}/shared/{collection}[/{id}]`
13
+ * - write (Bearer): `PUT …/shared/{collection}/{id}` body `{ data, draft? }`
14
+ * - delete(Bearer): `DELETE …/shared/{collection}/{id}`
15
+ * - asset (Bearer): `POST …/{gameId}/asset/upload-url` → presigned R2 PUT
16
+ *
17
+ * The game reads the SAME content back through the SDK session
18
+ * (`session.listContent` / `getContent`) — one shared backend, no per-game
19
+ * server. See the "Build an in-game editor" guide in the README.
20
+ *
21
+ * SECURITY: `ADMIN_SECRET` is the platform owner's key (full write to any
22
+ * game's shared content). It is an authoring-time credential, entered at
23
+ * runtime (kept in `sessionStorage`, never in the repo) and never shipped to
24
+ * players. Player-generated content uses the SDK's public owner-write scope
25
+ * instead, not this backend.
26
+ */
27
+ import type { GameDoc } from "../protocol/gamedata";
28
+ /** Bytes accepted by {@link AdminContentBackend.uploadAsset}. */
29
+ export type AssetBody = Blob | ArrayBuffer | ArrayBufferView;
30
+ export interface AdminContentBackend {
31
+ /** List all docs in a shared collection (includes drafts — Bearer is sent). */
32
+ list(collection: string): Promise<GameDoc[]>;
33
+ /** Fetch one shared doc, or `null` if it doesn't exist. */
34
+ get(collection: string, id: string): Promise<GameDoc | null>;
35
+ /** Create/overwrite a shared doc. `draft: true` hides it from the public (player) read until published. */
36
+ save(collection: string, id: string, value: unknown, opts?: {
37
+ draft?: boolean;
38
+ }): Promise<void>;
39
+ /** Delete a shared doc. */
40
+ remove(collection: string, id: string): Promise<void>;
41
+ /** Upload a binary asset to R2 and return its public URL (store it in a doc via {@link save}). */
42
+ uploadAsset(opts: {
43
+ collection: string;
44
+ filename: string;
45
+ contentType?: string;
46
+ body: AssetBody;
47
+ }): Promise<{
48
+ url: string;
49
+ }>;
50
+ }
51
+ export interface AdminContentOptions {
52
+ /** Platform origin (no trailing slash), e.g. `https://my-game.partykit.dev` or `http://localhost:1999`. */
53
+ host: string;
54
+ /** The game's stable id (slug), matching its platform manifest. */
55
+ gameId: string;
56
+ /** Returns the `ADMIN_SECRET` at call time (e.g. from `sessionStorage`). Read per-request so a re-entered secret takes effect. */
57
+ getSecret: () => string;
58
+ }
59
+ /**
60
+ * Build an {@link AdminContentBackend} for one game. Pure factory — no network
61
+ * until a method is called. Every request carries the Bearer secret so the
62
+ * editor can see drafts and perform writes.
63
+ */
64
+ export declare function createAdminContentBackend(opts: AdminContentOptions): AdminContentBackend;
65
+ //# sourceMappingURL=adminContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminContent.d.ts","sourceRoot":"","sources":["../../src/client/adminContent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,iEAAiE;AACjE,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,WAAW,GAAG,eAAe,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,2DAA2D;IAC3D,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC7D,2GAA2G;IAC3G,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChG,2BAA2B;IAC3B,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,kGAAkG;IAClG,WAAW,CAAC,IAAI,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9H;AAED,MAAM,WAAW,mBAAmB;IAClC,2GAA2G;IAC3G,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,SAAS,EAAE,MAAM,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,mBAAmB,CA+CxF"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Admin content backend — the authoring-time mirror of the player-facing
3
+ * content API ({@link PlatformSession.getContent} / `saveContent`), for
4
+ * standalone in-game editor pages.
5
+ *
6
+ * A standalone editor page (e.g. `run-editor.html`, `track-editor.html`) has no
7
+ * platform session, so it authenticates with the platform owner's
8
+ * `ADMIN_SECRET` (Bearer) instead of the author allowlist. This factory wraps
9
+ * the generic gamedata party's HTTP contract for one `gameId` so editors don't
10
+ * hand-roll `fetch` + headers:
11
+ *
12
+ * - read (public): `GET {host}/parties/gamedata/{gameId}/shared/{collection}[/{id}]`
13
+ * - write (Bearer): `PUT …/shared/{collection}/{id}` body `{ data, draft? }`
14
+ * - delete(Bearer): `DELETE …/shared/{collection}/{id}`
15
+ * - asset (Bearer): `POST …/{gameId}/asset/upload-url` → presigned R2 PUT
16
+ *
17
+ * The game reads the SAME content back through the SDK session
18
+ * (`session.listContent` / `getContent`) — one shared backend, no per-game
19
+ * server. See the "Build an in-game editor" guide in the README.
20
+ *
21
+ * SECURITY: `ADMIN_SECRET` is the platform owner's key (full write to any
22
+ * game's shared content). It is an authoring-time credential, entered at
23
+ * runtime (kept in `sessionStorage`, never in the repo) and never shipped to
24
+ * players. Player-generated content uses the SDK's public owner-write scope
25
+ * instead, not this backend.
26
+ */
27
+ /**
28
+ * Build an {@link AdminContentBackend} for one game. Pure factory — no network
29
+ * until a method is called. Every request carries the Bearer secret so the
30
+ * editor can see drafts and perform writes.
31
+ */
32
+ export function createAdminContentBackend(opts) {
33
+ const base = `${opts.host.replace(/\/$/, "")}/parties/gamedata/${opts.gameId}`;
34
+ const authHeaders = () => ({ Authorization: `Bearer ${opts.getSecret()}` });
35
+ async function req(method, url, body) {
36
+ const res = await fetch(url, {
37
+ method,
38
+ headers: { ...authHeaders(), ...(body !== undefined ? { "Content-Type": "application/json" } : {}) },
39
+ body: body !== undefined ? JSON.stringify(body) : undefined,
40
+ });
41
+ if (!res.ok) {
42
+ const detail = await res.text().catch(() => "");
43
+ throw new Error(`[rydr-admin] ${method} ${url} → ${res.status} ${detail}`);
44
+ }
45
+ return (await res.json());
46
+ }
47
+ return {
48
+ async list(collection) {
49
+ const r = await req("GET", `${base}/shared/${collection}`);
50
+ return r.docs ?? [];
51
+ },
52
+ async get(collection, id) {
53
+ const r = await req("GET", `${base}/shared/${collection}/${id}`);
54
+ return r.doc ?? null;
55
+ },
56
+ async save(collection, id, value, saveOpts) {
57
+ await req("PUT", `${base}/shared/${collection}/${id}`, { data: value, draft: saveOpts?.draft });
58
+ },
59
+ async remove(collection, id) {
60
+ await req("DELETE", `${base}/shared/${collection}/${id}`);
61
+ },
62
+ async uploadAsset({ collection, filename, contentType, body }) {
63
+ const { uploadUrl, url } = await req("POST", `${base}/asset/upload-url`, { collection, filename });
64
+ const put = await fetch(uploadUrl, {
65
+ method: "PUT",
66
+ headers: contentType ? { "Content-Type": contentType } : {},
67
+ body: body,
68
+ });
69
+ if (!put.ok)
70
+ throw new Error(`[rydr-admin] asset PUT → ${put.status}`);
71
+ return { url };
72
+ },
73
+ };
74
+ }
75
+ //# sourceMappingURL=adminContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminContent.js","sourceRoot":"","sources":["../../src/client/adminContent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /** Game-side client: connect to the platform and observe bridged hardware. */
2
+ export * from "./HardwareStore";
3
+ export * from "./PlatformClient";
4
+ export * from "./replayCodec";
5
+ export * from "./Room";
6
+ export * from "./adminContent";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /** Game-side client: connect to the platform and observe bridged hardware. */
2
+ export * from "./HardwareStore";
3
+ export * from "./PlatformClient";
4
+ export * from "./replayCodec";
5
+ export * from "./Room";
6
+ export * from "./adminContent";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Replay blob codec — the SDK-owned wire shape for replays.
3
+ *
4
+ * A game hands {@link saveReplay} an array of {@link ReplayFrame}; the SDK packs it into a
5
+ * versioned {@link ReplayBody}, gzip+base64-encodes it (so it travels as one opaque string the
6
+ * platform stores verbatim), and in the same pass derives the {@link ReplayMeta} display summary.
7
+ * `decodeReplay` is the inverse, used when a caller actually needs the timeline back.
8
+ *
9
+ * Encoding mirrors the platform's other compressed payloads: gzip via `CompressionStream`, base64,
10
+ * prefixed `gz:`. When `CompressionStream` is unavailable the blob falls back to URL-encoded base64
11
+ * JSON (no `gz:` prefix), and `decodeReplay` accepts both.
12
+ */
13
+ import type { ReplayBody, ReplayFrame, ReplayMeta } from "../protocol/replays";
14
+ /**
15
+ * Encode replay frames into the blob saved by {@link PlatformSession.saveReplay}, plus the derived
16
+ * {@link ReplayMeta}. Async because gzip compression is streaming.
17
+ */
18
+ export declare function encodeReplay(frames: ReplayFrame[], version?: number): Promise<{
19
+ blob: string;
20
+ meta: ReplayMeta;
21
+ }>;
22
+ /**
23
+ * Decode a replay blob (as returned by {@link PlatformSession.getReplay} / `getReplays`) back into
24
+ * its {@link ReplayBody}. Returns `null` if the blob is empty or cannot be parsed.
25
+ */
26
+ export declare function decodeReplay(blob: string | null): Promise<ReplayBody | null>;
27
+ //# sourceMappingURL=replayCodec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replayCodec.d.ts","sourceRoot":"","sources":["../../src/client/replayCodec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAuB/E;;;GAGG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,WAAW,EAAE,EACrB,OAAO,SAAI,GACV,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAqB7C;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmBlF"}
@@ -0,0 +1,74 @@
1
+ const GZIP_PREFIX = "gz:";
2
+ /** Derive the platform-visible summary from a replay's frames. */
3
+ function deriveMeta(frames) {
4
+ if (frames.length === 0) {
5
+ return { durationMs: 0, avgPower: 0, maxPower: 0 };
6
+ }
7
+ let sum = 0;
8
+ let max = 0;
9
+ for (const f of frames) {
10
+ sum += f.power;
11
+ if (f.power > max)
12
+ max = f.power;
13
+ }
14
+ const lastFrame = frames[frames.length - 1];
15
+ return {
16
+ durationMs: lastFrame ? lastFrame.t : 0,
17
+ avgPower: Math.round(sum / frames.length),
18
+ maxPower: Math.round(max),
19
+ };
20
+ }
21
+ /**
22
+ * Encode replay frames into the blob saved by {@link PlatformSession.saveReplay}, plus the derived
23
+ * {@link ReplayMeta}. Async because gzip compression is streaming.
24
+ */
25
+ export async function encodeReplay(frames, version = 1) {
26
+ const body = { version, frames };
27
+ const json = JSON.stringify(body);
28
+ const meta = deriveMeta(frames);
29
+ if (typeof CompressionStream === "undefined") {
30
+ return { blob: btoa(encodeURIComponent(json)), meta };
31
+ }
32
+ try {
33
+ const cs = new CompressionStream("gzip");
34
+ const writer = cs.writable.getWriter();
35
+ void writer.write(new TextEncoder().encode(json));
36
+ void writer.close();
37
+ const compressed = new Uint8Array(await new Response(cs.readable).arrayBuffer());
38
+ let binary = "";
39
+ for (const byte of compressed)
40
+ binary += String.fromCharCode(byte);
41
+ return { blob: `${GZIP_PREFIX}${btoa(binary)}`, meta };
42
+ }
43
+ catch {
44
+ return { blob: btoa(encodeURIComponent(json)), meta };
45
+ }
46
+ }
47
+ /**
48
+ * Decode a replay blob (as returned by {@link PlatformSession.getReplay} / `getReplays`) back into
49
+ * its {@link ReplayBody}. Returns `null` if the blob is empty or cannot be parsed.
50
+ */
51
+ export async function decodeReplay(blob) {
52
+ if (!blob)
53
+ return null;
54
+ try {
55
+ if (blob.startsWith(GZIP_PREFIX)) {
56
+ if (typeof DecompressionStream === "undefined")
57
+ return null;
58
+ const binary = atob(blob.slice(GZIP_PREFIX.length));
59
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
60
+ const ds = new DecompressionStream("gzip");
61
+ const writer = ds.writable.getWriter();
62
+ void writer.write(bytes);
63
+ void writer.close();
64
+ const json = new TextDecoder().decode(await new Response(ds.readable).arrayBuffer());
65
+ return JSON.parse(json);
66
+ }
67
+ // Uncompressed fallback (URL-encoded base64 JSON).
68
+ return JSON.parse(decodeURIComponent(atob(blob)));
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ //# sourceMappingURL=replayCodec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replayCodec.js","sourceRoot":"","sources":["../../src/client/replayCodec.ts"],"names":[],"mappings":"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"}