@playcademy/sdk 0.0.1-beta.26 → 0.0.1-beta.28

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.
@@ -98,12 +98,7 @@ export declare class PlaycademyClient {
98
98
  fetch: (gameIdOrSlug: string) => Promise<import("@playcademy/data/types").GameWithManifest>;
99
99
  list: () => Promise<Array<import("@playcademy/data/types").Game>>;
100
100
  saveState: (state: Record<string, unknown>) => Promise<void>;
101
- loadState: () => Promise<{
102
- data: unknown;
103
- updatedAt: Date | null;
104
- userId: string;
105
- gameId: string;
106
- }>;
101
+ loadState: () => Promise<import("@playcademy/data/types").GameStateData>;
107
102
  startSession: (gameId?: string) => Promise<import("../types").StartSessionResponse>;
108
103
  endSession: (sessionId: string, gameId?: string) => Promise<void>;
109
104
  };
@@ -176,14 +171,7 @@ export declare class PlaycademyClient {
176
171
  };
177
172
  /** Map methods (elements) */
178
173
  maps: {
179
- elements: (mapId: string) => Promise<{
180
- id: string;
181
- mapId: string | null;
182
- elementSlug: string;
183
- interactionType: "info" | "game_entry" | "game_registry" | "teleport" | "door_in" | "door_out" | "npc_interaction" | "quest_trigger";
184
- metadata: Record<string, unknown> | null;
185
- gameId: string | null;
186
- }[]>;
174
+ elements: (mapId: string) => Promise<import("@playcademy/data/types").MapElementWithGame[]>;
187
175
  };
188
176
  /** Admin methods (games, items, currencies, shop listings) */
189
177
  admin: {
@@ -55,8 +55,9 @@ export declare function createDevNamespace(client: PlaycademyClient): {
55
55
  * ```typescript
56
56
  * const gameFile = new File([zipData], 'game.zip')
57
57
  * const game = await client.dev.games.upsert('my-game', {
58
- * title: 'My Awesome Game',
59
- * description: 'A fun puzzle game'
58
+ * displayName: 'My Awesome Game',
59
+ * platform: 'web',
60
+ * metadata: { description: 'A fun puzzle game' }
60
61
  * }, gameFile)
61
62
  * ```
62
63
  */
@@ -1,4 +1,4 @@
1
- import type { Game, GameWithManifest } from '@playcademy/data/types';
1
+ import type { Game, GameStateData, GameWithManifest } from '@playcademy/data/types';
2
2
  import type { PlaycademyClient, StartSessionResponse } from '../../types';
3
3
  /**
4
4
  * Creates the games namespace for the PlaycademyClient.
@@ -56,7 +56,7 @@ export declare function createGamesNamespace(client: PlaycademyClient): {
56
56
  * Loads the saved game state from the server.
57
57
  * Requires a gameId to be configured in the client.
58
58
  *
59
- * @returns Promise resolving to the saved game state
59
+ * @returns Promise resolving to the saved game state data
60
60
  *
61
61
  * @example
62
62
  * ```typescript
@@ -64,12 +64,7 @@ export declare function createGamesNamespace(client: PlaycademyClient): {
64
64
  * console.log('Current level:', state.level)
65
65
  * ```
66
66
  */
67
- loadState: () => Promise<{
68
- data: unknown;
69
- updatedAt: Date | null;
70
- userId: string;
71
- gameId: string;
72
- }>;
67
+ loadState: () => Promise<GameStateData>;
73
68
  /**
74
69
  * Starts a new game session.
75
70
  *
@@ -1,3 +1,4 @@
1
+ import type { MapElementWithGame } from '@playcademy/data/types';
1
2
  import type { PlaycademyClient } from '../../types';
2
3
  /**
3
4
  * Creates the maps namespace for the PlaycademyClient.
@@ -11,22 +12,15 @@ export declare function createMapsNamespace(client: PlaycademyClient): {
11
12
  * Retrieves all elements for a specific map.
12
13
  *
13
14
  * @param mapId - The ID of the map to get elements for
14
- * @returns Promise resolving to array of map elements
15
+ * @returns Promise resolving to array of map elements with game data
15
16
  *
16
17
  * @example
17
18
  * ```typescript
18
19
  * const elements = await client.maps.elements('map-123')
19
20
  * elements.forEach(element => {
20
- * console.log(`Element: ${element.type} at (${element.x}, ${element.y})`)
21
+ * console.log(`Element: ${element.elementSlug} - Game: ${element.game?.displayName || 'None'}`)
21
22
  * })
22
23
  * ```
23
24
  */
24
- elements: (mapId: string) => Promise<{
25
- id: string;
26
- mapId: string | null;
27
- elementSlug: string;
28
- interactionType: "info" | "game_entry" | "game_registry" | "teleport" | "door_in" | "door_out" | "npc_interaction" | "quest_trigger";
29
- metadata: Record<string, unknown> | null;
30
- gameId: string | null;
31
- }[]>;
25
+ elements: (mapId: string) => Promise<MapElementWithGame[]>;
32
26
  };
package/dist/index.js CHANGED
@@ -268,7 +268,8 @@ async function request({
268
268
  credentials: "omit"
269
269
  });
270
270
  if (!res.ok) {
271
- const errorBody = await res.clone().json().catch(() => res.text().catch(() => {
271
+ const clonedRes = res.clone();
272
+ const errorBody = await clonedRes.json().catch(() => clonedRes.text().catch(() => {
272
273
  return;
273
274
  })) ?? undefined;
274
275
  throw new ApiError(res.status, res.statusText, errorBody);
@@ -389,12 +390,15 @@ function createUsersNamespace(client) {
389
390
  },
390
391
  quantity: async (identifier) => {
391
392
  const itemId = await resolveItemId(identifier);
392
- const inventory = await client.users.inventory.get();
393
+ const inventory = await client["request"](`/inventory`, "GET");
393
394
  const item = inventory.find((inv) => inv.item?.id === itemId);
394
395
  return item?.quantity ?? 0;
395
396
  },
396
397
  has: async (identifier, minQuantity = 1) => {
397
- const qty = await client.users.inventory.quantity(identifier);
398
+ const itemId = await resolveItemId(identifier);
399
+ const inventory = await client["request"](`/inventory`, "GET");
400
+ const item = inventory.find((inv) => inv.item?.id === itemId);
401
+ const qty = item?.quantity ?? 0;
398
402
  return qty >= minQuantity;
399
403
  }
400
404
  }
@@ -412,11 +416,63 @@ function createDevNamespace(client) {
412
416
  }
413
417
  },
414
418
  games: {
415
- upsert: (slug, metadata, file) => {
416
- const form = new FormData;
417
- form.append("metadata", JSON.stringify(metadata));
418
- form.append("file", file);
419
- return client["request"](`/games/${slug}`, "PUT", form);
419
+ upsert: async (slug, metadata, file) => {
420
+ const game = await client["request"](`/games/${slug}`, "PUT", metadata);
421
+ const fileName = file instanceof File ? file.name : "game.zip";
422
+ const initiateResponse = await client["request"]("/games/uploads/initiate/", "POST", {
423
+ fileName,
424
+ gameId: game.id
425
+ });
426
+ const uploadResponse = await fetch(initiateResponse.presignedUrl, {
427
+ method: "PUT",
428
+ body: file,
429
+ headers: {
430
+ "Content-Type": file.type || "application/octet-stream"
431
+ }
432
+ });
433
+ if (!uploadResponse.ok) {
434
+ throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
435
+ }
436
+ const finalizeResponse = await client["request"]("/games/uploads/finalize/", "POST", {
437
+ tempS3Key: initiateResponse.tempS3Key,
438
+ gameId: initiateResponse.gameId,
439
+ version: initiateResponse.version,
440
+ slug,
441
+ metadata,
442
+ originalFileName: fileName
443
+ });
444
+ if (!finalizeResponse.body) {
445
+ throw new Error("Finalize response body missing");
446
+ }
447
+ const reader = finalizeResponse.body.pipeThrough(new TextDecoderStream).getReader();
448
+ let buffer = "";
449
+ while (true) {
450
+ const { done, value } = await reader.read();
451
+ if (done)
452
+ break;
453
+ buffer += value;
454
+ let eolIndex;
455
+ while ((eolIndex = buffer.indexOf(`
456
+
457
+ `)) >= 0) {
458
+ const message = buffer.slice(0, eolIndex);
459
+ buffer = buffer.slice(eolIndex + 2);
460
+ const eventLine = message.match(/^event: (.*)$/m);
461
+ const dataLine = message.match(/^data: (.*)$/m);
462
+ if (eventLine && dataLine) {
463
+ const eventType = eventLine[1];
464
+ const eventData = JSON.parse(dataLine[1]);
465
+ if (eventType === "complete") {
466
+ reader.cancel();
467
+ return eventData;
468
+ } else if (eventType === "error") {
469
+ reader.cancel();
470
+ throw new Error(eventData.message);
471
+ }
472
+ }
473
+ }
474
+ }
475
+ throw new Error("Upload completed but no final game data received");
420
476
  },
421
477
  update: (gameId, props) => client["request"](`/games/${gameId}`, "PATCH", props),
422
478
  delete: (gameId) => client["request"](`/games/${gameId}`, "DELETE")
@@ -949,7 +1005,7 @@ function createCreditsNamespace(client) {
949
1005
  };
950
1006
  return {
951
1007
  balance: async () => {
952
- const inventory = await client.users.inventory.get();
1008
+ const inventory = await client["request"]("/inventory", "GET");
953
1009
  const primaryCurrencyInventoryItem = inventory.find((item) => item.item?.slug === CURRENCIES.PRIMARY);
954
1010
  return primaryCurrencyInventoryItem?.quantity ?? 0;
955
1011
  },
@@ -958,7 +1014,10 @@ function createCreditsNamespace(client) {
958
1014
  throw new Error("Amount must be positive");
959
1015
  }
960
1016
  const creditsItemId = await getCreditsItemId();
961
- const result = await client.users.inventory.add(creditsItemId, amount);
1017
+ const result = await client["request"]("/inventory/add", "POST", {
1018
+ itemId: creditsItemId,
1019
+ qty: amount
1020
+ });
962
1021
  client["emit"]("inventoryChange", {
963
1022
  itemId: creditsItemId,
964
1023
  delta: amount,
@@ -971,7 +1030,10 @@ function createCreditsNamespace(client) {
971
1030
  throw new Error("Amount must be positive");
972
1031
  }
973
1032
  const creditsItemId = await getCreditsItemId();
974
- const result = await client.users.inventory.remove(creditsItemId, amount);
1033
+ const result = await client["request"]("/inventory/remove", "POST", {
1034
+ itemId: creditsItemId,
1035
+ qty: amount
1036
+ });
975
1037
  client["emit"]("inventoryChange", {
976
1038
  itemId: creditsItemId,
977
1039
  delta: -amount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
- "version": "0.0.1-beta.26",
3
+ "version": "0.0.1-beta.28",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -21,14 +21,18 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "build": "bun build.js",
24
- "bump": "bunx bumpp --no-tag --no-push -c \"chore(@playcademy/sdk): release v%s\"",
25
- "pub": "bun run build && bun run bump && bun publish --access public"
24
+ "bump": "SKIP_TESTS=1 bunx bumpp --no-tag --no-push -c \"chore(@playcademy/sdk): release v%s\"",
25
+ "pub": "bun run build && bun run bump && bun publish --access public",
26
+ "test": "bun test",
27
+ "test:watch": "bun test --watch"
26
28
  },
27
29
  "dependencies": {
28
30
  "@playcademy/logger": "0.0.1"
29
31
  },
30
32
  "devDependencies": {
31
33
  "@playcademy/data": "0.0.1",
34
+ "@playcademy/sandbox": "0.1.0-beta.10",
35
+ "@playcademy/test": "0.0.1",
32
36
  "@types/bun": "latest",
33
37
  "typescript": "^5.7.2",
34
38
  "yocto-spinner": "^0.2.2"