@playcademy/sdk 0.9.1-beta.1 → 0.9.1-beta.3

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/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { UserRole, AUTH_PROVIDER_IDS } from '@playcademy/constants';
2
- import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
1
+ import * as _playcademy_types from '@playcademy/types';
2
+ import { TimebackGrade, TimebackSubject } from '@playcademy/types/timeback';
3
+ import { TimebackUserRole, UserEnrollment, UserOrganization, UserInfo } from '@playcademy/types/user';
4
+ import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
3
5
 
4
6
  /**
5
7
  * Base error class for Cademy SDK specific errors.
@@ -138,11 +140,11 @@ interface ApiErrorInfo {
138
140
  * @example
139
141
  * ```typescript
140
142
  * try {
141
- * await client.shop.purchase(itemId)
143
+ * await client.scores.submit(100)
142
144
  * } catch (error) {
143
145
  * const info = extractApiErrorInfo(error)
144
146
  * if (info) {
145
- * showToast(`Error: ${info.message}`)
147
+ * console.error(`Error: ${info.message}`)
146
148
  * }
147
149
  * }
148
150
  * ```
@@ -150,828 +152,235 @@ interface ApiErrorInfo {
150
152
  declare function extractApiErrorInfo(error: unknown): ApiErrorInfo | null;
151
153
 
152
154
  /**
153
- * Connection monitoring types
154
- *
155
- * Type definitions for connection state, configuration, and callbacks.
155
+ * OAuth 2.0 implementation for the Playcademy SDK
156
156
  */
157
+
157
158
  /**
158
- * Possible connection states.
159
+ * Parses an OAuth state parameter to extract CSRF token and any encoded data.
159
160
  *
160
- * - **online**: Connection is stable and healthy
161
- * - **offline**: Complete loss of network connectivity
162
- * - **degraded**: Connection is slow or experiencing intermittent issues
161
+ * @param state - The OAuth state parameter to parse
162
+ * @returns Object containing CSRF token and optional decoded data
163
+ */
164
+ declare function parseOAuthState(state: string): {
165
+ csrfToken: string;
166
+ data?: Record<string, string>;
167
+ };
168
+
169
+ /** Permitted HTTP verbs */
170
+ type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
171
+ interface RetryPolicy {
172
+ retryableMethods?: readonly Method[];
173
+ }
174
+
175
+ /**
176
+ * Cache configuration types for runtime customization
163
177
  */
164
- type ConnectionState = 'online' | 'offline' | 'degraded';
165
178
  /**
166
- * Configuration options for ConnectionMonitor.
167
- *
168
- * @see {@link ConnectionMonitor} for usage
179
+ * Runtime configuration for TTL cache behavior
169
180
  */
170
- interface ConnectionMonitorConfig {
171
- /** Base URL for heartbeat pings (e.g., 'https://api.playcademy.com') */
172
- baseUrl: string;
173
- /** How often to send heartbeat pings in milliseconds (default: 10000) */
174
- heartbeatInterval?: number;
175
- /** How long to wait for heartbeat response in milliseconds (default: 5000) */
176
- heartbeatTimeout?: number;
177
- /** Number of consecutive failures before triggering disconnect (default: 2) */
178
- failureThreshold?: number;
179
- /** Enable periodic heartbeat monitoring (default: true) */
180
- enableHeartbeat?: boolean;
181
- /** Enable browser online/offline event listeners (default: true) */
182
- enableOfflineEvents?: boolean;
181
+ interface TTLCacheConfig {
182
+ /** Time-to-live in milliseconds. Set to 0 to disable caching for this call. */
183
+ ttl?: number;
184
+ /** Force refresh, bypassing cache */
185
+ force?: boolean;
186
+ /** Skip cache and fetch fresh data (alias for force) */
187
+ skipCache?: boolean;
183
188
  }
189
+
184
190
  /**
185
- * Callback function signature for connection state changes.
191
+ * @fileoverview Authentication Strategy Pattern
186
192
  *
187
- * @param state - The new connection state
188
- * @param reason - Human-readable reason for the state change
193
+ * Provides different authentication strategies for the Playcademy SDK.
194
+ * Each strategy knows how to add its authentication headers to requests.
189
195
  */
190
- type ConnectionChangeCallback = (state: ConnectionState, reason: string) => void;
191
196
 
192
197
  /**
193
- * Connection Monitor
194
- *
195
- * Monitors network connectivity using multiple signals:
196
- * 1. navigator.onLine - Instant offline detection
197
- * 2. Periodic heartbeat - Detects slow/degraded connections
198
- * 3. Request failure tracking - Piggybacks on actual API calls
199
- *
200
- * Designed for school WiFi environments where connections may be
201
- * unstable or degraded without fully disconnecting.
198
+ * Base interface for authentication strategies
202
199
  */
200
+ interface AuthStrategy {
201
+ /** Get the token value */
202
+ getToken(): string | null;
203
+ /** Get the token type */
204
+ getType(): TokenType;
205
+ /** Get authentication headers for a request */
206
+ getHeaders(): Record<string, string>;
207
+ }
203
208
 
204
209
  /**
205
- * Monitors network connectivity using multiple signals and notifies callbacks of state changes.
206
- *
207
- * The ConnectionMonitor uses a multi-signal approach to detect connection issues:
208
- *
209
- * 1. **navigator.onLine events** - Instant detection of hard disconnects
210
- * 2. **Heartbeat pings** - Periodic checks to detect slow/degraded connections
211
- * 3. **Request failure tracking** - Piggybacks on actual API calls
212
- *
213
- * This comprehensive approach ensures reliable detection across different network
214
- * failure modes common in school WiFi environments (hard disconnect, slow connection,
215
- * intermittent failures).
216
- *
217
- * @example
218
- * ```typescript
219
- * const monitor = new ConnectionMonitor({
220
- * baseUrl: 'https://api.playcademy.com',
221
- * heartbeatInterval: 10000, // Check every 10s
222
- * failureThreshold: 2 // Trigger after 2 failures
223
- * })
224
- *
225
- * monitor.onChange((state, reason) => {
226
- * console.log(`Connection: ${state} - ${reason}`)
227
- * })
228
- *
229
- * monitor.start()
230
- * ```
210
+ * Base Playcademy SDK client with shared infrastructure.
211
+ * Provides authentication, HTTP requests, events,
212
+ * and fundamental namespaces used by all clients.
231
213
  *
232
- * @see {@link ConnectionManagerConfig} for configuration options
214
+ * Extended by PlaycademyClient (game SDK) and PlaycademyInternalClient (platform SDK).
233
215
  */
234
- declare class ConnectionMonitor {
235
- private state;
236
- private callbacks;
237
- private heartbeatInterval?;
238
- private consecutiveFailures;
239
- private isMonitoring;
240
- private config;
216
+ declare abstract class PlaycademyBaseClient {
217
+ baseUrl: string;
218
+ gameUrl?: string;
219
+ mode: PlaycademyMode;
220
+ protected authStrategy: AuthStrategy;
221
+ protected gameId?: string;
222
+ protected config: Partial<ClientConfig>;
223
+ protected listeners: EventListeners;
224
+ protected authContext?: {
225
+ isInIframe: boolean;
226
+ };
227
+ protected initPayload?: InitPayload;
228
+ protected launchId?: string;
229
+ constructor(config?: Partial<ClientConfig>);
241
230
  /**
242
- * Creates a new ConnectionMonitor instance.
243
- *
244
- * The monitor starts in a stopped state. Call `start()` to begin monitoring.
245
- *
246
- * @param config - Configuration options
247
- * @param config.baseUrl - Base URL for heartbeat pings
248
- * @param config.heartbeatInterval - How often to check (default: 10000ms)
249
- * @param config.heartbeatTimeout - Request timeout (default: 5000ms)
250
- * @param config.failureThreshold - Failures before triggering disconnect (default: 2)
251
- * @param config.enableHeartbeat - Enable periodic checks (default: true)
252
- * @param config.enableOfflineEvents - Listen to browser events (default: true)
231
+ * Gets the effective base URL for API requests.
253
232
  */
254
- constructor(config: ConnectionMonitorConfig);
233
+ getBaseUrl(): string;
255
234
  /**
256
- * Starts monitoring the connection state.
257
- *
258
- * Sets up event listeners and begins heartbeat checks based on configuration.
259
- * Idempotent - safe to call multiple times.
235
+ * Gets the effective game backend URL for integration requests.
260
236
  */
261
- start(): void;
237
+ protected getGameBackendUrl(): string;
262
238
  /**
263
- * Stops monitoring the connection state and cleans up resources.
264
- *
265
- * Removes event listeners and clears heartbeat intervals.
266
- * Idempotent - safe to call multiple times.
239
+ * Simple ping method for testing connectivity.
267
240
  */
268
- stop(): void;
241
+ ping(): string;
269
242
  /**
270
- * Registers a callback to be notified of all connection state changes.
271
- *
272
- * The callback fires for all state transitions: online → offline,
273
- * offline → degraded, degraded → online, etc.
274
- *
275
- * @param callback - Function called with (state, reason) when connection changes
276
- * @returns Cleanup function to unregister the callback
277
- *
278
- * @example
279
- * ```typescript
280
- * const cleanup = monitor.onChange((state, reason) => {
281
- * console.log(`Connection: ${state}`)
282
- * if (state === 'offline') {
283
- * showReconnectingUI()
284
- * }
285
- * })
286
- *
287
- * // Later: cleanup() to unregister
288
- * ```
243
+ * Sets the authentication token for API requests.
289
244
  */
290
- onChange(callback: ConnectionChangeCallback): () => void;
245
+ setToken(token: string | null, tokenType?: TokenType): void;
246
+ setLaunchId(launchId: string | null | undefined): void;
291
247
  /**
292
- * Gets the current connection state.
293
- *
294
- * @returns The current state ('online', 'offline', or 'degraded')
248
+ * Gets the current token type.
295
249
  */
296
- getState(): ConnectionState;
250
+ getTokenType(): TokenType;
297
251
  /**
298
- * Manually triggers an immediate connection check.
299
- *
300
- * Forces a heartbeat ping to verify connectivity right now, bypassing
301
- * the normal interval. Useful before critical operations.
302
- *
303
- * @returns Promise resolving to the current connection state after the check
304
- *
305
- * @example
306
- * ```typescript
307
- * const state = await monitor.checkNow()
308
- * if (state !== 'online') {
309
- * alert('Please check your internet connection')
310
- * }
311
- * ```
252
+ * Gets the current authentication token.
312
253
  */
313
- checkNow(): Promise<ConnectionState>;
254
+ getToken(): string | null;
314
255
  /**
315
- * Reports a request failure for tracking.
316
- *
317
- * This should be called from your request wrapper whenever an API call fails.
318
- * Only network errors are tracked (TypeError, fetch failures) - HTTP error
319
- * responses (4xx, 5xx) are ignored.
320
- *
321
- * After consecutive failures exceed the threshold, the monitor transitions
322
- * to 'degraded' or 'offline' state.
323
- *
324
- * @param error - The error from the failed request
325
- *
326
- * @example
327
- * ```typescript
328
- * try {
329
- * await fetch('/api/data')
330
- * } catch (error) {
331
- * monitor.reportRequestFailure(error)
332
- * throw error
333
- * }
334
- * ```
256
+ * Checks if the client has a valid API token.
257
+ */
258
+ isAuthenticated(): boolean;
259
+ /**
260
+ * Registers a callback to be called when authentication state changes.
261
+ */
262
+ onAuthChange(callback: (token: string | null) => void): void;
263
+ /**
264
+ * Sets the authentication context for the client.
265
+ * @internal
335
266
  */
336
- reportRequestFailure(error: unknown): void;
267
+ _setAuthContext(context: {
268
+ isInIframe: boolean;
269
+ }): void;
337
270
  /**
338
- * Reports a successful request.
271
+ * Registers an event listener for client events.
339
272
  *
340
- * This should be called from your request wrapper whenever an API call succeeds.
341
- * Resets the consecutive failure counter and transitions from 'degraded' to
342
- * 'online' if the connection has recovered.
273
+ * @param event - The event name to listen for.
274
+ * @param callback - The handler invoked when the event fires.
275
+ * @returns A cleanup function that removes this specific listener.
276
+ */
277
+ on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): () => void;
278
+ /**
279
+ * Removes a previously registered event listener.
343
280
  *
344
- * @example
345
- * ```typescript
346
- * try {
347
- * const result = await fetch('/api/data')
348
- * monitor.reportRequestSuccess()
349
- * return result
350
- * } catch (error) {
351
- * monitor.reportRequestFailure(error)
352
- * throw error
353
- * }
354
- * ```
281
+ * @param event - The event name to stop listening for.
282
+ * @param callback - The exact function reference passed to {@link on}.
283
+ */
284
+ off<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
285
+ /**
286
+ * Emits an event to all registered listeners.
287
+ */
288
+ protected emit<E extends keyof ClientEvents>(event: E, payload: ClientEvents[E]): void;
289
+ /**
290
+ * Makes an authenticated HTTP request to the platform API.
291
+ */
292
+ protected request<T>(path: string, method: Method, options?: {
293
+ body?: unknown;
294
+ headers?: Record<string, string>;
295
+ raw?: boolean;
296
+ retryPolicy?: RetryPolicy;
297
+ }): Promise<T>;
298
+ /**
299
+ * Makes an authenticated HTTP request to the game's backend Worker.
300
+ */
301
+ protected requestGameBackend<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>, options?: {
302
+ raw?: boolean;
303
+ retryPolicy?: RetryPolicy;
304
+ }): Promise<T>;
305
+ /**
306
+ * Ensures a gameId is available, throwing an error if not.
307
+ */
308
+ protected _ensureGameId(): string;
309
+ /**
310
+ * Detects and sets the authentication context (iframe vs standalone).
311
+ */
312
+ private _detectAuthContext;
313
+ /**
314
+ * Current user data.
315
+ * - `me()` - Get authenticated user profile
355
316
  */
356
- reportRequestSuccess(): void;
357
- private _detectInitialState;
358
- private _handleOnline;
359
- private _handleOffline;
360
- private _startHeartbeat;
361
- private _performHeartbeat;
362
- private _handleHeartbeatFailure;
363
- private _setState;
317
+ users: {
318
+ me: () => Promise<_playcademy_types.AuthenticatedUser>;
319
+ };
364
320
  }
365
321
 
366
322
  /**
367
- * OAuth 2.0 implementation for the Playcademy SDK
368
- */
369
-
370
- /**
371
- * Parses an OAuth state parameter to extract CSRF token and any encoded data.
323
+ * Auto-initializes a PlaycademyClient with context from the environment.
324
+ * Works in both iframe mode (production/development) and standalone mode (local dev).
372
325
  *
373
- * @param state - The OAuth state parameter to parse
374
- * @returns Object containing CSRF token and optional decoded data
375
- */
376
- declare function parseOAuthState(state: string): {
377
- csrfToken: string;
378
- data?: Record<string, string>;
379
- };
380
-
381
- /** Permitted HTTP verbs */
382
- type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
383
- interface RetryPolicy {
384
- retryableMethods?: readonly Method[];
385
- }
386
-
387
- /**
388
- * TimeBack Enums & Literal Types
326
+ * This is the recommended way to initialize the SDK as it automatically:
327
+ * - Detects the runtime environment (iframe vs standalone)
328
+ * - Configures the client with the appropriate context
329
+ * - Sets up event listeners for token refresh
330
+ * - Exposes the client for debugging in development mode
389
331
  *
390
- * Basic type definitions used throughout the TimeBack integration.
332
+ * @param options - Optional configuration overrides
333
+ * @param options.baseUrl - Override the base URL for API requests
334
+ * @returns Promise resolving to a fully initialized PlaycademyClient
335
+ * @throws Error if not running in a browser context
391
336
  *
392
- * @module types/timeback/types
393
- */
394
- /**
395
- * Valid TimeBack subject values for course configuration.
396
- * These are the supported subject values for OneRoster courses.
397
- */
398
- type TimebackSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
399
- /**
400
- * Grade levels per AE OneRoster GradeEnum.
401
- * -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
402
- */
403
- type TimebackGrade = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
404
- /**
405
- * Valid Caliper subject values.
406
- * Matches OneRoster subjects, with "None" as a Caliper-specific fallback.
337
+ * @example
338
+ * ```typescript
339
+ * // Default initialization
340
+ * const client = await PlaycademyClient.init()
341
+ *
342
+ * // With custom base URL
343
+ * const client = await PlaycademyClient.init({ baseUrl: 'https://custom.api.com' })
344
+ * ```
407
345
  */
408
- type CaliperSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
346
+ declare function init<T extends PlaycademyBaseClient = PlaycademyBaseClient>(this: new (config?: Partial<ClientConfig>) => T, options?: {
347
+ baseUrl?: string;
348
+ allowedParentOrigins?: string[];
349
+ }): Promise<T>;
409
350
 
410
351
  /**
411
- * TimeBack Client SDK DTOs
352
+ * Authenticates a user with email and password.
353
+ *
354
+ * This is a standalone authentication method that doesn't require an initialized client.
355
+ * Use this for login flows before creating a client instance.
412
356
  *
413
- * Data transfer objects for the TimeBack client SDK including
414
- * progress tracking, session management, and activity completion.
357
+ * @deprecated Use client.auth.login() instead for better error handling and automatic token management
358
+ *
359
+ * @param baseUrl - The base URL of the Playcademy API
360
+ * @param email - User's email address
361
+ * @param password - User's password
362
+ * @returns Promise resolving to authentication response with token
363
+ * @throws PlaycademyError if authentication fails or network error occurs
415
364
  *
416
- * Note: TimebackClientConfig lives in @playcademy/timeback as it's
417
- * SDK configuration, not a DTO.
365
+ * @example
366
+ * ```typescript
367
+ * // Preferred approach:
368
+ * const client = new PlaycademyClient({ baseUrl: '/api' })
369
+ * const result = await client.auth.login({
370
+ * email: 'user@example.com',
371
+ * password: 'password'
372
+ * })
418
373
  *
419
- * @module types/timeback/client
374
+ * // Legacy approach (still works):
375
+ * try {
376
+ * const response = await PlaycademyClient.login('/api', 'user@example.com', 'password')
377
+ * const client = new PlaycademyClient({ token: response.token })
378
+ * } catch (error) {
379
+ * console.error('Login failed:', error.message)
380
+ * }
381
+ * ```
420
382
  */
421
-
422
- /**
423
- * Known extensions for TimeBack Activity Metrics Collection
424
- */
425
- interface TimebackActivityExtensions {
426
- /** Percentage complete (0-100) for the app course */
427
- pctCompleteApp?: number;
428
- /** Allow other arbitrary extensions */
429
- [key: string]: unknown;
430
- }
431
- /**
432
- * Activity data for ending an activity
433
- */
434
- interface ActivityData {
435
- /** Unique activity identifier (required) */
436
- activityId: string;
437
- /** Grade level for this activity (required for multi-grade course routing) */
438
- grade: number;
439
- /** Subject area (required for multi-grade course routing) */
440
- subject: CaliperSubject;
441
- /** Activity display name (optional) */
442
- activityName?: string;
443
- /** Course identifier (auto-filled from config if not provided) */
444
- courseId?: string;
445
- /** Course display name (auto-filled from config if not provided) */
446
- courseName?: string;
447
- /** Student email address (optional) */
448
- studentEmail?: string;
449
- /** Application name for Caliper events (defaults to 'Game') */
450
- appName?: string;
451
- /** Sensor URL for Caliper events (defaults to baseUrl) */
452
- sensorUrl?: string;
453
- }
454
- /**
455
- * Score data with optional XP override for ending an activity
456
- */
457
- interface EndActivityScoreData {
458
- /** Number of questions answered correctly */
459
- correctQuestions: number;
460
- /** Total number of questions */
461
- totalQuestions: number;
462
- /** Optional XP override - bypasses automatic XP calculation */
463
- xpAwarded?: number;
464
- /** Number of learning units mastered */
465
- masteredUnits?: number;
466
- /** Optional arbitrary extensions to include in the Caliper event */
467
- extensions?: TimebackActivityExtensions;
468
- }
469
-
470
- /**
471
- * TimeBack API Request/Response Types
472
- *
473
- * Types for TimeBack API endpoints including XP tracking,
474
- * setup, verification, and activity completion.
475
- *
476
- * @module types/timeback/api
477
- */
478
-
479
- type TimebackPromotionStatus = 'promoted' | 'no-next-course' | 'already-promoted' | 'not-enrolled' | 'not-mastered';
480
- interface TimebackPromotionResult {
481
- status: TimebackPromotionStatus;
482
- currentCourseId: string;
483
- nextCourseId?: string;
484
- masteredUnits?: number;
485
- masterableUnits?: number;
486
- }
487
- interface EndActivityResponse {
488
- status: 'ok';
489
- courseId: string;
490
- xpAwarded: number;
491
- masteredUnits?: number;
492
- pctCompleteApp?: number;
493
- scoreStatus?: string;
494
- inProgress?: string;
495
- }
496
- interface AdvanceCourseResponse {
497
- status: 'ok';
498
- promotion: TimebackPromotionResult;
499
- }
500
-
501
- /**
502
- * Cache configuration types for runtime customization
503
- */
504
- /**
505
- * Runtime configuration for TTL cache behavior
506
- */
507
- interface TTLCacheConfig {
508
- /** Time-to-live in milliseconds. Set to 0 to disable caching for this call. */
509
- ttl?: number;
510
- /** Force refresh, bypassing cache */
511
- force?: boolean;
512
- /** Skip cache and fetch fresh data (alias for force) */
513
- skipCache?: boolean;
514
- }
515
-
516
- /**
517
- * User Types
518
- *
519
- * Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
520
- *
521
- * @module types/user
522
- */
523
-
524
- type UserRoleEnumType = UserRole;
525
- type DeveloperStatusEnumType = 'none' | 'pending' | 'approved';
526
- type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
527
- type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
528
- interface UserEnrollment {
529
- gameId?: string;
530
- courseId: string;
531
- enrollmentIds?: {
532
- active: string;
533
- inactive?: string[];
534
- };
535
- grade: number;
536
- subject: string;
537
- orgId?: string;
538
- }
539
- interface UserOrganization {
540
- id: string;
541
- name: string | null;
542
- type: TimebackOrgType | string;
543
- isPrimary: boolean;
544
- }
545
- interface TimebackStudentProfile {
546
- role: TimebackUserRole;
547
- organizations: UserOrganization[];
548
- }
549
- interface UserTimebackData extends TimebackStudentProfile {
550
- id: string;
551
- enrollments: UserEnrollment[];
552
- }
553
- /**
554
- * OpenID Connect UserInfo claims (NOT a database row).
555
- */
556
- interface UserInfo {
557
- sub: string;
558
- email: string;
559
- name: string | null;
560
- email_verified?: boolean;
561
- given_name?: string;
562
- family_name?: string;
563
- issuer?: string;
564
- lti_roles?: unknown;
565
- lti_context?: unknown;
566
- lti_resource_link?: unknown;
567
- timeback_id?: string;
568
- }
569
- interface DemoProfile {
570
- displayName: string;
571
- isDefault: boolean;
572
- }
573
- /**
574
- * Update shape for `client.demo.profile.update(...)`.
575
- *
576
- * Kept as a named type so callers typed against it pick up new fields
577
- * automatically, but `displayName` is the only updatable field today and
578
- * the server's `DemoProfileSchema` requires it — a no-field payload would
579
- * 400 at runtime, so we model that at the type level too. When additional
580
- * fields land, make them required/optional individually based on server
581
- * validation, rather than blanket-optional.
582
- */
583
- interface DemoProfileUpdate {
584
- displayName: string;
585
- }
586
- /**
587
- * Authenticated user for API responses.
588
- * Differs from UserRow: omits timebackId, adds hasTimebackAccount and timeback.
589
- */
590
- interface AuthenticatedUser {
591
- id: string;
592
- email: string;
593
- emailVerified: boolean;
594
- name: string | null;
595
- image: string | null;
596
- username: string | null;
597
- role: UserRoleEnumType;
598
- developerStatus: DeveloperStatusEnumType;
599
- characterCreated: boolean;
600
- createdAt: Date;
601
- updatedAt: Date;
602
- hasTimebackAccount: boolean;
603
- timeback?: UserTimebackData;
604
- }
605
-
606
- /**
607
- * Leaderboard Types
608
- *
609
- * @module types/leaderboard
610
- */
611
- type LeaderboardTimeframe = 'all_time' | 'monthly' | 'weekly' | 'daily';
612
- interface LeaderboardOptions {
613
- timeframe?: LeaderboardTimeframe;
614
- limit?: number;
615
- offset?: number;
616
- gameId?: string;
617
- }
618
- /**
619
- * Leaderboard entry with required game context.
620
- * Used when fetching leaderboards for a specific game.
621
- */
622
- interface GameLeaderboardEntry {
623
- rank: number;
624
- userId: string;
625
- username: string;
626
- userImage?: string | null;
627
- score: number;
628
- achievedAt: Date;
629
- metadata?: Record<string, unknown>;
630
- gameId: string;
631
- gameTitle: string;
632
- gameSlug: string;
633
- }
634
-
635
- declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
636
- name: "items";
637
- schema: undefined;
638
- columns: {
639
- id: drizzle_orm_pg_core.PgColumn<{
640
- name: "id";
641
- tableName: "items";
642
- dataType: "string";
643
- columnType: "PgUUID";
644
- data: string;
645
- driverParam: string;
646
- notNull: true;
647
- hasDefault: true;
648
- isPrimaryKey: true;
649
- isAutoincrement: false;
650
- hasRuntimeDefault: false;
651
- enumValues: undefined;
652
- baseColumn: never;
653
- identity: undefined;
654
- generated: undefined;
655
- }, {}, {}>;
656
- slug: drizzle_orm_pg_core.PgColumn<{
657
- name: "slug";
658
- tableName: "items";
659
- dataType: "string";
660
- columnType: "PgText";
661
- data: string;
662
- driverParam: string;
663
- notNull: true;
664
- hasDefault: false;
665
- isPrimaryKey: false;
666
- isAutoincrement: false;
667
- hasRuntimeDefault: false;
668
- enumValues: [string, ...string[]];
669
- baseColumn: never;
670
- identity: undefined;
671
- generated: undefined;
672
- }, {}, {}>;
673
- gameId: drizzle_orm_pg_core.PgColumn<{
674
- name: "game_id";
675
- tableName: "items";
676
- dataType: "string";
677
- columnType: "PgUUID";
678
- data: string;
679
- driverParam: string;
680
- notNull: false;
681
- hasDefault: false;
682
- isPrimaryKey: false;
683
- isAutoincrement: false;
684
- hasRuntimeDefault: false;
685
- enumValues: undefined;
686
- baseColumn: never;
687
- identity: undefined;
688
- generated: undefined;
689
- }, {}, {}>;
690
- displayName: drizzle_orm_pg_core.PgColumn<{
691
- name: "display_name";
692
- tableName: "items";
693
- dataType: "string";
694
- columnType: "PgText";
695
- data: string;
696
- driverParam: string;
697
- notNull: true;
698
- hasDefault: false;
699
- isPrimaryKey: false;
700
- isAutoincrement: false;
701
- hasRuntimeDefault: false;
702
- enumValues: [string, ...string[]];
703
- baseColumn: never;
704
- identity: undefined;
705
- generated: undefined;
706
- }, {}, {}>;
707
- description: drizzle_orm_pg_core.PgColumn<{
708
- name: "description";
709
- tableName: "items";
710
- dataType: "string";
711
- columnType: "PgText";
712
- data: string;
713
- driverParam: string;
714
- notNull: false;
715
- hasDefault: false;
716
- isPrimaryKey: false;
717
- isAutoincrement: false;
718
- hasRuntimeDefault: false;
719
- enumValues: [string, ...string[]];
720
- baseColumn: never;
721
- identity: undefined;
722
- generated: undefined;
723
- }, {}, {}>;
724
- type: drizzle_orm_pg_core.PgColumn<{
725
- name: "type";
726
- tableName: "items";
727
- dataType: "string";
728
- columnType: "PgEnumColumn";
729
- data: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade";
730
- driverParam: string;
731
- notNull: true;
732
- hasDefault: true;
733
- isPrimaryKey: false;
734
- isAutoincrement: false;
735
- hasRuntimeDefault: false;
736
- enumValues: ["currency", "badge", "trophy", "collectible", "consumable", "unlock", "upgrade", "accessory", "other"];
737
- baseColumn: never;
738
- identity: undefined;
739
- generated: undefined;
740
- }, {}, {}>;
741
- isPlaceable: drizzle_orm_pg_core.PgColumn<{
742
- name: "is_placeable";
743
- tableName: "items";
744
- dataType: "boolean";
745
- columnType: "PgBoolean";
746
- data: boolean;
747
- driverParam: boolean;
748
- notNull: true;
749
- hasDefault: true;
750
- isPrimaryKey: false;
751
- isAutoincrement: false;
752
- hasRuntimeDefault: false;
753
- enumValues: undefined;
754
- baseColumn: never;
755
- identity: undefined;
756
- generated: undefined;
757
- }, {}, {}>;
758
- imageUrl: drizzle_orm_pg_core.PgColumn<{
759
- name: "image_url";
760
- tableName: "items";
761
- dataType: "string";
762
- columnType: "PgText";
763
- data: string;
764
- driverParam: string;
765
- notNull: false;
766
- hasDefault: false;
767
- isPrimaryKey: false;
768
- isAutoincrement: false;
769
- hasRuntimeDefault: false;
770
- enumValues: [string, ...string[]];
771
- baseColumn: never;
772
- identity: undefined;
773
- generated: undefined;
774
- }, {}, {}>;
775
- metadata: drizzle_orm_pg_core.PgColumn<{
776
- name: "metadata";
777
- tableName: "items";
778
- dataType: "json";
779
- columnType: "PgJsonb";
780
- data: unknown;
781
- driverParam: unknown;
782
- notNull: false;
783
- hasDefault: true;
784
- isPrimaryKey: false;
785
- isAutoincrement: false;
786
- hasRuntimeDefault: false;
787
- enumValues: undefined;
788
- baseColumn: never;
789
- identity: undefined;
790
- generated: undefined;
791
- }, {}, {}>;
792
- createdAt: drizzle_orm_pg_core.PgColumn<{
793
- name: "created_at";
794
- tableName: "items";
795
- dataType: "date";
796
- columnType: "PgTimestamp";
797
- data: Date;
798
- driverParam: string;
799
- notNull: true;
800
- hasDefault: true;
801
- isPrimaryKey: false;
802
- isAutoincrement: false;
803
- hasRuntimeDefault: false;
804
- enumValues: undefined;
805
- baseColumn: never;
806
- identity: undefined;
807
- generated: undefined;
808
- }, {}, {}>;
809
- };
810
- dialect: 'pg';
811
- }>;
812
- declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
813
- name: "inventory_items";
814
- schema: undefined;
815
- columns: {
816
- id: drizzle_orm_pg_core.PgColumn<{
817
- name: "id";
818
- tableName: "inventory_items";
819
- dataType: "string";
820
- columnType: "PgUUID";
821
- data: string;
822
- driverParam: string;
823
- notNull: true;
824
- hasDefault: true;
825
- isPrimaryKey: true;
826
- isAutoincrement: false;
827
- hasRuntimeDefault: false;
828
- enumValues: undefined;
829
- baseColumn: never;
830
- identity: undefined;
831
- generated: undefined;
832
- }, {}, {}>;
833
- userId: drizzle_orm_pg_core.PgColumn<{
834
- name: "user_id";
835
- tableName: "inventory_items";
836
- dataType: "string";
837
- columnType: "PgText";
838
- data: string;
839
- driverParam: string;
840
- notNull: true;
841
- hasDefault: false;
842
- isPrimaryKey: false;
843
- isAutoincrement: false;
844
- hasRuntimeDefault: false;
845
- enumValues: [string, ...string[]];
846
- baseColumn: never;
847
- identity: undefined;
848
- generated: undefined;
849
- }, {}, {}>;
850
- itemId: drizzle_orm_pg_core.PgColumn<{
851
- name: "item_id";
852
- tableName: "inventory_items";
853
- dataType: "string";
854
- columnType: "PgUUID";
855
- data: string;
856
- driverParam: string;
857
- notNull: true;
858
- hasDefault: false;
859
- isPrimaryKey: false;
860
- isAutoincrement: false;
861
- hasRuntimeDefault: false;
862
- enumValues: undefined;
863
- baseColumn: never;
864
- identity: undefined;
865
- generated: undefined;
866
- }, {}, {}>;
867
- quantity: drizzle_orm_pg_core.PgColumn<{
868
- name: "quantity";
869
- tableName: "inventory_items";
870
- dataType: "number";
871
- columnType: "PgInteger";
872
- data: number;
873
- driverParam: string | number;
874
- notNull: true;
875
- hasDefault: true;
876
- isPrimaryKey: false;
877
- isAutoincrement: false;
878
- hasRuntimeDefault: false;
879
- enumValues: undefined;
880
- baseColumn: never;
881
- identity: undefined;
882
- generated: undefined;
883
- }, {}, {}>;
884
- updatedAt: drizzle_orm_pg_core.PgColumn<{
885
- name: "updated_at";
886
- tableName: "inventory_items";
887
- dataType: "date";
888
- columnType: "PgTimestamp";
889
- data: Date;
890
- driverParam: string;
891
- notNull: false;
892
- hasDefault: true;
893
- isPrimaryKey: false;
894
- isAutoincrement: false;
895
- hasRuntimeDefault: false;
896
- enumValues: undefined;
897
- baseColumn: never;
898
- identity: undefined;
899
- generated: undefined;
900
- }, {}, {}>;
901
- };
902
- dialect: 'pg';
903
- }>;
904
-
905
- type ItemRow = typeof items.$inferSelect;
906
- type InventoryItemRow = typeof inventoryItems.$inferSelect;
907
- type InventoryItemWithItem = InventoryItemRow & {
908
- item: ItemRow;
909
- };
910
-
911
- /**
912
- * Auto-initializes a PlaycademyClient with context from the environment.
913
- * Works in both iframe mode (production/development) and standalone mode (local dev).
914
- *
915
- * This is the recommended way to initialize the SDK as it automatically:
916
- * - Detects the runtime environment (iframe vs standalone)
917
- * - Configures the client with the appropriate context
918
- * - Sets up event listeners for token refresh
919
- * - Exposes the client for debugging in development mode
920
- *
921
- * @param options - Optional configuration overrides
922
- * @param options.baseUrl - Override the base URL for API requests
923
- * @returns Promise resolving to a fully initialized PlaycademyClient
924
- * @throws Error if not running in a browser context
925
- *
926
- * @example
927
- * ```typescript
928
- * // Default initialization
929
- * const client = await PlaycademyClient.init()
930
- *
931
- * // With custom base URL
932
- * const client = await PlaycademyClient.init({ baseUrl: 'https://custom.api.com' })
933
- * ```
934
- */
935
- declare function init<T extends PlaycademyBaseClient = PlaycademyBaseClient>(this: new (config?: Partial<ClientConfig>) => T, options?: {
936
- baseUrl?: string;
937
- allowedParentOrigins?: string[];
938
- onDisconnect?: DisconnectHandler;
939
- enableConnectionMonitoring?: boolean;
940
- }): Promise<T>;
941
-
942
- /**
943
- * Authenticates a user with email and password.
944
- *
945
- * This is a standalone authentication method that doesn't require an initialized client.
946
- * Use this for login flows before creating a client instance.
947
- *
948
- * @deprecated Use client.auth.login() instead for better error handling and automatic token management
949
- *
950
- * @param baseUrl - The base URL of the Playcademy API
951
- * @param email - User's email address
952
- * @param password - User's password
953
- * @returns Promise resolving to authentication response with token
954
- * @throws PlaycademyError if authentication fails or network error occurs
955
- *
956
- * @example
957
- * ```typescript
958
- * // Preferred approach:
959
- * const client = new PlaycademyClient({ baseUrl: '/api' })
960
- * const result = await client.auth.login({
961
- * email: 'user@example.com',
962
- * password: 'password'
963
- * })
964
- *
965
- * // Legacy approach (still works):
966
- * try {
967
- * const response = await PlaycademyClient.login('/api', 'user@example.com', 'password')
968
- * const client = new PlaycademyClient({ token: response.token })
969
- * } catch (error) {
970
- * console.error('Login failed:', error.message)
971
- * }
972
- * ```
973
- */
974
- declare function login(baseUrl: string, email: string, password: string): Promise<LoginResponse>;
383
+ declare function login(baseUrl: string, email: string, password: string): Promise<LoginResponse>;
975
384
 
976
385
  /**
977
386
  * @fileoverview Playcademy Messaging System
@@ -1054,14 +463,6 @@ declare enum MessageEvents {
1054
463
  * Payload: boolean (true = show overlay, false = hide overlay)
1055
464
  */
1056
465
  OVERLAY = "PLAYCADEMY_OVERLAY",
1057
- /**
1058
- * Broadcasts connection state changes to games.
1059
- * Sent by platform when network connectivity changes.
1060
- * Payload:
1061
- * - `state`: 'online' | 'offline' | 'degraded'
1062
- * - `reason`: string
1063
- */
1064
- CONNECTION_STATE = "PLAYCADEMY_CONNECTION_STATE",
1065
466
  /**
1066
467
  * Game has finished loading and is ready to receive messages.
1067
468
  * Sent once after game initialization is complete.
@@ -1099,14 +500,6 @@ declare enum MessageEvents {
1099
500
  * - `type`: 'keydown' | 'keyup'
1100
501
  */
1101
502
  KEY_EVENT = "PLAYCADEMY_KEY_EVENT",
1102
- /**
1103
- * Game requests platform to display an alert.
1104
- * Sent when connection issues are detected or other important events occur.
1105
- * Payload:
1106
- * - `message`: string
1107
- * - `options`: `{ type?: 'info' | 'warning' | 'error', duration?: number }`
1108
- */
1109
- DISPLAY_ALERT = "PLAYCADEMY_DISPLAY_ALERT",
1110
503
  /**
1111
504
  * Game signals that demo mode has ended.
1112
505
  * Sent when a demo experience reaches its CTA/upgrade boundary.
@@ -1171,8 +564,6 @@ interface MessageEventMap {
1171
564
  [MessageEvents.FORCE_EXIT]: void;
1172
565
  /** Overlay visibility state (true = show, false = hide) */
1173
566
  [MessageEvents.OVERLAY]: boolean;
1174
- /** Connection state change from platform */
1175
- [MessageEvents.CONNECTION_STATE]: ConnectionStatePayload;
1176
567
  /** Ready message has no payload data */
1177
568
  [MessageEvents.READY]: void;
1178
569
  /** SDK init error data */
@@ -1183,8 +574,6 @@ interface MessageEventMap {
1183
574
  [MessageEvents.TELEMETRY]: TelemetryPayload;
1184
575
  /** Key event data */
1185
576
  [MessageEvents.KEY_EVENT]: KeyEventPayload;
1186
- /** Display alert request from game */
1187
- [MessageEvents.DISPLAY_ALERT]: DisplayAlertPayload;
1188
577
  /** Demo end signal from game */
1189
578
  [MessageEvents.DEMO_END]: DemoEndPayload;
1190
579
  /** Authentication state change notification */
@@ -1460,322 +849,103 @@ declare class PlaycademyMessaging {
1460
849
  */
1461
850
  private sendViaPostMessage;
1462
851
  /**
1463
- * **Send Via CustomEvent Method**
1464
- *
1465
- * Sends a message using the browser's CustomEvent API for local same-context communication.
1466
- * This method is used when both the sender and receiver are in the same window context,
1467
- * typically during local development or when the parent needs to send messages to the game.
1468
- *
1469
- * **CustomEvent Protocol**:
1470
- * CustomEvent is a browser API for creating and dispatching custom events within the same
1471
- * window context. It's simpler than postMessage but only works within the same origin/context.
1472
- *
1473
- * **Event Structure**:
1474
- * - **type**: The event name (e.g., 'PLAYCADEMY_INIT')
1475
- * - **detail**: The payload data attached to the event
1476
- *
1477
- * **When This Is Used**:
1478
- * - Local development when game and shell run in the same context
1479
- * - Parent-to-game communication (INIT, TOKEN_REFRESH, PAUSE, etc.)
1480
- * - Any scenario where postMessage isn't needed
1481
- *
1482
- * **Event Bubbling**:
1483
- * CustomEvents are dispatched on the window object and can be listened to by any
1484
- * code in the same context using `addEventListener(type, handler)`.
1485
- *
1486
- * @template K - The message event type (ensures type safety)
1487
- * @param type - The message event type to send (becomes the event name)
1488
- * @param payload - The data to send with the message (stored in event.detail)
1489
- *
1490
- * @example
1491
- * ```typescript
1492
- * // Send initialization data
1493
- * sendViaCustomEvent(MessageEvents.INIT, {
1494
- * baseUrl: '/api',
1495
- * token: 'abc123',
1496
- * gameId: 'game-456'
1497
- * })
1498
- * // Creates: CustomEvent('PLAYCADEMY_INIT', { detail: { baseUrl, token, gameId } })
1499
- *
1500
- * // Send pause signal
1501
- * sendViaCustomEvent(MessageEvents.PAUSE, undefined)
1502
- * // Creates: CustomEvent('PLAYCADEMY_PAUSE', { detail: undefined })
1503
- *
1504
- * // Listeners can access the data via event.detail:
1505
- * window.addEventListener('PLAYCADEMY_INIT', (event) => {
1506
- * console.log(event.detail.gameId) // 'game-456'
1507
- * })
1508
- * ```
1509
- */
1510
- private sendViaCustomEvent;
1511
- }
1512
- /**
1513
- * **Playcademy Messaging Singleton**
1514
- *
1515
- * This is the main messaging instance used throughout the Playcademy platform.
1516
- * It's exported as a singleton to ensure consistent communication across all parts
1517
- * of the application.
1518
- *
1519
- * **Why a Singleton?**:
1520
- * - Ensures all parts of the app use the same messaging instance
1521
- * - Prevents conflicts between multiple messaging systems
1522
- * - Simplifies the API - no need to pass instances around
1523
- * - Maintains consistent event listener management
1524
- *
1525
- * **Usage in Different Contexts**:
1526
- *
1527
- * **In Games**:
1528
- * ```typescript
1529
- * import { messaging, MessageEvents } from '@playcademy/sdk'
1530
- *
1531
- * // Tell parent we're ready
1532
- * messaging.send(MessageEvents.READY, undefined)
1533
- *
1534
- * // Listen for pause/resume
1535
- * messaging.listen(MessageEvents.PAUSE, () => game.pause())
1536
- * messaging.listen(MessageEvents.RESUME, () => game.resume())
1537
- * ```
1538
- *
1539
- * **In Parent Shell**:
1540
- * ```typescript
1541
- * import { messaging, MessageEvents } from '@playcademy/sdk'
1542
- *
1543
- * // Send initialization data to game
1544
- * messaging.send(MessageEvents.INIT, { baseUrl, token, gameId })
1545
- *
1546
- * // Listen for game events
1547
- * messaging.listen(MessageEvents.EXIT, () => closeGame())
1548
- * messaging.listen(MessageEvents.READY, () => showGame())
1549
- * ```
1550
- *
1551
- * **Automatic Transport Selection**:
1552
- * The messaging system automatically chooses the right transport method:
1553
- * - Uses postMessage when game is in iframe sending to parent
1554
- * - Uses CustomEvent for local development and parent-to-game communication
1555
- *
1556
- * **Type Safety**:
1557
- * All message sending and receiving is fully type-safe with TypeScript.
1558
- */
1559
- declare const messaging: PlaycademyMessaging;
1560
-
1561
- /**
1562
- * @fileoverview Authentication Strategy Pattern
1563
- *
1564
- * Provides different authentication strategies for the Playcademy SDK.
1565
- * Each strategy knows how to add its authentication headers to requests.
1566
- */
1567
-
1568
- /**
1569
- * Base interface for authentication strategies
1570
- */
1571
- interface AuthStrategy {
1572
- /** Get the token value */
1573
- getToken(): string | null;
1574
- /** Get the token type */
1575
- getType(): TokenType;
1576
- /** Get authentication headers for a request */
1577
- getHeaders(): Record<string, string>;
1578
- }
1579
-
1580
- /**
1581
- * Base Playcademy SDK client with shared infrastructure.
1582
- * Provides authentication, HTTP requests, events, connection monitoring,
1583
- * and fundamental namespaces used by all clients.
1584
- *
1585
- * Extended by PlaycademyClient (game SDK) and PlaycademyInternalClient (platform SDK).
1586
- */
1587
- declare abstract class PlaycademyBaseClient {
1588
- baseUrl: string;
1589
- gameUrl?: string;
1590
- mode: PlaycademyMode;
1591
- protected authStrategy: AuthStrategy;
1592
- protected gameId?: string;
1593
- protected config: Partial<ClientConfig>;
1594
- protected listeners: EventListeners;
1595
- protected internalClientSessionId?: string;
1596
- protected authContext?: {
1597
- isInIframe: boolean;
1598
- };
1599
- protected initPayload?: InitPayload;
1600
- protected connectionManager?: ConnectionManager;
1601
- protected launchId?: string;
1602
- /**
1603
- * Internal session manager for automatic session lifecycle.
1604
- * @private
1605
- * @internal
1606
- */
1607
- protected _sessionManager: {
1608
- startSession: (gameId: string) => Promise<{
1609
- sessionId: string;
1610
- }>;
1611
- endSession: (sessionId: string, gameId: string) => Promise<void>;
1612
- };
1613
- constructor(config?: Partial<ClientConfig>);
1614
- /**
1615
- * Gets the effective base URL for API requests.
1616
- */
1617
- getBaseUrl(): string;
1618
- /**
1619
- * Gets the effective game backend URL for integration requests.
1620
- */
1621
- protected getGameBackendUrl(): string;
1622
- /**
1623
- * Simple ping method for testing connectivity.
1624
- */
1625
- ping(): string;
1626
- /**
1627
- * Sets the authentication token for API requests.
1628
- */
1629
- setToken(token: string | null, tokenType?: TokenType): void;
1630
- setLaunchId(launchId: string | null | undefined): void;
1631
- /**
1632
- * Gets the current token type.
1633
- */
1634
- getTokenType(): TokenType;
1635
- /**
1636
- * Gets the current authentication token.
1637
- */
1638
- getToken(): string | null;
1639
- /**
1640
- * Checks if the client has a valid API token.
1641
- */
1642
- isAuthenticated(): boolean;
1643
- /**
1644
- * Registers a callback to be called when authentication state changes.
1645
- */
1646
- onAuthChange(callback: (token: string | null) => void): void;
1647
- /**
1648
- * Registers a callback to be called when connection issues are detected.
1649
- */
1650
- onDisconnect(callback: (context: DisconnectContext) => void | Promise<void>): () => void;
1651
- /**
1652
- * Gets the current connection state.
1653
- */
1654
- getConnectionState(): ConnectionState | 'unknown';
1655
- /**
1656
- * Manually triggers a connection check immediately.
1657
- */
1658
- checkConnection(): Promise<ConnectionState | 'unknown'>;
1659
- /**
1660
- * Sets the authentication context for the client.
1661
- * @internal
1662
- */
1663
- _setAuthContext(context: {
1664
- isInIframe: boolean;
1665
- }): void;
1666
- /**
1667
- * Registers an event listener for client events.
1668
- *
1669
- * @param event - The event name to listen for.
1670
- * @param callback - The handler invoked when the event fires.
1671
- * @returns A cleanup function that removes this specific listener.
1672
- */
1673
- on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): () => void;
1674
- /**
1675
- * Removes a previously registered event listener.
1676
- *
1677
- * @param event - The event name to stop listening for.
1678
- * @param callback - The exact function reference passed to {@link on}.
1679
- */
1680
- off<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
1681
- /**
1682
- * Emits an event to all registered listeners.
1683
- */
1684
- protected emit<E extends keyof ClientEvents>(event: E, payload: ClientEvents[E]): void;
1685
- /**
1686
- * Makes an authenticated HTTP request to the platform API.
1687
- */
1688
- protected request<T>(path: string, method: Method, options?: {
1689
- body?: unknown;
1690
- headers?: Record<string, string>;
1691
- raw?: boolean;
1692
- retryPolicy?: RetryPolicy;
1693
- }): Promise<T>;
1694
- /**
1695
- * Makes an authenticated HTTP request to the game's backend Worker.
1696
- */
1697
- protected requestGameBackend<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>, options?: {
1698
- raw?: boolean;
1699
- retryPolicy?: RetryPolicy;
1700
- }): Promise<T>;
1701
- /**
1702
- * Ensures a gameId is available, throwing an error if not.
1703
- */
1704
- protected _ensureGameId(): string;
1705
- /**
1706
- * Detects and sets the authentication context (iframe vs standalone).
1707
- */
1708
- private _detectAuthContext;
1709
- /**
1710
- * Initializes connection monitoring if enabled.
1711
- */
1712
- private _initializeConnectionMonitor;
1713
- private _initializeInternalSession;
1714
- /**
1715
- * Current user data and inventory management.
1716
- * - `me()` - Get authenticated user profile
1717
- * - `inventory.get()` - List user's items
1718
- * - `inventory.add(slug, qty)` - Award items to user
1719
- */
1720
- users: {
1721
- me: () => Promise<AuthenticatedUser>;
1722
- inventory: {
1723
- get: () => Promise<InventoryItemWithItem[]>;
1724
- add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1725
- remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1726
- quantity: (identifier: string) => Promise<number>;
1727
- has: (identifier: string, minQuantity?: number) => Promise<boolean>;
1728
- };
1729
- };
1730
- }
1731
-
1732
- /**
1733
- * Options for configuring activity tracking behavior.
1734
- */
1735
- interface StartActivityOptions {
1736
- /**
1737
- * How long heartbeats continue after the activity is automatically paused
1738
- * because the tab is hidden or the player is inactive while visible.
1739
- * Defaults to 10 minutes. Set to `Infinity` to keep heartbeats running
1740
- * indefinitely during automatic pauses. Invalid values fall back to the
1741
- * 10-minute default.
1742
- */
1743
- pausedHeartbeatTimeoutMs?: number;
1744
- /**
1745
- * @deprecated Use `pausedHeartbeatTimeoutMs` instead.
1746
- *
1747
- * Backward-compatible alias for callers that still use the old option
1748
- * name from earlier SDK releases.
1749
- */
1750
- hiddenTimeoutMs?: number;
1751
- /**
1752
- * How often to flush periodic heartbeats with accumulated time data.
1753
- * Defaults to 15 seconds. Set to `Infinity` to disable the interval;
1754
- * final unload/endActivity flushes still run. Values must be greater than
1755
- * 0 or `Infinity`; invalid values fall back to the 15-second default.
1756
- */
1757
- heartbeatIntervalMs?: number;
1758
- /**
1759
- * How long the tab can remain visible without keyboard or mouse activity
1760
- * before the activity is marked inactive. Defaults to 10 minutes. Set to
1761
- * `Infinity` to disable keyboard/mouse inactivity tracking. Invalid values
1762
- * fall back to the 10-minute default.
1763
- */
1764
- inactivityTimeoutMs?: number;
1765
- /**
1766
- * Stable identifier for this activity run. When provided, it is used on
1767
- * every heartbeat and on endActivity instead of a freshly-generated UUID.
852
+ * **Send Via CustomEvent Method**
1768
853
  *
1769
- * Pass the same `runId` across multiple `startActivity()` calls (for
1770
- * example, after the player closes and reopens a resumable activity) so
1771
- * downstream systems can correlate related sessions into a single run.
854
+ * Sends a message using the browser's CustomEvent API for local same-context communication.
855
+ * This method is used when both the sender and receiver are in the same window context,
856
+ * typically during local development or when the parent needs to send messages to the game.
1772
857
  *
1773
- * Must be a UUID (the backend validates it as such) and unique per
1774
- * logical run. If omitted, the SDK generates a new UUID on each call,
1775
- * which means every session is treated as its own run.
858
+ * **CustomEvent Protocol**:
859
+ * CustomEvent is a browser API for creating and dispatching custom events within the same
860
+ * window context. It's simpler than postMessage but only works within the same origin/context.
861
+ *
862
+ * **Event Structure**:
863
+ * - **type**: The event name (e.g., 'PLAYCADEMY_INIT')
864
+ * - **detail**: The payload data attached to the event
865
+ *
866
+ * **When This Is Used**:
867
+ * - Local development when game and shell run in the same context
868
+ * - Parent-to-game communication (INIT, TOKEN_REFRESH, PAUSE, etc.)
869
+ * - Any scenario where postMessage isn't needed
870
+ *
871
+ * **Event Bubbling**:
872
+ * CustomEvents are dispatched on the window object and can be listened to by any
873
+ * code in the same context using `addEventListener(type, handler)`.
874
+ *
875
+ * @template K - The message event type (ensures type safety)
876
+ * @param type - The message event type to send (becomes the event name)
877
+ * @param payload - The data to send with the message (stored in event.detail)
878
+ *
879
+ * @example
880
+ * ```typescript
881
+ * // Send initialization data
882
+ * sendViaCustomEvent(MessageEvents.INIT, {
883
+ * baseUrl: '/api',
884
+ * token: 'abc123',
885
+ * gameId: 'game-456'
886
+ * })
887
+ * // Creates: CustomEvent('PLAYCADEMY_INIT', { detail: { baseUrl, token, gameId } })
888
+ *
889
+ * // Send pause signal
890
+ * sendViaCustomEvent(MessageEvents.PAUSE, undefined)
891
+ * // Creates: CustomEvent('PLAYCADEMY_PAUSE', { detail: undefined })
892
+ *
893
+ * // Listeners can access the data via event.detail:
894
+ * window.addEventListener('PLAYCADEMY_INIT', (event) => {
895
+ * console.log(event.detail.gameId) // 'game-456'
896
+ * })
897
+ * ```
1776
898
  */
1777
- runId?: string;
899
+ private sendViaCustomEvent;
1778
900
  }
901
+ /**
902
+ * **Playcademy Messaging Singleton**
903
+ *
904
+ * This is the main messaging instance used throughout the Playcademy platform.
905
+ * It's exported as a singleton to ensure consistent communication across all parts
906
+ * of the application.
907
+ *
908
+ * **Why a Singleton?**:
909
+ * - Ensures all parts of the app use the same messaging instance
910
+ * - Prevents conflicts between multiple messaging systems
911
+ * - Simplifies the API - no need to pass instances around
912
+ * - Maintains consistent event listener management
913
+ *
914
+ * **Usage in Different Contexts**:
915
+ *
916
+ * **In Games**:
917
+ * ```typescript
918
+ * import { messaging, MessageEvents } from '@playcademy/sdk'
919
+ *
920
+ * // Tell parent we're ready
921
+ * messaging.send(MessageEvents.READY, undefined)
922
+ *
923
+ * // Listen for pause/resume
924
+ * messaging.listen(MessageEvents.PAUSE, () => game.pause())
925
+ * messaging.listen(MessageEvents.RESUME, () => game.resume())
926
+ * ```
927
+ *
928
+ * **In Parent Shell**:
929
+ * ```typescript
930
+ * import { messaging, MessageEvents } from '@playcademy/sdk'
931
+ *
932
+ * // Send initialization data to game
933
+ * messaging.send(MessageEvents.INIT, { baseUrl, token, gameId })
934
+ *
935
+ * // Listen for game events
936
+ * messaging.listen(MessageEvents.EXIT, () => closeGame())
937
+ * messaging.listen(MessageEvents.READY, () => showGame())
938
+ * ```
939
+ *
940
+ * **Automatic Transport Selection**:
941
+ * The messaging system automatically chooses the right transport method:
942
+ * - Uses postMessage when game is in iframe sending to parent
943
+ * - Uses CustomEvent for local development and parent-to-game communication
944
+ *
945
+ * **Type Safety**:
946
+ * All message sending and receiving is fully type-safe with TypeScript.
947
+ */
948
+ declare const messaging: PlaycademyMessaging;
1779
949
 
1780
950
  /**
1781
951
  * Playcademy SDK client for game developers.
@@ -1803,7 +973,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1803
973
  getGameToken: (gameId: string, options?: {
1804
974
  apply?: boolean;
1805
975
  }) => Promise<GameTokenResponse>;
1806
- exit: () => Promise<void>;
976
+ exit: () => void;
1807
977
  onInit: (handler: (context: GameContextPayload) => void) => void;
1808
978
  onTokenRefresh: (handler: (data: {
1809
979
  token: string;
@@ -1844,31 +1014,23 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1844
1014
  * - `user.fetch()` - Refresh user context from server
1845
1015
  *
1846
1016
  * Activity tracking:
1847
- * - `startActivity(metadata)` - Begin tracking an activity with automatic
1848
- * hidden-tab and visible-tab inactivity handling, plus configurable
1849
- * paused-heartbeat timeout behavior
1017
+ * - `currentRunId` - Current activity run ID, or undefined when inactive
1018
+ * - `startActivity(metadata)` - Begin tracking an activity, return its run
1019
+ * ID, and automatically handle hidden-tab and visible-tab inactivity
1020
+ * with configurable paused-heartbeat timeout behavior
1850
1021
  * - `pauseActivity()` / `resumeActivity()` - Pause/resume timer
1851
1022
  * - `endActivity(scoreData)` - Submit activity results to TimeBack
1852
1023
  */
1853
1024
  timeback: {
1854
1025
  readonly user: TimebackUser;
1855
- startActivity: (metadata: ActivityData, options?: StartActivityOptions) => void;
1026
+ readonly currentRunId: string | undefined;
1027
+ startActivity: (metadata: _playcademy_types.ActivityData, options?: StartActivityOptions) => StartActivityResult;
1856
1028
  pauseActivity: () => void;
1857
1029
  resumeActivity: () => void;
1858
- endActivity: (data: EndActivityScoreData) => Promise<EndActivityResponse>;
1030
+ endActivity: (data: _playcademy_types.EndActivityScoreData) => Promise<_playcademy_types.EndActivityResponse>;
1859
1031
  advanceCourse: (options?: {
1860
- subject?: TimebackSubject;
1861
- }) => Promise<AdvanceCourseResponse>;
1862
- };
1863
- /**
1864
- * Playcademy Credits (platform currency) management.
1865
- * - `get()` - Get user's credit balance
1866
- * - `add(amount)` - Award credits to user
1867
- */
1868
- credits: {
1869
- balance: () => Promise<number>;
1870
- add: (amount: number) => Promise<number>;
1871
- spend: (amount: number) => Promise<number>;
1032
+ subject?: _playcademy_types.TimebackSubject;
1033
+ }) => Promise<_playcademy_types.AdvanceCourseResponse>;
1872
1034
  };
1873
1035
  /**
1874
1036
  * Game score submission and leaderboards.
@@ -1882,7 +1044,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1882
1044
  * - `fetch(options?)` - Fetch leaderboard entries
1883
1045
  */
1884
1046
  leaderboard: {
1885
- fetch: (options?: LeaderboardOptions) => Promise<GameLeaderboardEntry[]>;
1047
+ fetch: (options?: _playcademy_types.LeaderboardOptions) => Promise<_playcademy_types.GameLeaderboardEntry[]>;
1886
1048
  };
1887
1049
  /**
1888
1050
  * Demo-mode helpers. Methods throw when called outside `client.mode === 'demo'`,
@@ -1893,20 +1055,11 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1893
1055
  */
1894
1056
  demo: {
1895
1057
  profile: {
1896
- get: () => Promise<DemoProfile>;
1897
- update: (updates: DemoProfileUpdate) => Promise<DemoProfile>;
1058
+ get: () => Promise<_playcademy_types.DemoProfile>;
1059
+ update: (updates: _playcademy_types.DemoProfileUpdate) => Promise<_playcademy_types.DemoProfile>;
1898
1060
  };
1899
1061
  end: (score: number, options?: DemoEndOptions) => void;
1900
1062
  };
1901
- /**
1902
- * Realtime multiplayer authentication.
1903
- * - `getToken()` - Get token for WebSocket/realtime connections
1904
- */
1905
- realtime: {
1906
- token: {
1907
- get: () => Promise<RealtimeTokenResponse>;
1908
- };
1909
- };
1910
1063
  /**
1911
1064
  * Make requests to your game's custom backend API routes.
1912
1065
  * - `get(path)`, `post(path, body)`, `put()`, `delete()` - HTTP methods
@@ -1932,6 +1085,57 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1932
1085
  };
1933
1086
  }
1934
1087
 
1088
+ /**
1089
+ * Options for configuring activity tracking behavior.
1090
+ */
1091
+ interface StartActivityOptions {
1092
+ /**
1093
+ * How long heartbeats continue after the activity is automatically paused
1094
+ * because the tab is hidden or the player is inactive while visible.
1095
+ * Defaults to 10 minutes. Set to `Infinity` to keep heartbeats running
1096
+ * indefinitely during automatic pauses. Invalid values fall back to the
1097
+ * 10-minute default.
1098
+ */
1099
+ pausedHeartbeatTimeoutMs?: number;
1100
+ /**
1101
+ * @deprecated Use `pausedHeartbeatTimeoutMs` instead.
1102
+ *
1103
+ * Backward-compatible alias for callers that still use the old option
1104
+ * name from earlier SDK releases.
1105
+ */
1106
+ hiddenTimeoutMs?: number;
1107
+ /**
1108
+ * How often to flush periodic heartbeats with accumulated time data.
1109
+ * Defaults to 15 seconds. Set to `Infinity` to disable the interval;
1110
+ * final unload/endActivity flushes still run. Values must be greater than
1111
+ * 0 or `Infinity`; invalid values fall back to the 15-second default.
1112
+ */
1113
+ heartbeatIntervalMs?: number;
1114
+ /**
1115
+ * How long the tab can remain visible without keyboard or mouse activity
1116
+ * before the activity is marked inactive. Defaults to 10 minutes. Set to
1117
+ * `Infinity` to disable keyboard/mouse inactivity tracking. Invalid values
1118
+ * fall back to the 10-minute default.
1119
+ */
1120
+ inactivityTimeoutMs?: number;
1121
+ /**
1122
+ * Stable identifier for this activity run. When provided, it is used on
1123
+ * every heartbeat and on endActivity instead of a freshly-generated UUID.
1124
+ *
1125
+ * Pass the same `runId` across multiple `startActivity()` calls (for
1126
+ * example, after the player closes and reopens a resumable activity) so
1127
+ * downstream systems can correlate related sessions into a single run.
1128
+ *
1129
+ * Must be a UUID (the backend validates it as such) and unique per
1130
+ * logical run. If omitted, the SDK generates a new UUID on each call,
1131
+ * which means every session is treated as its own run.
1132
+ */
1133
+ runId?: string;
1134
+ }
1135
+ interface StartActivityResult {
1136
+ runId: string;
1137
+ }
1138
+
1935
1139
  /**
1936
1140
  * Type definitions for the game timeback namespace.
1937
1141
  *
@@ -2108,32 +1312,8 @@ interface ClientConfig {
2108
1312
  tokenType?: TokenType;
2109
1313
  gameId?: string;
2110
1314
  launchId?: string;
2111
- autoStartSession?: boolean;
2112
- onDisconnect?: DisconnectHandler;
2113
- enableConnectionMonitoring?: boolean;
2114
1315
  mode?: PlaycademyMode;
2115
1316
  }
2116
- /**
2117
- * Handler called when connection state changes to offline or degraded.
2118
- * Games can implement this to handle disconnects gracefully.
2119
- */
2120
- type DisconnectHandler = (context: DisconnectContext) => void | Promise<void>;
2121
- /**
2122
- * Context provided to disconnect handlers
2123
- */
2124
- interface DisconnectContext {
2125
- /** Current connection state */
2126
- state: 'offline' | 'degraded';
2127
- /** Reason for the disconnect */
2128
- reason: string;
2129
- /** Timestamp when disconnect was detected */
2130
- timestamp: number;
2131
- /** Utility to display a platform-level alert */
2132
- displayAlert: (message: string, options?: {
2133
- type?: 'info' | 'warning' | 'error';
2134
- duration?: number;
2135
- }) => void;
2136
- }
2137
1317
  interface InitPayload {
2138
1318
  /** Hub API base URL */
2139
1319
  baseUrl: string;
@@ -2143,8 +1323,6 @@ interface InitPayload {
2143
1323
  token: string;
2144
1324
  /** Game ID */
2145
1325
  gameId: string;
2146
- /** Realtime WebSocket URL */
2147
- realtimeUrl?: string;
2148
1326
  /** Timeback context (if user has a Timeback account) */
2149
1327
  timeback?: TimebackInitContext;
2150
1328
  /** Runtime mode for the game client */
@@ -2153,7 +1331,6 @@ interface InitPayload {
2153
1331
  interface GameContextPayload {
2154
1332
  token: string;
2155
1333
  baseUrl: string;
2156
- realtimeUrl: string;
2157
1334
  gameId: string;
2158
1335
  forwardKeys?: string[];
2159
1336
  mode?: PlaycademyMode;
@@ -2165,38 +1342,12 @@ interface ClientEvents {
2165
1342
  authChange: {
2166
1343
  token: string | null;
2167
1344
  };
2168
- inventoryChange: {
2169
- itemId: string;
2170
- delta: number;
2171
- newTotal: number;
2172
- };
2173
- levelUp: {
2174
- oldLevel: number;
2175
- newLevel: number;
2176
- creditsAwarded: number;
2177
- };
2178
- xpGained: {
2179
- amount: number;
2180
- totalXP: number;
2181
- leveledUp: boolean;
2182
- };
2183
- connectionChange: {
2184
- state: 'online' | 'offline' | 'degraded';
2185
- reason: string;
2186
- };
2187
1345
  }
2188
1346
 
2189
1347
  /**
2190
1348
  * Event and message payload types for SDK messaging system
2191
1349
  */
2192
1350
 
2193
- interface DisplayAlertPayload {
2194
- message: string;
2195
- options?: {
2196
- type?: 'info' | 'warning' | 'error';
2197
- duration?: number;
2198
- };
2199
- }
2200
1351
  /**
2201
1352
  * Authentication state change event payload.
2202
1353
  * Used when authentication state changes in the application.
@@ -2253,14 +1404,6 @@ interface KeyEventPayload {
2253
1404
  /** Event type */
2254
1405
  type: 'keydown' | 'keyup';
2255
1406
  }
2256
- /**
2257
- * Connection state payload.
2258
- * Broadcast from platform to games when connection changes.
2259
- */
2260
- interface ConnectionStatePayload {
2261
- state: 'online' | 'offline' | 'degraded';
2262
- reason: string;
2263
- }
2264
1407
  /**
2265
1408
  * Init error payload.
2266
1409
  * Sent from game iframe to parent when SDK initialization fails
@@ -2301,19 +1444,6 @@ interface GameTokenResponse {
2301
1444
  exp: number;
2302
1445
  baseUrl?: string;
2303
1446
  }
2304
- interface InventoryMutationResponse {
2305
- newTotal: number;
2306
- }
2307
-
2308
- /**
2309
- * Realtime namespace types
2310
- */
2311
- /**
2312
- * Response type for the realtime token API
2313
- */
2314
- interface RealtimeTokenResponse {
2315
- token: string;
2316
- }
2317
1447
 
2318
1448
  /**
2319
1449
  * Scores namespace types
@@ -2395,179 +1525,5 @@ interface DevUploadHooks {
2395
1525
  onClose?: () => void;
2396
1526
  }
2397
1527
 
2398
- /**
2399
- * Connection Manager
2400
- *
2401
- * Manages connection monitoring and integrates it with the Playcademy client.
2402
- * Handles event wiring, state management, and disconnect callbacks.
2403
- *
2404
- * In iframe mode, disables local monitoring and listens to platform connection
2405
- * state broadcasts instead (avoids duplicate heartbeats).
2406
- */
2407
-
2408
- /**
2409
- * Configuration for the ConnectionManager.
2410
- */
2411
- interface ConnectionManagerConfig {
2412
- /** Base URL for API requests (used for heartbeat pings) */
2413
- baseUrl: string;
2414
- /** Authentication context (iframe vs standalone) for alert routing */
2415
- authContext?: {
2416
- isInIframe: boolean;
2417
- };
2418
- /** Handler to call when connection issues are detected */
2419
- onDisconnect?: DisconnectHandler;
2420
- /** Callback to emit connection change events to the client */
2421
- onConnectionChange?: (state: ConnectionState, reason: string) => void;
2422
- }
2423
- /**
2424
- * Manages connection monitoring for the Playcademy client.
2425
- *
2426
- * The ConnectionManager serves as an integration layer between the low-level
2427
- * ConnectionMonitor and the PlaycademyClient. It handles:
2428
- * - Event wiring and coordination
2429
- * - Disconnect callbacks with context
2430
- * - Platform-level alert integration
2431
- * - Request success/failure tracking
2432
- *
2433
- * This class is used internally by PlaycademyClient and typically not
2434
- * instantiated directly by game developers.
2435
- *
2436
- * @see {@link ConnectionMonitor} for the underlying monitoring implementation
2437
- * @see {@link PlaycademyClient.onDisconnect} for the public API
2438
- */
2439
- declare class ConnectionManager {
2440
- private monitor?;
2441
- private authContext?;
2442
- private disconnectHandler?;
2443
- private connectionChangeCallback?;
2444
- private currentState;
2445
- private additionalDisconnectHandlers;
2446
- /**
2447
- * Creates a new ConnectionManager instance.
2448
- *
2449
- * @param config - Configuration options for the manager
2450
- *
2451
- * @example
2452
- * ```typescript
2453
- * const manager = new ConnectionManager({
2454
- * baseUrl: 'https://api.playcademy.com',
2455
- * authContext: { isInIframe: false },
2456
- * onDisconnect: (context) => {
2457
- * console.log(`Disconnected: ${context.state}`)
2458
- * },
2459
- * onConnectionChange: (state, reason) => {
2460
- * console.log(`Connection changed: ${state}`)
2461
- * }
2462
- * })
2463
- * ```
2464
- */
2465
- constructor(config: ConnectionManagerConfig);
2466
- /**
2467
- * Gets the current connection state.
2468
- *
2469
- * @returns The current connection state ('online', 'offline', or 'degraded')
2470
- *
2471
- * @example
2472
- * ```typescript
2473
- * const state = manager.getState()
2474
- * if (state === 'offline') {
2475
- * console.log('No connection')
2476
- * }
2477
- * ```
2478
- */
2479
- getState(): ConnectionState;
2480
- /**
2481
- * Manually triggers a connection check immediately.
2482
- *
2483
- * Forces a heartbeat ping to verify the current connection status.
2484
- * Useful when you need to check connectivity before a critical operation.
2485
- *
2486
- * In iframe mode, this returns the last known state from platform.
2487
- *
2488
- * @returns Promise resolving to the current connection state
2489
- *
2490
- * @example
2491
- * ```typescript
2492
- * const state = await manager.checkNow()
2493
- * if (state === 'online') {
2494
- * await performCriticalOperation()
2495
- * }
2496
- * ```
2497
- */
2498
- checkNow(): Promise<ConnectionState>;
2499
- /**
2500
- * Reports a successful API request to the connection monitor.
2501
- *
2502
- * This resets the consecutive failure counter and transitions from
2503
- * 'degraded' to 'online' state if applicable.
2504
- *
2505
- * Typically called automatically by the SDK's request wrapper.
2506
- * No-op in iframe mode (platform handles monitoring).
2507
- */
2508
- reportRequestSuccess(): void;
2509
- /**
2510
- * Reports a failed API request to the connection monitor.
2511
- *
2512
- * Only network errors are tracked (not 4xx/5xx HTTP responses).
2513
- * After consecutive failures exceed the threshold, the state transitions
2514
- * to 'degraded' or 'offline'.
2515
- *
2516
- * Typically called automatically by the SDK's request wrapper.
2517
- * No-op in iframe mode (platform handles monitoring).
2518
- *
2519
- * @param error - The error from the failed request
2520
- */
2521
- reportRequestFailure(error: unknown): void;
2522
- /**
2523
- * Registers a callback to be called when connection issues are detected.
2524
- *
2525
- * The callback only fires for 'offline' and 'degraded' states, not when
2526
- * recovering to 'online'. This provides a clean API for games to handle
2527
- * disconnect scenarios without being notified of every state change.
2528
- *
2529
- * Works in both iframe and standalone modes transparently.
2530
- *
2531
- * @param callback - Function to call when connection degrades
2532
- * @returns Cleanup function to unregister the callback
2533
- *
2534
- * @example
2535
- * ```typescript
2536
- * const cleanup = manager.onDisconnect(({ state, reason, displayAlert }) => {
2537
- * if (state === 'offline') {
2538
- * displayAlert?.('Connection lost. Saving your progress...', { type: 'error' })
2539
- * saveGameState()
2540
- * }
2541
- * })
2542
- *
2543
- * // Later: cleanup() to unregister
2544
- * ```
2545
- */
2546
- onDisconnect(callback: DisconnectHandler): () => void;
2547
- /**
2548
- * Stops connection monitoring and performs cleanup.
2549
- *
2550
- * Removes event listeners and clears heartbeat intervals.
2551
- * Should be called when the client is being destroyed.
2552
- */
2553
- stop(): void;
2554
- /**
2555
- * Sets up listener for platform connection state broadcasts (iframe mode only).
2556
- */
2557
- private _setupPlatformListener;
2558
- /**
2559
- * Handles connection state changes from the monitor or platform.
2560
- *
2561
- * Coordinates between:
2562
- * 1. Emitting events to the client (for client.on('connectionChange'))
2563
- * 2. Calling the disconnect handler if configured
2564
- * 3. Calling any additional handlers registered via onDisconnect()
2565
- *
2566
- * @param state - The new connection state
2567
- * @param reason - Human-readable reason for the state change
2568
- */
2569
- private _handleConnectionChange;
2570
- }
2571
-
2572
- export { ApiError, ConnectionManager, ConnectionMonitor, MessageEvents, PlaycademyClient, PlaycademyError, extractApiErrorInfo, messaging };
2573
- export type { ApiErrorCode, ApiErrorInfo, ConnectionMonitorConfig, ConnectionState, ConnectionStatePayload, DevUploadEvent, DevUploadHooks, DisconnectContext, DisconnectHandler, DisplayAlertPayload, ErrorResponseBody, PlaycademyMode };
1528
+ export { ApiError, MessageEvents, PlaycademyClient, PlaycademyError, extractApiErrorInfo, messaging };
1529
+ export type { ApiErrorCode, ApiErrorInfo, DevUploadEvent, DevUploadHooks, ErrorResponseBody, PlaycademyMode };