@playcademy/sdk 0.0.1-beta.9 → 0.0.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 (49) hide show
  1. package/README.md +511 -124
  2. package/dist/core/auth/flows/popup.d.ts +14 -0
  3. package/dist/core/auth/flows/redirect.d.ts +15 -0
  4. package/dist/core/auth/flows/unified.d.ts +11 -0
  5. package/dist/core/auth/login.d.ts +20 -0
  6. package/dist/core/auth/oauth.d.ts +115 -0
  7. package/dist/core/auth/utils.d.ts +23 -0
  8. package/dist/core/cache/cooldown-cache.d.ts +31 -0
  9. package/dist/core/cache/index.d.ts +14 -0
  10. package/dist/core/cache/permanent-cache.d.ts +39 -0
  11. package/dist/core/cache/singleton-cache.d.ts +29 -0
  12. package/dist/core/cache/ttl-cache.d.ts +54 -0
  13. package/dist/core/cache/types.d.ts +23 -0
  14. package/dist/core/client.d.ts +444 -68
  15. package/dist/core/namespaces/achievements.d.ts +84 -0
  16. package/dist/core/namespaces/admin.d.ts +385 -0
  17. package/dist/core/namespaces/auth.d.ts +54 -0
  18. package/dist/core/namespaces/character.d.ts +205 -0
  19. package/dist/core/namespaces/credits.d.ts +51 -0
  20. package/dist/core/namespaces/dev.d.ts +323 -0
  21. package/dist/core/namespaces/games.d.ts +173 -0
  22. package/dist/core/namespaces/identity.d.ts +91 -0
  23. package/dist/core/namespaces/index.d.ts +19 -0
  24. package/dist/core/namespaces/leaderboard.d.ts +48 -0
  25. package/dist/core/namespaces/levels.d.ts +90 -0
  26. package/dist/core/namespaces/maps.d.ts +93 -0
  27. package/dist/core/namespaces/realtime.client.d.ts +129 -0
  28. package/dist/core/namespaces/realtime.d.ts +90 -0
  29. package/dist/core/namespaces/runtime.d.ts +222 -0
  30. package/dist/core/namespaces/scores.d.ts +55 -0
  31. package/dist/core/namespaces/shop.d.ts +25 -0
  32. package/dist/core/namespaces/sprites.d.ts +35 -0
  33. package/dist/core/namespaces/telemetry.d.ts +28 -0
  34. package/dist/core/namespaces/timeback.d.ts +111 -0
  35. package/dist/core/namespaces/users.d.ts +172 -0
  36. package/dist/core/request.d.ts +1 -1
  37. package/dist/core/static/identity.d.ts +37 -0
  38. package/dist/core/static/index.d.ts +3 -0
  39. package/dist/core/static/init.d.ts +21 -0
  40. package/dist/core/static/login.d.ts +34 -0
  41. package/dist/index.d.ts +10 -0
  42. package/dist/index.js +2846 -0
  43. package/dist/messaging.d.ts +544 -0
  44. package/dist/types.d.ts +169 -8
  45. package/dist/types.js +748 -0
  46. package/package.json +18 -11
  47. package/dist/bus.d.ts +0 -37
  48. package/dist/runtime.d.ts +0 -7
  49. package/dist/runtime.js +0 -377
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
+ "version": "0.0.2",
3
4
  "type": "module",
4
- "version": "0.0.1-beta.9",
5
5
  "exports": {
6
6
  ".": {
7
- "import": "./dist/runtime.js",
8
- "require": "./dist/runtime.js",
9
- "types": "./dist/runtime.d.ts"
7
+ "import": "./dist/index.js",
8
+ "require": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
10
  },
11
11
  "./types": {
12
12
  "import": "./dist/types.js",
@@ -14,24 +14,31 @@
14
14
  "types": "./dist/types.d.ts"
15
15
  }
16
16
  },
17
+ "main": "dist/index.js",
18
+ "module": "dist/index.js",
17
19
  "files": [
18
20
  "dist"
19
21
  ],
20
- "main": "dist/runtime.js",
21
- "module": "dist/runtime.js",
22
22
  "scripts": {
23
23
  "build": "bun build.js",
24
- "pub": "bun run build && bunx bumpp --no-tag --no-push -c \"chore(@playcademy/sdk): release v%s\" && bun publish --access public"
24
+ "lint": "bunx eslint . --fix --ignore-pattern dist",
25
+ "bump": "SKIP_TESTS=1 bunx bumpp --no-tag --no-push -c \"chore(@playcademy/sdk): release v%s\"",
26
+ "pub": "bun run build && bun run bump && bun publish --access public",
27
+ "test": "bun test",
28
+ "test:watch": "bun test --watch",
29
+ "docs": "typedoc"
25
30
  },
26
31
  "devDependencies": {
27
- "@playcademy/types": "latest",
32
+ "@playcademy/data": "0.0.1",
33
+ "@playcademy/sandbox": "0.1.0-beta.18",
34
+ "@playcademy/test": "0.0.1",
28
35
  "@types/bun": "latest",
29
- "typescript": "^5.0.0",
30
- "yocto-spinner": "^0.2.1"
36
+ "typescript": "^5.7.2",
37
+ "yocto-spinner": "^0.2.2",
38
+ "@playcademy/logger": "0.0.1"
31
39
  },
32
40
  "peerDependencies": {
33
41
  "drizzle-orm": "^0.42.0",
34
- "@playcademy/types": "latest",
35
42
  "typescript": "^5"
36
43
  }
37
44
  }
package/dist/bus.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { GameContextPayload } from './types';
2
- export declare enum BusEvents {
3
- INIT = "PLAYCADEMY_INIT",
4
- TOKEN_REFRESH = "PLAYCADEMY_TOKEN_REFRESH",
5
- PAUSE = "PLAYCADEMY_PAUSE",
6
- RESUME = "PLAYCADEMY_RESUME",
7
- FORCE_EXIT = "PLAYCADEMY_FORCE_EXIT",
8
- OVERLAY = "PLAYCADEMY_OVERLAY",
9
- READY = "PLAYCADEMY_READY",
10
- EXIT = "PLAYCADEMY_EXIT",
11
- TELEMETRY = "PLAYCADEMY_TELEMETRYYYYYYYYYYY"
12
- }
13
- type BusHandler<T = unknown> = (payload: T) => void;
14
- export type BusEventMap = {
15
- [BusEvents.INIT]: GameContextPayload;
16
- [BusEvents.TOKEN_REFRESH]: {
17
- token: string;
18
- exp: number;
19
- };
20
- [BusEvents.PAUSE]: void;
21
- [BusEvents.RESUME]: void;
22
- [BusEvents.FORCE_EXIT]: void;
23
- [BusEvents.OVERLAY]: boolean;
24
- [BusEvents.READY]: void;
25
- [BusEvents.EXIT]: void;
26
- [BusEvents.TELEMETRY]: {
27
- fps: number;
28
- mem: number;
29
- };
30
- };
31
- interface Bus {
32
- emit<K extends BusEvents>(type: K, payload: BusEventMap[K]): void;
33
- on<K extends BusEvents>(type: K, handler: BusHandler<BusEventMap[K]>): void;
34
- off<K extends BusEvents>(type: K, handler: BusHandler<BusEventMap[K]>): void;
35
- }
36
- export declare const bus: Bus;
37
- export {};
package/dist/runtime.d.ts DELETED
@@ -1,7 +0,0 @@
1
- import { PlaycademyClient } from './core/client';
2
- /** For Node, SSR, dashboards, etc. */
3
- export { PlaycademyClient } from './core/client';
4
- /** Factory for code running *inside* the CADEMY loader (games) */
5
- export declare function initFromWindow(): Promise<PlaycademyClient>;
6
- export { bus, BusEvents } from './bus';
7
- export { PlaycademyError } from './core/errors';
package/dist/runtime.js DELETED
@@ -1,377 +0,0 @@
1
- // src/bus.ts
2
- var BusEvents;
3
- ((BusEvents2) => {
4
- BusEvents2["INIT"] = "PLAYCADEMY_INIT";
5
- BusEvents2["TOKEN_REFRESH"] = "PLAYCADEMY_TOKEN_REFRESH";
6
- BusEvents2["PAUSE"] = "PLAYCADEMY_PAUSE";
7
- BusEvents2["RESUME"] = "PLAYCADEMY_RESUME";
8
- BusEvents2["FORCE_EXIT"] = "PLAYCADEMY_FORCE_EXIT";
9
- BusEvents2["OVERLAY"] = "PLAYCADEMY_OVERLAY";
10
- BusEvents2["READY"] = "PLAYCADEMY_READY";
11
- BusEvents2["EXIT"] = "PLAYCADEMY_EXIT";
12
- BusEvents2["TELEMETRY"] = "PLAYCADEMY_TELEMETRYYYYYYYYYYY";
13
- })(BusEvents ||= {});
14
- var busListeners = new Map;
15
- var bus = {
16
- emit(type, payload) {
17
- const isIframe = typeof window !== "undefined" && window.self !== window.top;
18
- const iframeToParentEvents = [
19
- "PLAYCADEMY_READY" /* READY */,
20
- "PLAYCADEMY_EXIT" /* EXIT */,
21
- "PLAYCADEMY_TELEMETRYYYYYYYYYYY" /* TELEMETRY */
22
- ];
23
- if (isIframe && iframeToParentEvents.includes(type)) {
24
- let messageData = { type };
25
- if (payload !== undefined && typeof payload === "object" && payload !== null) {
26
- messageData = { ...messageData, ...payload };
27
- }
28
- window.parent.postMessage(messageData, "*");
29
- } else {
30
- window.dispatchEvent(new CustomEvent(type, { detail: payload }));
31
- }
32
- },
33
- on(type, handler) {
34
- const listener = (event) => handler(event.detail);
35
- if (!busListeners.has(type)) {
36
- busListeners.set(type, new Map);
37
- }
38
- busListeners.get(type).set(handler, listener);
39
- window.addEventListener(type, listener);
40
- },
41
- off(type, handler) {
42
- const typeListeners = busListeners.get(type);
43
- if (!typeListeners || !typeListeners.has(handler)) {
44
- return;
45
- }
46
- const actualListener = typeListeners.get(handler);
47
- window.removeEventListener(type, actualListener);
48
- typeListeners.delete(handler);
49
- if (typeListeners.size === 0) {
50
- busListeners.delete(type);
51
- }
52
- }
53
- };
54
-
55
- // src/core/errors.ts
56
- class PlaycademyError extends Error {
57
- constructor(message) {
58
- super(message);
59
- this.name = "PlaycademyError";
60
- }
61
- }
62
-
63
- class ApiError extends Error {
64
- status;
65
- details;
66
- constructor(status, message, details) {
67
- super(`${status} ${message}`);
68
- this.status = status;
69
- this.details = details;
70
- Object.setPrototypeOf(this, ApiError.prototype);
71
- }
72
- }
73
-
74
- // src/core/request.ts
75
- async function request({
76
- path,
77
- baseUrl,
78
- token,
79
- method = "GET",
80
- body,
81
- extraHeaders = {}
82
- }) {
83
- const url = baseUrl.replace(/\/$/, "") + (path.startsWith("/") ? path : `/${path}`);
84
- const headers = { ...extraHeaders };
85
- let payload;
86
- if (body instanceof FormData) {
87
- payload = body;
88
- } else if (body !== undefined && body !== null) {
89
- payload = JSON.stringify(body);
90
- headers["Content-Type"] = "application/json";
91
- }
92
- if (token)
93
- headers["Authorization"] = `Bearer ${token}`;
94
- const res = await fetch(url, {
95
- method,
96
- headers,
97
- body: payload,
98
- credentials: "omit"
99
- });
100
- if (!res.ok) {
101
- const errorBody = await res.clone().json().catch(() => res.text().catch(() => {
102
- return;
103
- })) ?? undefined;
104
- throw new ApiError(res.status, res.statusText, errorBody);
105
- }
106
- if (res.status === 204)
107
- return;
108
- const contentType = res.headers.get("content-type") ?? "";
109
- if (contentType.includes("application/json")) {
110
- return await res.json();
111
- }
112
- return await res.text();
113
- }
114
- async function fetchManifest(assetBundleBase) {
115
- const manifestUrl = `${assetBundleBase.replace(/\/$/, "")}/playcademy.manifest.json`;
116
- try {
117
- const response = await fetch(manifestUrl);
118
- if (!response.ok) {
119
- console.error(`[fetchManifest] Failed to fetch manifest from ${manifestUrl}. Status: ${response.status}`);
120
- throw new PlaycademyError(`Failed to fetch manifest: ${response.status} ${response.statusText}`);
121
- }
122
- return await response.json();
123
- } catch (error) {
124
- if (error instanceof PlaycademyError) {
125
- throw error;
126
- }
127
- console.error(`[fetchManifest] Error fetching or parsing manifest from ${manifestUrl}:`, error);
128
- throw new PlaycademyError("Failed to load or parse game manifest");
129
- }
130
- }
131
-
132
- // src/core/client.ts
133
- class PlaycademyClient {
134
- baseUrl;
135
- token;
136
- gameId;
137
- listeners = {};
138
- internalClientSessionId;
139
- constructor(config) {
140
- this.baseUrl = config.baseUrl;
141
- this.token = config.token;
142
- this.gameId = config.gameId;
143
- if (this.gameId) {
144
- this._initializeInternalSession().catch((error) => {
145
- console.error("[SDK] Background initialization of auto-session failed:", error);
146
- });
147
- }
148
- }
149
- async _initializeInternalSession() {
150
- if (!this.gameId || this.internalClientSessionId) {
151
- return;
152
- }
153
- try {
154
- const response = await this.games.startSession(this.gameId);
155
- this.internalClientSessionId = response.sessionId;
156
- } catch (error) {
157
- console.error("[SDK] Auto-starting session failed for game", this.gameId, error);
158
- }
159
- }
160
- getBaseUrl() {
161
- const isRelative = this.baseUrl.startsWith("/");
162
- const isBrowser = typeof window !== "undefined";
163
- return isRelative && isBrowser ? `${window.location.origin}${this.baseUrl}` : this.baseUrl;
164
- }
165
- on(event, callback) {
166
- this.listeners[event] = this.listeners[event] ?? [];
167
- this.listeners[event].push(callback);
168
- }
169
- emit(event, payload) {
170
- (this.listeners[event] ?? []).forEach((listener) => {
171
- listener(payload);
172
- });
173
- }
174
- setToken(token) {
175
- this.token = token ?? undefined;
176
- this.emit("authChange", { token: this.token ?? null });
177
- }
178
- onAuthChange(callback) {
179
- this.on("authChange", (payload) => callback(payload.token));
180
- }
181
- async request(path, method, body, headers) {
182
- const effectiveHeaders = { ...headers };
183
- return request({
184
- path,
185
- method,
186
- body,
187
- baseUrl: this.baseUrl,
188
- token: this.token,
189
- extraHeaders: effectiveHeaders
190
- });
191
- }
192
- _ensureGameId() {
193
- if (!this.gameId) {
194
- throw new PlaycademyError("This operation requires a gameId, but none was provided when initializing the client.");
195
- }
196
- return this.gameId;
197
- }
198
- auth = {
199
- logout: async () => {
200
- await this.request(`/auth/logout`, "DELETE");
201
- this.setToken(null);
202
- }
203
- };
204
- runtime = {
205
- getGameToken: async (gameId, options) => {
206
- const res = await this.request(`/games/${gameId}/token`, "POST");
207
- if (options?.apply) {
208
- this.setToken(res.token);
209
- }
210
- return res;
211
- },
212
- exit: async () => {
213
- if (this.internalClientSessionId && this.gameId) {
214
- try {
215
- await this.games.endSession(this.internalClientSessionId, this.gameId);
216
- } catch (error) {
217
- console.error("[SDK] Failed to auto-end session:", this.internalClientSessionId, error);
218
- }
219
- }
220
- bus.emit("PLAYCADEMY_EXIT" /* EXIT */, undefined);
221
- }
222
- };
223
- games = {
224
- fetch: async (gameIdOrSlug) => {
225
- const baseGameData = await this.request(`/games/${gameIdOrSlug}`, "GET");
226
- const manifestData = await fetchManifest(baseGameData.assetBundleBase);
227
- return {
228
- ...baseGameData,
229
- manifest: manifestData
230
- };
231
- },
232
- list: () => this.request("/games", "GET"),
233
- saveState: async (state) => {
234
- const gameId = this._ensureGameId();
235
- await this.request(`/games/${gameId}/state`, "POST", state);
236
- },
237
- loadState: async () => {
238
- const gameId = this._ensureGameId();
239
- return this.request(`/games/${gameId}/state`, "GET");
240
- },
241
- startSession: async (gameId) => {
242
- const idToUse = gameId ?? this._ensureGameId();
243
- return this.request(`/games/${idToUse}/sessions`, "POST", {});
244
- },
245
- endSession: async (sessionId, gameId) => {
246
- const effectiveGameIdToEnd = gameId ?? this._ensureGameId();
247
- if (this.internalClientSessionId && sessionId === this.internalClientSessionId && effectiveGameIdToEnd === this.gameId) {
248
- this.internalClientSessionId = undefined;
249
- }
250
- await this.request(`/games/${effectiveGameIdToEnd}/sessions/${sessionId}/end`, "POST");
251
- }
252
- };
253
- users = {
254
- me: async () => {
255
- return this.request("/users/me", "GET");
256
- },
257
- inventory: {
258
- get: async () => this.request(`/inventory`, "GET"),
259
- add: async (rewardId, qty) => {
260
- const res = await this.request(`/inventory/add`, "POST", { rewardId, qty });
261
- this.emit("inventoryChange", {
262
- rewardId,
263
- delta: qty,
264
- newTotal: res.newTotal
265
- });
266
- return res;
267
- },
268
- spend: async (rewardId, qty) => {
269
- const res = await this.request(`/inventory/spend`, "POST", { rewardId, qty });
270
- this.emit("inventoryChange", {
271
- rewardId,
272
- delta: -qty,
273
- newTotal: res.newTotal
274
- });
275
- return res;
276
- }
277
- }
278
- };
279
- dev = {
280
- auth: {
281
- applyForDeveloper: () => this.request("/dev/apply", "POST"),
282
- getDeveloperStatus: async () => {
283
- const response = await this.request("/dev/status", "GET");
284
- return response.status;
285
- }
286
- },
287
- games: {
288
- upsert: (slug, metadata, file) => {
289
- const form = new FormData;
290
- form.append("metadata", JSON.stringify(metadata));
291
- form.append("file", file);
292
- return this.request(`/games/${slug}`, "PUT", form);
293
- },
294
- update: (gameId, props) => this.request(`/games/${gameId}`, "PATCH", props),
295
- delete: (gameId) => this.request(`/games/${gameId}`, "DELETE")
296
- },
297
- keys: {
298
- createKey: (gameId, label) => this.request(`/dev/games/${gameId}/keys`, "POST", { label }),
299
- listKeys: (gameId) => this.request(`/dev/games/${gameId}/keys`, "GET"),
300
- revokeKey: (keyId) => this.request(`/dev/keys/${keyId}`, "DELETE")
301
- }
302
- };
303
- maps = {
304
- elements: (mapId) => this.request(`/map/elements?mapId=${mapId}`, "GET")
305
- };
306
- admin = {
307
- games: {
308
- pauseGame: (gameId) => this.request(`/admin/games/${gameId}/pause`, "POST"),
309
- resumeGame: (gameId) => this.request(`/admin/games/${gameId}/resume`, "POST")
310
- },
311
- rewards: {
312
- createReward: (props) => this.request("/rewards", "POST", props),
313
- getReward: (rewardId) => this.request(`/rewards/${rewardId}`, "GET"),
314
- listRewards: () => this.request("/rewards", "GET"),
315
- updateReward: (rewardId, props) => this.request(`/rewards/${rewardId}`, "PATCH", props),
316
- deleteReward: (rewardId) => this.request(`/rewards/${rewardId}`, "DELETE")
317
- }
318
- };
319
- telemetry = {
320
- pushMetrics: (metrics) => this.request(`/telemetry/metrics`, "POST", metrics)
321
- };
322
- ping() {
323
- return "pong";
324
- }
325
- static async login(baseUrl, email, password) {
326
- let url = baseUrl;
327
- if (baseUrl.startsWith("/") && typeof window !== "undefined") {
328
- url = window.location.origin + baseUrl;
329
- }
330
- url = url + "/auth/login";
331
- const response = await fetch(url, {
332
- method: "POST",
333
- headers: {
334
- "Content-Type": "application/json"
335
- },
336
- body: JSON.stringify({ email, password })
337
- });
338
- if (!response.ok) {
339
- try {
340
- const errorData = await response.json();
341
- const errorMessage = errorData && errorData.message ? String(errorData.message) : response.statusText;
342
- throw new PlaycademyError(errorMessage);
343
- } catch (error) {
344
- console.error("[SDK] Failed to parse error response JSON, using status text instead:", error);
345
- throw new PlaycademyError(response.statusText);
346
- }
347
- }
348
- return response.json();
349
- }
350
- }
351
-
352
- // src/runtime.ts
353
- async function initFromWindow() {
354
- if (typeof window === "undefined") {
355
- throw new Error("initFromWindow must run in a browser context");
356
- }
357
- const preloaded = window.PLAYCADEMY;
358
- const config = preloaded?.token ? preloaded : await new Promise((resolve) => bus.on("PLAYCADEMY_INIT" /* INIT */, resolve));
359
- const client = new PlaycademyClient({
360
- baseUrl: config.baseUrl,
361
- token: config.token,
362
- gameId: config.gameId
363
- });
364
- bus.on("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, ({ token }) => client.setToken(token));
365
- bus.emit("PLAYCADEMY_READY" /* READY */, undefined);
366
- if (import.meta.env?.MODE === "development") {
367
- window.PLAYCADEMY_CLIENT = client;
368
- }
369
- return client;
370
- }
371
- export {
372
- initFromWindow,
373
- bus,
374
- PlaycademyError,
375
- PlaycademyClient,
376
- BusEvents
377
- };