@playcademy/sdk 0.0.1-beta.22 → 0.0.1-beta.23

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.
package/README.md CHANGED
@@ -136,11 +136,17 @@ All methods returning data are strongly typed.
136
136
  - `me()`: Fetch current user details.
137
137
  - **`inventory`**:
138
138
  - `get()`: Get player inventory.
139
- - `add(itemId, qty)`: Add item to player inventory.
140
- - `spend(itemId, qty)`: Spend item from player inventory.
139
+ - `add(identifier, qty)`: Add item to player inventory (accepts UUID or internal name).
140
+ - `remove(identifier, qty)`: Remove item from player inventory (accepts UUID or internal name).
141
+ - `quantity(identifier)`: Get current quantity of an item (accepts UUID or internal name).
142
+ - `has(identifier, minQuantity?)`: Check if user has enough of an item (accepts UUID or internal name).
141
143
  - **`progress`**: Manages persistent progress data for a game (e.g., levels completed, scores, collectibles).
142
144
  - `get(gameId?)`: Get the entire progress state for a game. `gameId` is optional and defaults to the client's current game context.
143
145
  - `update(data, gameId?)`: Update the progress state for a game. `gameId` is optional. The `data` object can be structured to hold progress for various internal nodes or aspects of the game.
146
+ - **`credits`**: Convenient methods for working with Playcademy Credits (primary platform currency).
147
+ - `balance()`: Get current credits balance.
148
+ - `add(amount)`: Add credits to user.
149
+ - `spend(amount)`: Spend credits from user.
144
150
  - **`games`**: Manages game sessions and transient game state.
145
151
  - `fetch(gameIdOrSlug)`: Fetch game details with manifest.
146
152
  - `list()`: List all games.
@@ -118,8 +118,10 @@ export declare class PlaycademyClient {
118
118
  }>;
119
119
  inventory: {
120
120
  get: () => Promise<import("@playcademy/types").InventoryItemWithItem[]>;
121
- add: (itemId: string, qty: number) => Promise<import("..").InventoryMutationResponse>;
122
- spend: (itemId: string, qty: number) => Promise<import("..").InventoryMutationResponse>;
121
+ add: (identifier: string, qty: number) => Promise<import("..").InventoryMutationResponse>;
122
+ remove: (identifier: string, qty: number) => Promise<import("..").InventoryMutationResponse>;
123
+ quantity: (identifier: string) => Promise<number>;
124
+ has: (identifier: string, minQuantity?: number) => Promise<boolean>;
123
125
  };
124
126
  };
125
127
  /** Developer tools (auth, games, keys management) */
@@ -318,7 +320,7 @@ export declare class PlaycademyClient {
318
320
  level: number;
319
321
  currentXp: number;
320
322
  xpToNextLevel: number;
321
- totalXpEarned: number;
323
+ totalXP: number;
322
324
  }>;
323
325
  addXP: (amount: number) => Promise<import("@playcademy/types").XPAddResult>;
324
326
  config: {
@@ -330,6 +332,12 @@ export declare class PlaycademyClient {
330
332
  telemetry: {
331
333
  pushMetrics: (metrics: Record<string, number>) => Promise<void>;
332
334
  };
335
+ /** Credits methods (credits management) */
336
+ credits: {
337
+ balance: () => Promise<number>;
338
+ add: (amount: number) => Promise<number>;
339
+ spend: (amount: number) => Promise<number>;
340
+ };
333
341
  /** Auto-initializes a PlaycademyClient with context from the environment */
334
342
  static init: typeof init;
335
343
  /** Authenticates a user with email and password */
@@ -0,0 +1,51 @@
1
+ import type { PlaycademyClient } from '../client';
2
+ /**
3
+ * Creates the credits namespace for the PlaycademyClient.
4
+ * Provides convenient methods for working with Playcademy Credits (the primary platform currency).
5
+ *
6
+ * @param client - The PlaycademyClient instance
7
+ * @returns Credits namespace with balance and transaction methods
8
+ */
9
+ export declare function createCreditsNamespace(client: PlaycademyClient): {
10
+ /**
11
+ * Gets the current balance of Playcademy Credits for the authenticated user.
12
+ * This is a convenience method that finds the primary currency in the user's inventory.
13
+ *
14
+ * @returns Promise resolving to the current credits balance
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const balance = await client.credits.balance()
19
+ * console.log('Current credits:', balance)
20
+ * ```
21
+ */
22
+ balance: () => Promise<number>;
23
+ /**
24
+ * Adds Playcademy Credits to the user's inventory.
25
+ * This is a convenience method that automatically finds the credits item ID.
26
+ *
27
+ * @param amount - The amount of credits to add (must be positive)
28
+ * @returns Promise resolving to the new total balance
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const newBalance = await client.credits.add(100)
33
+ * console.log('New balance after adding 100 credits:', newBalance)
34
+ * ```
35
+ */
36
+ add: (amount: number) => Promise<number>;
37
+ /**
38
+ * Spends (removes) Playcademy Credits from the user's inventory.
39
+ * This is a convenience method that automatically finds the credits item ID.
40
+ *
41
+ * @param amount - The amount of credits to spend (must be positive)
42
+ * @returns Promise resolving to the new total balance
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const newBalance = await client.credits.spend(50)
47
+ * console.log('New balance after spending 50 credits:', newBalance)
48
+ * ```
49
+ */
50
+ spend: (amount: number) => Promise<number>;
51
+ };
@@ -8,3 +8,4 @@ export { createAdminNamespace } from './admin';
8
8
  export { createShopNamespace } from './shop';
9
9
  export { createTelemetryNamespace } from './telemetry';
10
10
  export { createLevelsNamespace } from './levels';
11
+ export { createCreditsNamespace } from './credits';
@@ -18,7 +18,7 @@ export declare function createLevelsNamespace(client: PlaycademyClient): {
18
18
  * const userLevel = await client.levels.get()
19
19
  * console.log('Current level:', userLevel.currentLevel)
20
20
  * console.log('Current XP:', userLevel.currentXp)
21
- * console.log('Total XP earned:', userLevel.totalXpEarned)
21
+ * console.log('Total XP earned:', userLevel.totalXP)
22
22
  * ```
23
23
  */
24
24
  get: () => Promise<UserLevel>;
@@ -38,7 +38,7 @@ export declare function createLevelsNamespace(client: PlaycademyClient): {
38
38
  level: number;
39
39
  currentXp: number;
40
40
  xpToNextLevel: number;
41
- totalXpEarned: number;
41
+ totalXP: number;
42
42
  }>;
43
43
  /**
44
44
  * Adds XP to the current user.
@@ -52,33 +52,80 @@ export declare function createUsersNamespace(client: PlaycademyClient): {
52
52
  get: () => Promise<InventoryItemWithItem[]>;
53
53
  /**
54
54
  * Adds items to the user's inventory.
55
+ * Accepts either an item UUID or internal name.
55
56
  * Emits an 'inventoryChange' event when successful.
56
57
  *
57
- * @param itemId - The ID of the item to add
58
+ * @param identifier - The item UUID or internal name
58
59
  * @param qty - The quantity to add (must be positive)
59
60
  * @returns Promise resolving to mutation response with new total
60
61
  *
61
62
  * @example
62
63
  * ```typescript
63
- * const result = await client.users.inventory.add('gold-coin', 100)
64
+ * // Using internal name
65
+ * const result = await client.users.inventory.add('GOLD_COIN', 100)
66
+ *
67
+ * // Using UUID
68
+ * const result = await client.users.inventory.add('550e8400-e29b-41d4-a716-446655440000', 100)
69
+ *
64
70
  * console.log('New total:', result.newTotal)
65
71
  * ```
66
72
  */
67
- add: (itemId: string, qty: number) => Promise<InventoryMutationResponse>;
73
+ add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
68
74
  /**
69
- * Spends (removes) items from the user's inventory.
75
+ * Removes items from the user's inventory.
76
+ * Accepts either an item UUID or internal name.
70
77
  * Emits an 'inventoryChange' event when successful.
71
78
  *
72
- * @param itemId - The ID of the item to spend
73
- * @param qty - The quantity to spend (must be positive)
79
+ * @param identifier - The item UUID or internal name
80
+ * @param qty - The quantity to remove (must be positive)
74
81
  * @returns Promise resolving to mutation response with new total
75
82
  *
76
83
  * @example
77
84
  * ```typescript
78
- * const result = await client.users.inventory.spend('health-potion', 1)
79
- * console.log('Remaining potions:', result.newTotal)
85
+ * // Using internal name
86
+ * const result = await client.users.inventory.remove('HEALTH_POTION', 1)
87
+ *
88
+ * // Using UUID
89
+ * const result = await client.users.inventory.remove('uuid-456-789', 1)
90
+ *
91
+ * console.log('Remaining:', result.newTotal)
92
+ * ```
93
+ */
94
+ remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
95
+ /**
96
+ * Gets the current quantity of an item.
97
+ * Accepts either an item UUID or internal name.
98
+ *
99
+ * @param identifier - The item UUID or internal name
100
+ * @returns Promise resolving to the current quantity (0 if not owned)
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const qty = await client.users.inventory.quantity('HEALTH_POTION')
105
+ * const qty2 = await client.users.inventory.quantity('uuid-123-456')
106
+ * console.log('Health potions:', qty)
107
+ * ```
108
+ */
109
+ quantity: (identifier: string) => Promise<number>;
110
+ /**
111
+ * Checks if the user has at least the specified quantity of an item.
112
+ * Accepts either an item UUID or internal name.
113
+ *
114
+ * @param identifier - The item UUID or internal name
115
+ * @param minQuantity - Minimum quantity required (defaults to 1)
116
+ * @returns Promise resolving to true if user has enough of the item
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const hasKey = await client.users.inventory.has('DUNGEON_KEY')
121
+ * const hasEnoughGold = await client.users.inventory.has('GOLD_COIN', 100)
122
+ * const hasPotion = await client.users.inventory.has('uuid-123-456', 5)
123
+ *
124
+ * if (hasKey && hasEnoughGold) {
125
+ * console.log('Can enter premium dungeon!')
126
+ * }
80
127
  * ```
81
128
  */
82
- spend: (itemId: string, qty: number) => Promise<InventoryMutationResponse>;
129
+ has: (identifier: string, minQuantity?: number) => Promise<boolean>;
83
130
  };
84
131
  };
package/dist/index.js CHANGED
@@ -258,13 +258,29 @@ var init_games = __esm(() => {
258
258
 
259
259
  // src/core/namespaces/users.ts
260
260
  function createUsersNamespace(client) {
261
+ const itemIdCache = new Map;
262
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
263
+ const resolveItemId = async (identifier) => {
264
+ if (UUID_REGEX.test(identifier))
265
+ return identifier;
266
+ if (itemIdCache.has(identifier))
267
+ return itemIdCache.get(identifier);
268
+ const inventory = await client.users.inventory.get();
269
+ const item = inventory.find((inv) => inv.item?.internalName === identifier);
270
+ if (!item) {
271
+ throw new Error(`Item with internal name '${identifier}' not found in inventory`);
272
+ }
273
+ itemIdCache.set(identifier, item.item.id);
274
+ return item.item.id;
275
+ };
261
276
  return {
262
277
  me: async () => {
263
278
  return client["request"]("/users/me", "GET");
264
279
  },
265
280
  inventory: {
266
281
  get: async () => client["request"](`/inventory`, "GET"),
267
- add: async (itemId, qty) => {
282
+ add: async (identifier, qty) => {
283
+ const itemId = await resolveItemId(identifier);
268
284
  const res = await client["request"](`/inventory/add`, "POST", { itemId, qty });
269
285
  client["emit"]("inventoryChange", {
270
286
  itemId,
@@ -273,7 +289,8 @@ function createUsersNamespace(client) {
273
289
  });
274
290
  return res;
275
291
  },
276
- spend: async (itemId, qty) => {
292
+ remove: async (identifier, qty) => {
293
+ const itemId = await resolveItemId(identifier);
277
294
  const res = await client["request"](`/inventory/spend`, "POST", { itemId, qty });
278
295
  client["emit"]("inventoryChange", {
279
296
  itemId,
@@ -281,6 +298,19 @@ function createUsersNamespace(client) {
281
298
  newTotal: res.newTotal
282
299
  });
283
300
  return res;
301
+ },
302
+ quantity: async (identifier) => {
303
+ const inventory = await client.users.inventory.get();
304
+ if (UUID_REGEX.test(identifier)) {
305
+ const item2 = inventory.find((inv) => inv.item?.id === identifier);
306
+ return item2?.quantity ?? 0;
307
+ }
308
+ const item = inventory.find((inv) => inv.item?.internalName === identifier);
309
+ return item?.quantity ?? 0;
310
+ },
311
+ has: async (identifier, minQuantity = 1) => {
312
+ const qty = await client.users.inventory.quantity(identifier);
313
+ return qty >= minQuantity;
284
314
  }
285
315
  }
286
316
  };
@@ -384,7 +414,7 @@ function createLevelsNamespace(client) {
384
414
  const result = await client["request"]("/users/xp/add", "POST", payload);
385
415
  client["emit"]("xpGained", {
386
416
  amount,
387
- totalXpEarned: result.totalXpEarned,
417
+ totalXP: result.totalXP,
388
418
  leveledUp: result.leveledUp
389
419
  });
390
420
  if (result.leveledUp) {
@@ -407,10 +437,88 @@ function createLevelsNamespace(client) {
407
437
  };
408
438
  }
409
439
 
440
+ // ../data/src/constants.ts
441
+ var ITEM_INTERNAL_NAMES, CURRENCIES, BADGES;
442
+ var init_constants = __esm(() => {
443
+ ITEM_INTERNAL_NAMES = {
444
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
445
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
446
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
447
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
448
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
449
+ COMMON_SWORD: "COMMON_SWORD",
450
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
451
+ SMALL_BACKPACK: "SMALL_BACKPACK"
452
+ };
453
+ CURRENCIES = {
454
+ PRIMARY: ITEM_INTERNAL_NAMES.PLAYCADEMY_CREDITS,
455
+ XP: ITEM_INTERNAL_NAMES.PLAYCADEMY_XP
456
+ };
457
+ BADGES = {
458
+ FOUNDING_MEMBER: ITEM_INTERNAL_NAMES.FOUNDING_MEMBER_BADGE,
459
+ EARLY_ADOPTER: ITEM_INTERNAL_NAMES.EARLY_ADOPTER_BADGE,
460
+ FIRST_GAME: ITEM_INTERNAL_NAMES.FIRST_GAME_BADGE
461
+ };
462
+ });
463
+
464
+ // src/core/namespaces/credits.ts
465
+ function createCreditsNamespace(client) {
466
+ let cachedCreditsItemId = null;
467
+ const getCreditsItemId = async () => {
468
+ if (cachedCreditsItemId) {
469
+ return cachedCreditsItemId;
470
+ }
471
+ const items = await client.admin.items.list();
472
+ const creditsItem = items.find((item) => item.internalName === CURRENCIES.PRIMARY);
473
+ if (!creditsItem) {
474
+ throw new Error("Playcademy Credits item not found in catalog");
475
+ }
476
+ cachedCreditsItemId = creditsItem.id;
477
+ return creditsItem.id;
478
+ };
479
+ return {
480
+ balance: async () => {
481
+ const inventory = await client.users.inventory.get();
482
+ const creditsItem = inventory.find((item) => item.item?.internalName === CURRENCIES.PRIMARY);
483
+ return creditsItem?.quantity ?? 0;
484
+ },
485
+ add: async (amount) => {
486
+ if (amount <= 0) {
487
+ throw new Error("Amount must be positive");
488
+ }
489
+ const creditsItemId = await getCreditsItemId();
490
+ const result = await client.users.inventory.add(creditsItemId, amount);
491
+ client["emit"]("inventoryChange", {
492
+ itemId: creditsItemId,
493
+ delta: amount,
494
+ newTotal: result.newTotal
495
+ });
496
+ return result.newTotal;
497
+ },
498
+ spend: async (amount) => {
499
+ if (amount <= 0) {
500
+ throw new Error("Amount must be positive");
501
+ }
502
+ const creditsItemId = await getCreditsItemId();
503
+ const result = await client.users.inventory.remove(creditsItemId, amount);
504
+ client["emit"]("inventoryChange", {
505
+ itemId: creditsItemId,
506
+ delta: -amount,
507
+ newTotal: result.newTotal
508
+ });
509
+ return result.newTotal;
510
+ }
511
+ };
512
+ }
513
+ var init_credits = __esm(() => {
514
+ init_constants();
515
+ });
516
+
410
517
  // src/core/namespaces/index.ts
411
518
  var init_namespaces = __esm(() => {
412
519
  init_runtime();
413
520
  init_games();
521
+ init_credits();
414
522
  });
415
523
 
416
524
  // src/core/static/init.ts
@@ -609,6 +717,7 @@ var init_client = __esm(() => {
609
717
  shop = createShopNamespace(this);
610
718
  levels = createLevelsNamespace(this);
611
719
  telemetry = createTelemetryNamespace(this);
720
+ credits = createCreditsNamespace(this);
612
721
  static init = init;
613
722
  static login = login;
614
723
  };
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { User, InventoryItemWithItem, Game, DeveloperKey, DeveloperStatusResponse, MapElement, Item, InsertItem, ManifestV1, UpdateItem, Currency, InsertCurrency, UpdateCurrency, ShopListing, InsertShopListing, UpdateShopListing, UserLevel, LevelConfig, XPAddResult, UserLevelWithConfig, XPActionInput } from '@playcademy/types';
2
+ export { ITEM_INTERNAL_NAMES, CURRENCIES, BADGES, } from '@playcademy/data/constants';
2
3
  export interface ClientConfig {
3
4
  baseUrl: string;
4
5
  token?: string;
@@ -20,7 +21,7 @@ export interface ClientEvents {
20
21
  };
21
22
  xpGained: {
22
23
  amount: number;
23
- totalXpEarned: number;
24
+ totalXP: number;
24
25
  leveledUp: boolean;
25
26
  };
26
27
  }
package/dist/types.js CHANGED
@@ -0,0 +1,43 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, {
5
+ get: all[name],
6
+ enumerable: true,
7
+ configurable: true,
8
+ set: (newValue) => all[name] = () => newValue
9
+ });
10
+ };
11
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
+
13
+ // ../data/src/constants.ts
14
+ var ITEM_INTERNAL_NAMES, CURRENCIES, BADGES;
15
+ var init_constants = __esm(() => {
16
+ ITEM_INTERNAL_NAMES = {
17
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
18
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
19
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
20
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
21
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
22
+ COMMON_SWORD: "COMMON_SWORD",
23
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
24
+ SMALL_BACKPACK: "SMALL_BACKPACK"
25
+ };
26
+ CURRENCIES = {
27
+ PRIMARY: ITEM_INTERNAL_NAMES.PLAYCADEMY_CREDITS,
28
+ XP: ITEM_INTERNAL_NAMES.PLAYCADEMY_XP
29
+ };
30
+ BADGES = {
31
+ FOUNDING_MEMBER: ITEM_INTERNAL_NAMES.FOUNDING_MEMBER_BADGE,
32
+ EARLY_ADOPTER: ITEM_INTERNAL_NAMES.EARLY_ADOPTER_BADGE,
33
+ FIRST_GAME: ITEM_INTERNAL_NAMES.FIRST_GAME_BADGE
34
+ };
35
+ });
36
+
37
+ // src/types.ts
38
+ init_constants();
39
+ export {
40
+ ITEM_INTERNAL_NAMES,
41
+ CURRENCIES,
42
+ BADGES
43
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
3
  "type": "module",
4
- "version": "0.0.1-beta.22",
4
+ "version": "0.0.1-beta.23",
5
5
  "exports": {
6
6
  ".": {
7
7
  "import": "./dist/index.js",