@playcademy/sdk 0.0.1-beta.9 → 0.0.1

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 +168 -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/README.md CHANGED
@@ -1,169 +1,556 @@
1
- # Playcademy SDK
1
+ # @playcademy/sdk
2
2
 
3
- A TypeScript SDK for interacting with the Playcademy platform API.
3
+ **Official TypeScript SDK for the Playcademy platform**
4
+
5
+ The Playcademy SDK provides a comprehensive, type-safe interface for building games on the Playcademy platform. It handles authentication, game sessions, user data, inventory management, and all platform APIs through a unified client interface.
4
6
 
5
7
  ## Overview
6
8
 
7
- This SDK provides a unified client for various interactions with Playcademy.
9
+ The SDK serves as the primary interface between your game and the Playcademy platform, providing:
10
+
11
+ - **Automatic Environment Detection**: Seamlessly works in development and production
12
+ - **Type-Safe API Access**: Full TypeScript support with comprehensive type definitions
13
+ - **Session Management**: Automatic game session handling and state persistence
14
+ - **Event System**: Real-time notifications for inventory changes, level ups, and more
15
+ - **Developer Tools**: Built-in support for game development and testing workflows
16
+
17
+ ### Key Benefits
18
+
19
+ - **Zero Configuration**: Automatic initialization with environment detection
20
+ - **Production Ready**: Battle-tested API patterns with robust error handling
21
+ - **Real-Time Communication**: Open game-scoped WebSocket channels for multiplayer features.
22
+ - **Event System**: Subscribe to platform events like inventory changes and level ups
23
+ - **Comprehensive Coverage**: Access to all Playcademy platform features
24
+ - **Development Experience**: Integrated with sandbox environment for local development
25
+
26
+ ### Use Cases
8
27
 
9
- ## Install
28
+ - **Game Development**: Primary SDK for building games on Playcademy
29
+ - **Web Applications**: Frontend applications interacting with the platform
30
+ - **Developer Tools**: Scripts and utilities for game management
31
+ - **Server Integration**: Backend services integrating with Playcademy APIs
32
+ - **Testing & Automation**: Automated testing of platform integrations
33
+
34
+ ## Installation
35
+
36
+ Install the SDK using your preferred package manager:
10
37
 
11
38
  ```bash
39
+ # Using Bun (recommended)
12
40
  bun add @playcademy/sdk
13
- # or
41
+
42
+ # Using npm
14
43
  npm install @playcademy/sdk
15
- # or
44
+
45
+ # Using yarn
16
46
  yarn add @playcademy/sdk
17
- # or
47
+
48
+ # Using pnpm
18
49
  pnpm add @playcademy/sdk
19
50
  ```
20
51
 
21
- ## Initialization
52
+ ## Quick Start
22
53
 
23
- ### Browser / Mini-Games (Runtime Environment)
54
+ ### Automatic Initialization (Recommended)
24
55
 
25
- For code running within the Playcademy game loader environment (e.g., mini-games):
56
+ For most game development scenarios, use automatic initialization:
26
57
 
27
- ```ts
28
- import { initFromWindow } from '@playcademy/sdk'
58
+ ```typescript
59
+ import { PlaycademyClient } from '@playcademy/sdk'
29
60
 
30
- // Reads configuration from window.PLAYCADEMY injected by the loader
31
- const client = await initFromWindow() // initFromWindow is async
61
+ async function initializeGame() {
62
+ try {
63
+ // Automatic initialization - detects environment and configures appropriately
64
+ const client = await PlaycademyClient.init()
65
+
66
+ // Get current user
67
+ const user = await client.users.me()
68
+ console.log('Welcome,', user.name)
69
+
70
+ // The client is ready for all platform operations
71
+ return client
72
+ } catch (error) {
73
+ console.error('Failed to initialize Playcademy SDK:', error)
74
+ throw error
75
+ }
76
+ }
32
77
  ```
33
78
 
34
- ### General Use (Node.js, Backend, UI Tooling)
79
+ ### Environment Detection
35
80
 
36
- For server-side code, UI applications, or other environments where you manage configuration manually:
81
+ The SDK automatically detects and configures for different environments:
37
82
 
38
- ```ts
39
- import { PlaycademyClient, type LoginResponse } from '@playcademy/sdk'
83
+ - **Development**: Connects to local sandbox (started by `@playcademy/vite-plugin`)
84
+ - **Production**: Receives configuration from Playcademy platform loader
85
+ - **Testing**: Falls back to mock configuration for automated testing
40
86
 
41
- async function initializeAndUseClient() {
42
- const baseUrl = 'https://api.playcademy.com' // Or your local /api endpoint
43
- let loginResponse: LoginResponse
87
+ ## Core Features
44
88
 
45
- try {
46
- // 1. Authenticate to get a token
47
- loginResponse = await PlaycademyClient.login(
48
- baseUrl,
49
- 'user@example.com',
50
- 'password',
51
- )
52
- } catch (error) {
53
- console.error('Login failed:', error)
54
- return
55
- }
89
+ ### Game Session Management
56
90
 
57
- // 2. Instantiate the client with the token
58
- // Optionally provide a gameId to enable automatic session management for that game.
59
- const client = new PlaycademyClient({
60
- baseUrl: baseUrl,
61
- token: loginResponse.token,
62
- // gameId: 'your-game-id' // Optional: if provided, client attempts to auto-start/end session
63
- })
91
+ ```typescript
92
+ // Automatic session management when gameId is available
93
+ const client = await PlaycademyClient.init()
64
94
 
65
- // If the token has appropriate permissions, you can access all namespaces:
66
- // client.dev.games.upsert(...)
67
- // client.admin.rewards.createReward(...)
68
- // Calling a method without sufficient token permissions will result in a server error.
95
+ // Save transient game state (position, health, temporary data)
96
+ await client.games.saveState({
97
+ currentLevel: 'forest_glade',
98
+ playerPosition: { x: 100, y: 200 },
99
+ health: 85,
100
+ activePowerUps: ['speed_boost'],
101
+ })
69
102
 
70
- // Example: Listen for auth changes (e.g., if token is refreshed or cleared by logout)
71
- client.onAuthChange(token => {
72
- console.log('Authentication token changed:', token)
73
- // You might want to update stored token here
74
- })
103
+ // Load previously saved state
104
+ const gameState = await client.games.loadState()
105
+ console.log('Loaded state:', gameState)
106
+
107
+ // Exit game (automatically ends session if managed)
108
+ await client.runtime.exit()
109
+ ```
110
+
111
+ ### User & Inventory Management
112
+
113
+ ```typescript
114
+ // Get user information
115
+ const user = await client.users.me()
116
+
117
+ // Inventory operations (accepts UUIDs or slugs)
118
+ const inventory = await client.users.inventory.get()
119
+ await client.users.inventory.add('magic-sword', 1)
120
+ await client.users.inventory.remove('health-potion', 1)
121
+
122
+ // Check item quantities and ownership
123
+ const goldCount = await client.users.inventory.quantity('gold-coin')
124
+ const hasKey = await client.users.inventory.has('dungeon-key')
125
+ const hasEnoughGold = await client.users.inventory.has('gold-coin', 100)
126
+ ```
127
+
128
+ ### Credits & Currency
75
129
 
76
- // Example: Logout
77
- // await client.auth.logout();
130
+ ```typescript
131
+ // Platform currency management
132
+ const balance = await client.credits.balance()
133
+ await client.credits.add(100)
134
+ await client.credits.spend(50)
135
+
136
+ // Check affordability
137
+ if ((await client.credits.balance()) >= 100) {
138
+ await client.credits.spend(100)
139
+ console.log('Purchase successful!')
78
140
  }
141
+ ```
142
+
143
+ ### Experience & Levels
79
144
 
80
- initializeAndUseClient()
145
+ ```typescript
146
+ // Level management
147
+ const userLevel = await client.levels.get()
148
+ const progress = await client.levels.progress()
149
+ console.log(`Level ${userLevel.currentLevel}, ${progress.xpToNextLevel} XP to next level`)
150
+
151
+ // Note: XP is now managed entirely through TimeBack integration.
152
+ // XP updates come from TimeBack webhooks only.
81
153
  ```
82
154
 
83
- ## Quickstart: Mini-Game Example
155
+ ### Real-time Communication
84
156
 
85
- ```ts
86
- import { initFromWindow } from '@playcademy/sdk'
157
+ Open a direct, low-latency communication channel for your game, perfect for multiplayer interactions.
87
158
 
88
- async function runGame() {
89
- const client = await initFromWindow()
159
+ ```typescript
160
+ // Open a channel scoped to the current game
161
+ const channel = await client.realtime.open('lobby-chat')
90
162
 
91
- try {
92
- // 1) Start a game session (gameId is optional, defaults to client.gameId)
93
- const { sessionId } = await client.games.startSession()
94
- console.log('Session started:', sessionId)
95
-
96
- // 2) Fetch player's inventory/rewards
97
- const rewards = await client.users.inventory.get()
98
- console.log('Player inventory:', rewards)
99
-
100
- // 3) Save game state (uses client.gameId implicitly)
101
- await client.games.saveState({
102
- currentLevel: 'level_2', // This is separate from progress, often more transient
103
- health: 95,
104
- })
105
- console.log('Game state saved')
163
+ // Send messages to other players in the channel
164
+ channel.send({ type: 'chat', message: 'Hello, world!' })
165
+
166
+ // Listen for incoming messages
167
+ channel.onMessage(data => {
168
+ console.log('Received message:', data)
169
+ })
106
170
 
107
- // 4) Load game state (uses client.gameId implicitly)
108
- const loadedState = await client.games.loadState()
109
- console.log('Game state loaded:', loadedState)
171
+ // Remember to close the channel when it's no longer needed
172
+ // channel.close()
173
+ ```
110
174
 
111
- // 5) End the session when finished (gameId is optional)
112
- await client.games.endSession(sessionId)
113
- console.log('Session ended')
175
+ ## API Reference
114
176
 
115
- // 6) Exit the game view (if embedded)
116
- client.runtime.exit()
117
- } catch (error) {
118
- console.error('An error occurred:', error)
177
+ ### Core Modules
178
+
179
+ #### **Authentication** (`client.auth`)
180
+
181
+ - `logout()`: Logs out user and clears authentication token
182
+
183
+ #### **Users** (`client.users`)
184
+
185
+ - `me()`: Get current user information
186
+ - **Inventory** (`client.users.inventory`):
187
+ - `get()`: Get user's inventory
188
+ - `add(identifier, quantity)`: Add items to inventory
189
+ - `remove(identifier, quantity)`: Remove items from inventory
190
+ - `quantity(identifier)`: Get item quantity
191
+ - `has(identifier, minQuantity?)`: Check item ownership
192
+
193
+ #### **Games** (`client.games`)
194
+
195
+ - `list()`: Get all available games
196
+ - `fetch(gameIdOrSlug)`: Get specific game details
197
+ - `saveState(state)`: Save transient game state
198
+ - `loadState()`: Load saved game state
199
+ - `startSession(gameId?)`: Start game session
200
+ - `endSession(sessionId, gameId?)`: End game session
201
+
202
+ #### **Credits** (`client.credits`)
203
+
204
+ - `balance()`: Get current credits balance
205
+ - `add(amount)`: Add credits to user
206
+ - `spend(amount)`: Spend user credits
207
+
208
+ #### **Levels** (`client.levels`)
209
+
210
+ - `get()`: Get current user level information
211
+ - `progress()`: Get level progress and XP to next level
212
+ - `addXP(amount)`: Add experience points
213
+ - **Config** (`client.levels.config`):
214
+ - `list()`: Get all level configurations
215
+ - `get(level)`: Get specific level configuration
216
+
217
+ #### **Maps** (`client.maps`)
218
+
219
+ - `elements(mapId)`: Get map elements and points of interest
220
+
221
+ #### **Runtime** (`client.runtime`)
222
+
223
+ - `getGameToken(gameId, options?)`: Get game-specific authentication token
224
+ - `exit()`: Signal platform to exit game view
225
+
226
+ #### **Real-time** (`client.realtime`)
227
+
228
+ - `open(channelName?)`: Opens a game-scoped WebSocket channel.
229
+ - `token.get()`: Retrieves a JWT for the real-time service.
230
+
231
+ #### **Leaderboard** (`client.leaderboard`) - Game-specific
232
+
233
+ - `fetch(options?)`: Get leaderboard for a specific game
234
+ - `options.timeframe`: Filter by time period (`'all_time'`, `'monthly'`, `'weekly'`, `'daily'`)
235
+ - `options.gameId`: Game ID to fetch leaderboard for (required)
236
+ - `options.limit`: Number of entries to return (default: 10)
237
+ - `options.offset`: Pagination offset (default: 0)
238
+
239
+ #### **Scores** (`client.scores`) - Platform-wide
240
+
241
+ - `submit(gameId, score, metadata?)`: Submit a score for any game
242
+ - `getUserScores(userId, options?)`: Get all scores for a user
243
+ - `options.gameId`: Filter by specific game (optional)
244
+ - `options.limit`: Number of scores to return (default: 50)
245
+
246
+ ### Developer Tools
247
+
248
+ #### **Developer Authentication** (`client.dev.auth`)
249
+
250
+ - `applyForDeveloper()`: Apply for developer status
251
+ - `getDeveloperStatus()`: Check current developer status
252
+
253
+ #### **Game Management** (`client.dev.games`)
254
+
255
+ - `upsert(slug, metadata, gameFile)`: Create or update game
256
+ - `update(gameId, updates)`: Update game properties
257
+ - `delete(gameId)`: Delete game
258
+
259
+ #### **API Keys** (`client.dev.keys`)
260
+
261
+ - `createKey(gameId, name)`: Create API key for server authentication
262
+ - `listKeys()`: List all API keys
263
+ - `revokeKey(keyId)`: Revoke API key
264
+
265
+ #### **Item Management** (`client.dev.items`)
266
+
267
+ - `list(gameId)`: List all items for a game
268
+ - `get(gameId, slug)`: Get specific item
269
+ - `create(gameId, slug, data)`: Create new game item
270
+ - `update(gameId, itemId, updates)`: Update existing item
271
+ - `delete(gameId, itemId)`: Delete item
272
+
273
+ ## Event System
274
+
275
+ The SDK provides real-time event notifications for important platform changes:
276
+
277
+ ### Available Events
278
+
279
+ ```typescript
280
+ // Authentication changes
281
+ client.on('authChange', payload => {
282
+ console.log('Authentication changed:', payload.token)
283
+ })
284
+
285
+ // Inventory changes
286
+ client.on('inventoryChange', payload => {
287
+ console.log(`Item ${payload.itemId}: ${payload.delta} (total: ${payload.newTotal})`)
288
+ })
289
+
290
+ // Experience gained
291
+ client.on('xpGained', payload => {
292
+ console.log(`Gained ${payload.amount} XP (total: ${payload.totalXP})`)
293
+ })
294
+
295
+ // Level up notifications
296
+ client.on('levelUp', payload => {
297
+ console.log(`Level up! ${payload.oldLevel} → ${payload.newLevel}`)
298
+ console.log('Credits awarded:', payload.creditsAwarded)
299
+ })
300
+ ```
301
+
302
+ ### Event-Driven UI Updates
303
+
304
+ ```typescript
305
+ // Update UI in response to platform events
306
+ client.on('inventoryChange', payload => {
307
+ updateInventoryDisplay(payload.itemId, payload.newTotal)
308
+ })
309
+
310
+ client.on('levelUp', payload => {
311
+ showLevelUpAnimation(payload.newLevel)
312
+ showCreditsAwarded(payload.creditsAwarded)
313
+ })
314
+
315
+ client.on('xpGained', payload => {
316
+ updateXPBar(payload.totalXP, payload.leveledUp)
317
+ })
318
+ ```
319
+
320
+ ## Advanced Usage
321
+
322
+ ### Manual Initialization
323
+
324
+ For server-side applications or custom environments:
325
+
326
+ ```typescript
327
+ import { PlaycademyClient } from '@playcademy/sdk'
328
+
329
+ import type { LoginResponse } from '@playcademy/sdk'
330
+
331
+ // Step 1: Authenticate
332
+ const loginData: LoginResponse = await PlaycademyClient.login(
333
+ 'https://api.playcademy.com',
334
+ 'user@example.com',
335
+ 'password',
336
+ )
337
+
338
+ // Step 2: Initialize client
339
+ const client = new PlaycademyClient({
340
+ baseUrl: 'https://api.playcademy.com',
341
+ token: loginData.token,
342
+ gameId: 'your-game-id', // Optional: enables automatic session management
343
+ })
344
+ ```
345
+
346
+ ### Custom Configuration
347
+
348
+ ```typescript
349
+ const client = new PlaycademyClient({
350
+ baseUrl: 'https://api.playcademy.com',
351
+ token: 'your-auth-token',
352
+ gameId: 'your-game-id',
353
+ // Additional options
354
+ timeout: 10000, // Request timeout in milliseconds
355
+ retries: 3, // Number of retry attempts
356
+ })
357
+ ```
358
+
359
+ ### Error Handling
360
+
361
+ ```typescript
362
+ import { PlaycademyError } from '@playcademy/sdk'
363
+
364
+ try {
365
+ const user = await client.users.me()
366
+ // Handle success
367
+ } catch (error) {
368
+ if (error instanceof PlaycademyError) {
369
+ console.error('Playcademy API Error:', error.message)
370
+ console.error('Status Code:', error.statusCode)
371
+ console.error('Error Code:', error.code)
372
+ } else {
373
+ console.error('Unexpected error:', error)
119
374
  }
120
375
  }
376
+ ```
377
+
378
+ ## Development Environment
379
+
380
+ ### Integration with Playcademy Vite Plugin
381
+
382
+ When using the official Playcademy Vite templates, the development environment is automatically configured:
383
+
384
+ ```typescript
385
+ // In your game's main file
386
+ import { PlaycademyClient } from '@playcademy/sdk'
387
+
388
+ // The vite plugin automatically starts the sandbox
389
+ const client = await PlaycademyClient.init()
390
+ // SDK automatically connects to local sandbox at http://localhost:4321
391
+ ```
392
+
393
+ ### Manual Sandbox Setup
394
+
395
+ If not using the Vite plugin, start the sandbox manually:
396
+
397
+ ```bash
398
+ # Start sandbox server (will also start realtime server on port 4322)
399
+ bunx @playcademy/sandbox --port 4321 --verbose
400
+
401
+ # In your application
402
+ const client = new PlaycademyClient({
403
+ baseUrl: 'http://localhost:4321/api',
404
+ realtimeUrl: 'ws://localhost:4322',
405
+ token: 'dev-token' // Sandbox provides mock authentication
406
+ })
407
+ ```
408
+
409
+ ## Best Practices
410
+
411
+ ### Initialization & Setup
412
+
413
+ - **Always use automatic initialization** for game development with `PlaycademyClient.init()`
414
+ - **Handle initialization errors gracefully** with proper try-catch blocks
415
+ - **Store the client instance** for reuse throughout your application lifecycle
416
+
417
+ ### State Management
418
+
419
+ - **Use `games.saveState()`** for transient data (current level, position, temporary status)
420
+ - **Use `users.inventory`** for persistent items and resources that carry between sessions
421
+ - **Save state periodically**, not on every frame or minor change
422
+ - **Load state once** at game start, then manage locally
423
+
424
+ ### Performance Optimization
425
+
426
+ - **Cache frequently accessed data** like user information and inventory
427
+ - **Batch inventory operations** when possible instead of individual API calls
428
+ - **Use event listeners** to update UI reactively rather than polling
429
+ - **Implement proper loading states** for better user experience
430
+
431
+ ### Error Handling
432
+
433
+ - **Wrap all SDK calls** in appropriate try-catch blocks
434
+ - **Provide fallback behavior** for network errors and API failures
435
+ - **Show meaningful error messages** to users when operations fail
436
+ - **Implement retry logic** for non-critical operations
437
+
438
+ ### Development Workflow
121
439
 
122
- runGame()
123
- ```
124
-
125
- ## API Modules
126
-
127
- The `PlaycademyClient` instance provides access to all API modules.
128
- The server will determine if the provided token has sufficient permissions for each operation.
129
- Internal event handling uses a `BusEvents` enum for type safety.
130
- All methods returning data are strongly typed.
131
-
132
- - **`auth`**: User logout. For login, use the static `PlaycademyClient.login()` method.
133
- - `logout()`: Logs out the current user and clears the token from the client instance.
134
- - **`onAuthChange(callback)`**: A top-level client method to subscribe to authentication token changes (login, logout, explicit `setToken`).
135
- - **`users`**:
136
- - `me()`: Fetch current user details.
137
- - **`inventory`**:
138
- - `get()`: Get player inventory.
139
- - `add(rewardId, qty)`: Add item to player inventory.
140
- - `spend(rewardId, qty)`: Spend item from player inventory.
141
- - **`progress`**: Manages persistent progress data for a game (e.g., levels completed, scores, collectibles).
142
- - `get(gameId?)`: Get the entire progress state for a game. `gameId` is optional and defaults to the client's current game context.
143
- - `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.
144
- - **`games`**: Manages game sessions and transient game state.
145
- - `fetch(gameIdOrSlug)`: Fetch game details with manifest.
146
- - `list()`: List all games.
147
- - `saveState(state)`: Save transient game state (e.g., current position, temporary buffs) for the current game context.
148
- - `loadState()`: Load transient game state for the current game context.
149
- - `startSession(gameId?)`: Start a game session. `gameId` is optional and defaults to `client.gameId` if set. If the client is managing an automatic session, this can be used to start additional, distinct sessions.
150
- - `endSession(sessionId, gameId?)`: End a game session. `gameId` is optional. If this ends an automatically managed session, the client will no longer attempt to auto-end it.
151
- - **`runtime`**:
152
- - `getGameToken(gameId, options?: { apply?: boolean })`: Fetches a game-specific token. If `options.apply` is true, it sets this token as the active token on the client instance (default is false).
153
- - `exit()`: Signals the hosting environment to close the game view. If the client is managing an automatic session (because `gameId` was provided at construction), this method will attempt to end that session before signaling exit.
154
- - **`maps`**:
155
- - `elements(mapId)`: Fetch elements (like nodes, points of interest) for a specific map.
156
- - **`dev.auth`**: Apply for developer status, get status.
157
- - **`dev.games`**: Upsert, update, delete games.
158
- - **`dev.keys`**: Create, list, revoke API keys for games.
159
- - **`admin.games`**: Pause/resume games.
160
- - **`admin.rewards`**: CRUD operations for rewards.
161
- - **`telemetry`**: Push metrics.
440
+ - **Use the sandbox environment** for all local development
441
+ - **Test both online and offline scenarios** to ensure robust error handling
442
+ - **Enable verbose logging** during development for debugging
443
+ - **Validate API responses** and handle edge cases appropriately
444
+
445
+ ## Testing
446
+
447
+ ### Unit Testing
448
+
449
+ ```typescript
450
+ // Mock the SDK for unit tests
451
+ import { jest } from '@jest/globals'
452
+
453
+ // Mock the entire SDK module
454
+ jest.mock('@playcademy/sdk', () => ({
455
+ PlaycademyClient: {
456
+ init: jest.fn().mockResolvedValue({
457
+ users: {
458
+ me: jest.fn().mockResolvedValue({ id: 'test-user', name: 'Test User' }),
459
+ inventory: {
460
+ get: jest.fn().mockResolvedValue([]),
461
+ add: jest.fn().mockResolvedValue(undefined),
462
+ },
463
+ },
464
+ }),
465
+ },
466
+ }))
467
+ ```
468
+
469
+ ### Integration Testing
470
+
471
+ ```typescript
472
+ // Test with real sandbox
473
+ import { PlaycademyClient } from '@playcademy/sdk'
474
+
475
+ describe('Playcademy Integration', () => {
476
+ let client: PlaycademyClient
477
+
478
+ beforeAll(async () => {
479
+ // Initialize with sandbox
480
+ client = new PlaycademyClient({
481
+ baseUrl: 'http://localhost:4321/api',
482
+ token: 'test-token',
483
+ })
484
+ })
485
+
486
+ test('should fetch user data', async () => {
487
+ const user = await client.users.me()
488
+ expect(user).toBeDefined()
489
+ expect(user.name).toEqual(expect.any(String))
490
+ })
491
+ })
492
+ ```
493
+
494
+ ## Troubleshooting
495
+
496
+ ### Common Issues
497
+
498
+ **SDK Initialization Timeout**
499
+
500
+ ```
501
+ Error: PLAYCADEMY_INIT not received within 5000ms
502
+ ```
503
+
504
+ - Ensure you're running in the correct environment (development with sandbox, or production with platform)
505
+ - Check that the Vite plugin is properly configured
506
+ - Verify the sandbox is running on the expected port
507
+
508
+ **Authentication Errors**
509
+
510
+ ```
511
+ Error: Unauthorized (401)
512
+ ```
513
+
514
+ - Check that your authentication token is valid
515
+ - Ensure you have the necessary permissions for the operation
516
+ - Try re-authenticating with `PlaycademyClient.login()`
517
+
518
+ **Network Connection Issues**
519
+
520
+ ```
521
+ Error: Failed to fetch
522
+ ```
523
+
524
+ - Verify the API endpoint is accessible
525
+ - Check network connectivity
526
+ - Ensure CORS is properly configured for cross-origin requests
527
+
528
+ ### Debugging
529
+
530
+ Use these debugging techniques for troubleshooting SDK issues:
531
+
532
+ ```typescript
533
+ // Check initialization process
534
+ try {
535
+ const client = await PlaycademyClient.init()
536
+ console.log('SDK initialized successfully')
537
+ } catch (error) {
538
+ console.error('SDK initialization failed:', error)
539
+ }
540
+
541
+ // Monitor network requests in browser dev tools (Network tab)
542
+ // Check console for SDK error messages
543
+ // Verify API responses and error details
544
+ ```
162
545
 
163
546
  ## Contributing
164
547
 
165
- PRs welcome. Fork, build, test, send a PR.
548
+ The SDK is a critical component of the Playcademy platform ecosystem. When contributing:
166
549
 
167
- ## License
550
+ 1. **Maintain Type Safety**: Ensure all new APIs are fully typed
551
+ 2. **Update Documentation**: Keep this README and JSDoc comments current
552
+ 3. **Add Tests**: Include both unit and integration tests for new features
553
+ 4. **Follow Patterns**: Use consistent patterns with existing SDK methods
554
+ 5. **Handle Errors**: Implement proper error handling and user feedback
168
555
 
169
- MIT
556
+ For general contribution guidelines, see the [monorepo CONTRIBUTING.md](../../CONTRIBUTING.md).