@playcademy/sdk 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import * as _playcademy_realtime_server_types from '@playcademy/realtime/server/types';
2
- import * as _playcademy_timeback_types from '@playcademy/timeback/types';
3
- import { CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
4
1
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
5
2
  import { z } from 'zod';
3
+ import * as _playcademy_timeback_types from '@playcademy/timeback/types';
4
+ import { CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
6
5
  import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
7
6
 
8
7
  /**
@@ -2453,13 +2452,66 @@ type MapObject = typeof mapObjects.$inferSelect;
2453
2452
  type User = typeof users.$inferSelect;
2454
2453
  type UserRoleEnumType = (typeof userRoleEnum.enumValues)[number];
2455
2454
  type DeveloperStatusResponse = z.infer<typeof DeveloperStatusResponseSchema>;
2455
+ /**
2456
+ * TimeBack enrollment information for a game.
2457
+ */
2458
+ type UserEnrollment = {
2459
+ gameId?: string;
2460
+ courseId: string;
2461
+ grade: number;
2462
+ subject: string;
2463
+ orgId?: string;
2464
+ };
2465
+ /**
2466
+ * TimeBack user role (matches OneRoster spec).
2467
+ */
2468
+ type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
2469
+ /**
2470
+ * Organization type (matches OneRoster spec).
2471
+ */
2472
+ type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
2473
+ /**
2474
+ * TimeBack organization data for a user.
2475
+ * Represents schools, districts, or other educational organizations.
2476
+ */
2477
+ type UserOrganization = {
2478
+ /** Organization ID (OneRoster sourcedId) */
2479
+ id: string;
2480
+ /** Organization name */
2481
+ name: string | null;
2482
+ /** Organization type (school, district, etc.) */
2483
+ type: TimebackOrgType | string;
2484
+ /** Whether this is the user's primary organization */
2485
+ isPrimary: boolean;
2486
+ };
2487
+ /**
2488
+ * TimeBack student profile (role + organizations).
2489
+ * Subset of UserTimebackData returned by OneRoster API.
2490
+ */
2491
+ type TimebackStudentProfile = {
2492
+ /** User's primary role in TimeBack (student, parent, teacher, etc.) */
2493
+ role: TimebackUserRole;
2494
+ /** User's organizations (schools/districts) */
2495
+ organizations: UserOrganization[];
2496
+ };
2497
+ /**
2498
+ * TimeBack-related data for a user.
2499
+ */
2500
+ type UserTimebackData = TimebackStudentProfile & {
2501
+ /** User's TimeBack ID (sourcedId) */
2502
+ id: string;
2503
+ /** Course enrollments */
2504
+ enrollments: UserEnrollment[];
2505
+ };
2456
2506
  /**
2457
2507
  * User data with authentication provider information.
2458
2508
  * Returned by the /users/me endpoint with additional auth context.
2459
2509
  */
2460
- type AuthenticatedUser = User & {
2510
+ type AuthenticatedUser = Omit<User, 'timebackId'> & {
2461
2511
  /** Whether the user authenticated via Timeback SSO */
2462
2512
  hasTimebackAccount: boolean;
2513
+ /** TimeBack data (id, role, enrollments, organizations) - only present if user has a timeback account */
2514
+ timeback?: UserTimebackData;
2463
2515
  };
2464
2516
 
2465
2517
  /**
@@ -2706,6 +2758,9 @@ type EndActivityResponse = {
2706
2758
  inProgress?: string;
2707
2759
  };
2708
2760
 
2761
+ /** Permitted HTTP verbs */
2762
+ type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2763
+
2709
2764
  /**
2710
2765
  * Connection monitoring types
2711
2766
  *
@@ -2720,6 +2775,180 @@ type EndActivityResponse = {
2720
2775
  */
2721
2776
  type ConnectionState = 'online' | 'offline' | 'degraded';
2722
2777
 
2778
+ /**
2779
+ * Connection Manager
2780
+ *
2781
+ * Manages connection monitoring and integrates it with the Playcademy client.
2782
+ * Handles event wiring, state management, and disconnect callbacks.
2783
+ *
2784
+ * In iframe mode, disables local monitoring and listens to platform connection
2785
+ * state broadcasts instead (avoids duplicate heartbeats).
2786
+ */
2787
+
2788
+ /**
2789
+ * Configuration for the ConnectionManager.
2790
+ */
2791
+ interface ConnectionManagerConfig {
2792
+ /** Base URL for API requests (used for heartbeat pings) */
2793
+ baseUrl: string;
2794
+ /** Authentication context (iframe vs standalone) for alert routing */
2795
+ authContext?: {
2796
+ isInIframe: boolean;
2797
+ };
2798
+ /** Handler to call when connection issues are detected */
2799
+ onDisconnect?: DisconnectHandler;
2800
+ /** Callback to emit connection change events to the client */
2801
+ onConnectionChange?: (state: ConnectionState, reason: string) => void;
2802
+ }
2803
+ /**
2804
+ * Manages connection monitoring for the Playcademy client.
2805
+ *
2806
+ * The ConnectionManager serves as an integration layer between the low-level
2807
+ * ConnectionMonitor and the PlaycademyClient. It handles:
2808
+ * - Event wiring and coordination
2809
+ * - Disconnect callbacks with context
2810
+ * - Platform-level alert integration
2811
+ * - Request success/failure tracking
2812
+ *
2813
+ * This class is used internally by PlaycademyClient and typically not
2814
+ * instantiated directly by game developers.
2815
+ *
2816
+ * @see {@link ConnectionMonitor} for the underlying monitoring implementation
2817
+ * @see {@link PlaycademyClient.onDisconnect} for the public API
2818
+ */
2819
+ declare class ConnectionManager {
2820
+ private monitor?;
2821
+ private authContext?;
2822
+ private disconnectHandler?;
2823
+ private connectionChangeCallback?;
2824
+ private currentState;
2825
+ private additionalDisconnectHandlers;
2826
+ /**
2827
+ * Creates a new ConnectionManager instance.
2828
+ *
2829
+ * @param config - Configuration options for the manager
2830
+ *
2831
+ * @example
2832
+ * ```typescript
2833
+ * const manager = new ConnectionManager({
2834
+ * baseUrl: 'https://api.playcademy.com',
2835
+ * authContext: { isInIframe: false },
2836
+ * onDisconnect: (context) => {
2837
+ * console.log(`Disconnected: ${context.state}`)
2838
+ * },
2839
+ * onConnectionChange: (state, reason) => {
2840
+ * console.log(`Connection changed: ${state}`)
2841
+ * }
2842
+ * })
2843
+ * ```
2844
+ */
2845
+ constructor(config: ConnectionManagerConfig);
2846
+ /**
2847
+ * Gets the current connection state.
2848
+ *
2849
+ * @returns The current connection state ('online', 'offline', or 'degraded')
2850
+ *
2851
+ * @example
2852
+ * ```typescript
2853
+ * const state = manager.getState()
2854
+ * if (state === 'offline') {
2855
+ * console.log('No connection')
2856
+ * }
2857
+ * ```
2858
+ */
2859
+ getState(): ConnectionState;
2860
+ /**
2861
+ * Manually triggers a connection check immediately.
2862
+ *
2863
+ * Forces a heartbeat ping to verify the current connection status.
2864
+ * Useful when you need to check connectivity before a critical operation.
2865
+ *
2866
+ * In iframe mode, this returns the last known state from platform.
2867
+ *
2868
+ * @returns Promise resolving to the current connection state
2869
+ *
2870
+ * @example
2871
+ * ```typescript
2872
+ * const state = await manager.checkNow()
2873
+ * if (state === 'online') {
2874
+ * await performCriticalOperation()
2875
+ * }
2876
+ * ```
2877
+ */
2878
+ checkNow(): Promise<ConnectionState>;
2879
+ /**
2880
+ * Reports a successful API request to the connection monitor.
2881
+ *
2882
+ * This resets the consecutive failure counter and transitions from
2883
+ * 'degraded' to 'online' state if applicable.
2884
+ *
2885
+ * Typically called automatically by the SDK's request wrapper.
2886
+ * No-op in iframe mode (platform handles monitoring).
2887
+ */
2888
+ reportRequestSuccess(): void;
2889
+ /**
2890
+ * Reports a failed API request to the connection monitor.
2891
+ *
2892
+ * Only network errors are tracked (not 4xx/5xx HTTP responses).
2893
+ * After consecutive failures exceed the threshold, the state transitions
2894
+ * to 'degraded' or 'offline'.
2895
+ *
2896
+ * Typically called automatically by the SDK's request wrapper.
2897
+ * No-op in iframe mode (platform handles monitoring).
2898
+ *
2899
+ * @param error - The error from the failed request
2900
+ */
2901
+ reportRequestFailure(error: unknown): void;
2902
+ /**
2903
+ * Registers a callback to be called when connection issues are detected.
2904
+ *
2905
+ * The callback only fires for 'offline' and 'degraded' states, not when
2906
+ * recovering to 'online'. This provides a clean API for games to handle
2907
+ * disconnect scenarios without being notified of every state change.
2908
+ *
2909
+ * Works in both iframe and standalone modes transparently.
2910
+ *
2911
+ * @param callback - Function to call when connection degrades
2912
+ * @returns Cleanup function to unregister the callback
2913
+ *
2914
+ * @example
2915
+ * ```typescript
2916
+ * const cleanup = manager.onDisconnect(({ state, reason, displayAlert }) => {
2917
+ * if (state === 'offline') {
2918
+ * displayAlert?.('Connection lost. Saving your progress...', { type: 'error' })
2919
+ * saveGameState()
2920
+ * }
2921
+ * })
2922
+ *
2923
+ * // Later: cleanup() to unregister
2924
+ * ```
2925
+ */
2926
+ onDisconnect(callback: DisconnectHandler): () => void;
2927
+ /**
2928
+ * Stops connection monitoring and performs cleanup.
2929
+ *
2930
+ * Removes event listeners and clears heartbeat intervals.
2931
+ * Should be called when the client is being destroyed.
2932
+ */
2933
+ stop(): void;
2934
+ /**
2935
+ * Sets up listener for platform connection state broadcasts (iframe mode only).
2936
+ */
2937
+ private _setupPlatformListener;
2938
+ /**
2939
+ * Handles connection state changes from the monitor or platform.
2940
+ *
2941
+ * Coordinates between:
2942
+ * 1. Emitting events to the client (for client.on('connectionChange'))
2943
+ * 2. Calling the disconnect handler if configured
2944
+ * 3. Calling any additional handlers registered via onDisconnect()
2945
+ *
2946
+ * @param state - The new connection state
2947
+ * @param reason - Human-readable reason for the state change
2948
+ */
2949
+ private _handleConnectionChange;
2950
+ }
2951
+
2723
2952
  /**
2724
2953
  * @fileoverview Playcademy Messaging System
2725
2954
  *
@@ -2931,208 +3160,103 @@ declare function init<T extends PlaycademyClient = PlaycademyClient>(this: new (
2931
3160
  */
2932
3161
  declare function login(baseUrl: string, email: string, password: string): Promise<LoginResponse>;
2933
3162
 
2934
- /** Permitted HTTP verbs */
2935
- type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
3163
+ /**
3164
+ * @fileoverview Authentication Strategy Pattern
3165
+ *
3166
+ * Provides different authentication strategies for the Playcademy SDK.
3167
+ * Each strategy knows how to add its authentication headers to requests.
3168
+ */
3169
+
3170
+ /**
3171
+ * Base interface for authentication strategies
3172
+ */
3173
+ interface AuthStrategy {
3174
+ /** Get the token value */
3175
+ getToken(): string | null;
3176
+ /** Get the token type */
3177
+ getType(): TokenType;
3178
+ /** Get authentication headers for a request */
3179
+ getHeaders(): Record<string, string>;
3180
+ }
2936
3181
 
2937
3182
  /**
2938
- * Main Playcademy SDK client for interacting with the platform API.
2939
- * Provides namespaced access to all platform features including games, users, inventory, and more.
3183
+ * Base Playcademy SDK client with shared infrastructure.
3184
+ * Provides authentication, HTTP requests, events, connection monitoring,
3185
+ * and fundamental namespaces used by all clients.
3186
+ *
3187
+ * Extended by PlaycademyClient (game SDK) and PlaycademyInternalClient (platform SDK).
2940
3188
  */
2941
- declare class PlaycademyClient {
3189
+ declare abstract class PlaycademyBaseClient {
2942
3190
  baseUrl: string;
2943
3191
  gameUrl?: string;
2944
- private authStrategy;
2945
- private gameId?;
2946
- private config;
2947
- private listeners;
2948
- private internalClientSessionId?;
2949
- private authContext?;
2950
- private initPayload?;
2951
- private connectionManager?;
3192
+ protected authStrategy: AuthStrategy;
3193
+ protected gameId?: string;
3194
+ protected config: Partial<ClientConfig>;
3195
+ protected listeners: EventListeners;
3196
+ protected internalClientSessionId?: string;
3197
+ protected authContext?: {
3198
+ isInIframe: boolean;
3199
+ };
3200
+ protected initPayload?: InitPayload;
3201
+ protected connectionManager?: ConnectionManager;
2952
3202
  /**
2953
3203
  * Internal session manager for automatic session lifecycle.
2954
- *
2955
- * This manager handles starting and ending game sessions automatically.
2956
- * Game developers don't need to call these methods directly - they're managed
2957
- * by the SDK during initialization and cleanup.
2958
- *
2959
3204
  * @private
2960
3205
  * @internal
2961
3206
  */
2962
- private _sessionManager;
2963
- /**
2964
- * Creates a new PlaycademyClient instance.
2965
- *
2966
- * @param config - Optional configuration object
2967
- * @param config.baseUrl - Base URL (e.g., 'https://hub.playcademy.net' or '/'). SDK automatically appends /api
2968
- * @param config.token - Authentication token
2969
- * @param config.tokenType - Optional token type (auto-detected if not provided)
2970
- * @param config.gameId - Game ID for automatic session management
2971
- * @param config.autoStartSession - Automatically start a game session?
2972
- */
3207
+ protected _sessionManager: {
3208
+ startSession: (gameId: string) => Promise<{
3209
+ sessionId: string;
3210
+ }>;
3211
+ endSession: (sessionId: string, gameId: string) => Promise<void>;
3212
+ };
2973
3213
  constructor(config?: Partial<ClientConfig>);
2974
3214
  /**
2975
3215
  * Gets the effective base URL for API requests.
2976
- * Converts relative URLs to absolute URLs in browser environments.
2977
- * Note: baseUrl already includes /api suffix from constructor.
2978
- *
2979
- * @returns The complete base URL for API requests (with /api suffix)
2980
3216
  */
2981
3217
  getBaseUrl(): string;
2982
3218
  /**
2983
3219
  * Gets the effective game backend URL for integration requests.
2984
- * Throws if gameUrl is not configured.
2985
- *
2986
- * @returns The complete game backend URL for API requests (with /api suffix)
2987
- * @throws PlaycademyError if gameUrl is not set
2988
3220
  */
2989
- private getGameBackendUrl;
3221
+ protected getGameBackendUrl(): string;
2990
3222
  /**
2991
3223
  * Simple ping method for testing connectivity.
2992
- *
2993
- * @returns 'pong' string response
2994
3224
  */
2995
3225
  ping(): string;
2996
3226
  /**
2997
3227
  * Sets the authentication token for API requests.
2998
- * Emits an 'authChange' event when the token changes.
2999
- *
3000
- * @param token - The authentication token, or null to clear
3001
- * @param tokenType - Optional token type (auto-detected if not provided)
3002
3228
  */
3003
3229
  setToken(token: string | null, tokenType?: TokenType): void;
3004
3230
  /**
3005
3231
  * Gets the current token type.
3006
- *
3007
- * @returns The token type
3008
3232
  */
3009
3233
  getTokenType(): TokenType;
3010
3234
  /**
3011
3235
  * Gets the current authentication token.
3012
- *
3013
- * @returns The current token or null if not authenticated
3014
- *
3015
- * @example
3016
- * ```typescript
3017
- * // Send token to your backend for verification
3018
- * const token = client.getToken()
3019
- * const response = await fetch('/api/auth/playcademy', {
3020
- * method: 'POST',
3021
- * body: JSON.stringify({ gameToken: token })
3022
- * })
3023
- * ```
3024
3236
  */
3025
3237
  getToken(): string | null;
3026
3238
  /**
3027
- * Checks if the client has a valid API token for making Playcademy API requests.
3028
- *
3029
- * For games (iframe context): Checks if we have a valid token from the parent.
3030
- * For Cademy (standalone): Checks if we have a token from better-auth.
3031
- *
3032
- * Note: This checks for API authentication, not whether a user has linked
3033
- * their identity via OAuth.
3034
- *
3035
- * @returns true if API token exists, false otherwise
3036
- *
3037
- * @example
3038
- * ```typescript
3039
- * if (client.isAuthenticated()) {
3040
- * // Can make API calls
3041
- * const games = await client.games.list()
3042
- * } else {
3043
- * console.error('No API token available')
3044
- * }
3045
- * ```
3239
+ * Checks if the client has a valid API token.
3046
3240
  */
3047
3241
  isAuthenticated(): boolean;
3048
3242
  /**
3049
3243
  * Registers a callback to be called when authentication state changes.
3050
- *
3051
- * @param callback - Function to call when auth state changes
3052
3244
  */
3053
3245
  onAuthChange(callback: (token: string | null) => void): void;
3054
3246
  /**
3055
3247
  * Registers a callback to be called when connection issues are detected.
3056
- *
3057
- * This is a convenience method that filters connection change events to only
3058
- * fire when the connection degrades (offline or degraded states). Use this
3059
- * when you want to handle disconnects without being notified of recoveries.
3060
- *
3061
- * For all connection state changes, use `client.on('connectionChange', ...)` instead.
3062
- *
3063
- * @param callback - Function to call when connection state changes to offline or degraded
3064
- * @returns Cleanup function to unregister the callback
3065
- *
3066
- * @example
3067
- * ```typescript
3068
- * const cleanup = client.onDisconnect(({ state, reason, displayAlert }) => {
3069
- * console.log(`Connection ${state}: ${reason}`)
3070
- *
3071
- * if (state === 'offline') {
3072
- * // Save state and return to game lobby
3073
- * displayAlert?.('Connection lost. Your progress has been saved.', { type: 'error' })
3074
- * saveGameState()
3075
- * returnToLobby()
3076
- * } else if (state === 'degraded') {
3077
- * displayAlert?.('Slow connection detected.', { type: 'warning' })
3078
- * }
3079
- * })
3080
- *
3081
- * // Later: cleanup() to unregister
3082
- * ```
3083
- *
3084
- * @see Connection monitoring documentation in SDK Browser docs for detailed usage examples
3085
- * @see {@link ConnectionManager.onDisconnect} for the underlying implementation
3086
3248
  */
3087
3249
  onDisconnect(callback: (context: DisconnectContext) => void | Promise<void>): () => void;
3088
3250
  /**
3089
3251
  * Gets the current connection state.
3090
- *
3091
- * Returns the last known connection state without triggering a new check.
3092
- * Use `checkConnection()` to force an immediate verification.
3093
- *
3094
- * @returns Current connection state ('online', 'offline', 'degraded') or 'unknown' if monitoring is disabled
3095
- *
3096
- * @example
3097
- * ```typescript
3098
- * const state = client.getConnectionState()
3099
- * if (state === 'offline') {
3100
- * console.log('No connection available')
3101
- * }
3102
- * ```
3103
- *
3104
- * @see {@link checkConnection} to trigger an immediate connection check
3105
- * @see {@link ConnectionManager.getState} for the underlying implementation
3106
3252
  */
3107
3253
  getConnectionState(): ConnectionState | 'unknown';
3108
3254
  /**
3109
3255
  * Manually triggers a connection check immediately.
3110
- *
3111
- * Forces a heartbeat ping to verify connectivity right now, bypassing the normal
3112
- * interval. Useful when you need to verify connection status before a critical
3113
- * operation (e.g., saving important game state).
3114
- *
3115
- * @returns Promise resolving to the current connection state after verification
3116
- *
3117
- * @example
3118
- * ```typescript
3119
- * // Check before critical operation
3120
- * const state = await client.checkConnection()
3121
- * if (state !== 'online') {
3122
- * alert('Please check your internet connection before saving')
3123
- * return
3124
- * }
3125
- *
3126
- * await client.games.saveState(importantData)
3127
- * ```
3128
- *
3129
- * @see {@link getConnectionState} to get the last known state without checking
3130
- * @see {@link ConnectionManager.checkNow} for the underlying implementation
3131
3256
  */
3132
3257
  checkConnection(): Promise<ConnectionState | 'unknown'>;
3133
3258
  /**
3134
3259
  * Sets the authentication context for the client.
3135
- * This is called during initialization to store environment info.
3136
3260
  * @internal
3137
3261
  */
3138
3262
  _setAuthContext(context: {
@@ -3140,28 +3264,14 @@ declare class PlaycademyClient {
3140
3264
  }): void;
3141
3265
  /**
3142
3266
  * Registers an event listener for client events.
3143
- *
3144
- * @param event - The event type to listen for
3145
- * @param callback - Function to call when the event is emitted
3146
3267
  */
3147
3268
  on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
3148
3269
  /**
3149
3270
  * Emits an event to all registered listeners.
3150
- *
3151
- * @param event - The event type to emit
3152
- * @param payload - The event payload
3153
3271
  */
3154
- private emit;
3272
+ protected emit<E extends keyof ClientEvents>(event: E, payload: ClientEvents[E]): void;
3155
3273
  /**
3156
3274
  * Makes an authenticated HTTP request to the platform API.
3157
- *
3158
- * @param path - API endpoint path
3159
- * @param method - HTTP method
3160
- * @param options - Optional request configuration
3161
- * @param options.body - Request body
3162
- * @param options.headers - Additional headers
3163
- * @param options.raw - If true, returns raw Response instead of parsing
3164
- * @returns Promise resolving to the response data or raw Response
3165
3275
  */
3166
3276
  protected request<T>(path: string, method: Method, options?: {
3167
3277
  body?: unknown;
@@ -3170,49 +3280,64 @@ declare class PlaycademyClient {
3170
3280
  }): Promise<T>;
3171
3281
  /**
3172
3282
  * Makes an authenticated HTTP request to the game's backend Worker.
3173
- * Uses gameUrl if set, otherwise falls back to platform API.
3174
- *
3175
- * @param path - API endpoint path
3176
- * @param method - HTTP method
3177
- * @param body - Request body (optional)
3178
- * @param headers - Additional headers (optional)
3179
- * @param raw - If true, returns raw Response instead of parsing (optional)
3180
- * @returns Promise resolving to the response data or raw Response
3181
3283
  */
3182
3284
  protected requestGameBackend<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>, raw?: boolean): Promise<T>;
3183
3285
  /**
3184
3286
  * Ensures a gameId is available, throwing an error if not.
3185
- *
3186
- * @returns The gameId
3187
- * @throws PlaycademyError if no gameId is configured
3188
3287
  */
3189
- private _ensureGameId;
3288
+ protected _ensureGameId(): string;
3190
3289
  /**
3191
3290
  * Detects and sets the authentication context (iframe vs standalone).
3192
- * Safe to call in any environment - isInIframe handles browser detection.
3193
3291
  */
3194
3292
  private _detectAuthContext;
3195
3293
  /**
3196
3294
  * Initializes connection monitoring if enabled.
3197
- * Safe to call in any environment - only runs in browser.
3198
3295
  */
3199
3296
  private _initializeConnectionMonitor;
3200
3297
  /**
3201
3298
  * Initializes an internal game session for automatic session management.
3202
- * Only starts a session if:
3203
- * 1. A gameId is configured
3204
- * 2. No session already exists
3205
- * 3. autoStartSession is enabled (defaults to false)
3206
3299
  */
3207
3300
  private _initializeInternalSession;
3208
- /** Identity provider connection methods (connect external accounts) */
3301
+ /**
3302
+ * Current user data and inventory management.
3303
+ * - `me()` - Get authenticated user profile
3304
+ * - `inventory.get()` - List user's items
3305
+ * - `inventory.add(slug, qty)` - Award items to user
3306
+ */
3307
+ users: {
3308
+ me: () => Promise<AuthenticatedUser>;
3309
+ inventory: {
3310
+ get: () => Promise<InventoryItemWithItem[]>;
3311
+ add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
3312
+ remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
3313
+ quantity: (identifier: string) => Promise<number>;
3314
+ has: (identifier: string, minQuantity?: number) => Promise<boolean>;
3315
+ };
3316
+ };
3317
+ }
3318
+
3319
+ /**
3320
+ * Playcademy SDK client for game developers.
3321
+ * Provides namespaced access to platform features for games running inside Cademy.
3322
+ */
3323
+ declare class PlaycademyClient extends PlaycademyBaseClient {
3324
+ /**
3325
+ * Connect external identity providers to the user's Playcademy account.
3326
+ * - `connect(provider)` - Link Discord, Google, etc. via OAuth popup
3327
+ */
3209
3328
  identity: {
3210
3329
  connect: (options: AuthOptions) => Promise<AuthResult>;
3211
3330
  _getContext: () => {
3212
3331
  isInIframe: boolean;
3213
3332
  };
3214
3333
  };
3215
- /** Runtime methods (getGameToken, exit) */
3334
+ /**
3335
+ * Game runtime lifecycle and asset loading.
3336
+ * - `exit()` - Return to Cademy hub
3337
+ * - `getGameToken()` - Get short-lived auth token
3338
+ * - `assets.url()`, `assets.json()`, `assets.fetch()` - Load game assets
3339
+ * - `on('pause')`, `on('resume')` - Handle visibility changes
3340
+ */
3216
3341
  runtime: {
3217
3342
  getGameToken: (gameId: string, options?: {
3218
3343
  apply?: boolean;
@@ -3247,42 +3372,58 @@ declare class PlaycademyClient {
3247
3372
  arrayBuffer: (path: string) => Promise<ArrayBuffer>;
3248
3373
  };
3249
3374
  };
3250
- /** User methods (me, inventory management) */
3251
- users: {
3252
- me: () => Promise<AuthenticatedUser>;
3253
- inventory: {
3254
- get: () => Promise<InventoryItemWithItem[]>;
3255
- add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
3256
- remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
3257
- quantity: (identifier: string) => Promise<number>;
3258
- has: (identifier: string, minQuantity?: number) => Promise<boolean>;
3259
- };
3260
- };
3261
- /** TimeBack XP methods (today, total, history) */
3375
+ /**
3376
+ * TimeBack integration for activity tracking and user context.
3377
+ *
3378
+ * User context (cached from init, refreshable):
3379
+ * - `user.role` - User's role (student, parent, teacher, etc.)
3380
+ * - `user.enrollments` - Courses the player is enrolled in for this game
3381
+ * - `user.organizations` - Schools/districts the player belongs to
3382
+ * - `user.fetch()` - Refresh user context from server
3383
+ *
3384
+ * Activity tracking:
3385
+ * - `startActivity(metadata)` - Begin tracking an activity
3386
+ * - `pauseActivity()` / `resumeActivity()` - Pause/resume timer
3387
+ * - `endActivity(scoreData)` - Submit activity results to TimeBack
3388
+ */
3262
3389
  timeback: {
3390
+ readonly user: TimebackUser;
3263
3391
  startActivity: (metadata: _playcademy_timeback_types.ActivityData) => void;
3264
3392
  pauseActivity: () => void;
3265
3393
  resumeActivity: () => void;
3266
3394
  endActivity: (data: _playcademy_timeback_types.EndActivityScoreData) => Promise<EndActivityResponse>;
3267
3395
  };
3268
- /** Credits methods (credits management) */
3396
+ /**
3397
+ * Playcademy Credits (platform currency) management.
3398
+ * - `get()` - Get user's credit balance
3399
+ * - `add(amount)` - Award credits to user
3400
+ */
3269
3401
  credits: {
3270
3402
  balance: () => Promise<number>;
3271
3403
  add: (amount: number) => Promise<number>;
3272
3404
  spend: (amount: number) => Promise<number>;
3273
3405
  };
3274
- /** Platform-wide scores methods (submit, getUserScores) */
3406
+ /**
3407
+ * Game score submission and leaderboards.
3408
+ * - `submit(gameId, score, metadata?)` - Record a game score
3409
+ */
3275
3410
  scores: {
3276
3411
  submit: (gameId: string, score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
3277
3412
  };
3278
- /** Realtime methods (token) */
3413
+ /**
3414
+ * Realtime multiplayer authentication.
3415
+ * - `getToken()` - Get token for WebSocket/realtime connections
3416
+ */
3279
3417
  realtime: {
3280
3418
  token: {
3281
3419
  get: () => Promise<RealtimeTokenResponse>;
3282
3420
  };
3283
- open(channel?: string, url?: string): Promise<_playcademy_realtime_server_types.RealtimeChannel>;
3284
3421
  };
3285
- /** Backend methods for calling custom game API routes */
3422
+ /**
3423
+ * Make requests to your game's custom backend API routes.
3424
+ * - `get(path)`, `post(path, body)`, `put()`, `delete()` - HTTP methods
3425
+ * - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
3426
+ */
3286
3427
  backend: {
3287
3428
  get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
3288
3429
  post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
@@ -3303,9 +3444,70 @@ declare class PlaycademyClient {
3303
3444
  };
3304
3445
  }
3305
3446
 
3447
+ /**
3448
+ * Type definitions for the game timeback namespace.
3449
+ *
3450
+ * Re-exports core types from @playcademy/data for SDK consumers,
3451
+ * plus SDK-specific types like TimebackInitContext.
3452
+ */
3453
+
3454
+ /**
3455
+ * A TimeBack enrollment for the current game session.
3456
+ * Alias for UserEnrollment without the optional gameId.
3457
+ */
3458
+ type TimebackEnrollment = Omit<UserEnrollment, 'gameId'>;
3459
+ /**
3460
+ * A TimeBack organization (school/district) for the current user.
3461
+ * Alias for UserOrganization.
3462
+ */
3463
+ type TimebackOrganization = UserOrganization;
3464
+ /**
3465
+ * TimeBack context passed during game initialization.
3466
+ * This is sent from the platform (cademy) to the game iframe via postMessage.
3467
+ */
3468
+ interface TimebackInitContext {
3469
+ /** User's TimeBack ID */
3470
+ id: string;
3471
+ /** User's role in TimeBack (student, parent, teacher, etc.) */
3472
+ role: TimebackUserRole;
3473
+ /** User's enrollments for this game (one per grade/subject combo) */
3474
+ enrollments: TimebackEnrollment[];
3475
+ /** User's organizations (schools/districts) */
3476
+ organizations: TimebackOrganization[];
3477
+ }
3478
+ /**
3479
+ * TimeBack user context with current data (may be stale from init).
3480
+ * Use `fetch()` to get fresh data from the server.
3481
+ */
3482
+ interface TimebackUserContext {
3483
+ /** User's TimeBack ID */
3484
+ id: string | undefined;
3485
+ /** User's role in TimeBack (student, parent, teacher, etc.) */
3486
+ role: TimebackUserRole | undefined;
3487
+ /** User's enrollments for this game */
3488
+ enrollments: TimebackEnrollment[];
3489
+ /** User's organizations (schools/districts) */
3490
+ organizations: TimebackOrganization[];
3491
+ }
3492
+ /**
3493
+ * TimeBack user object with both cached getters and fetch method.
3494
+ */
3495
+ interface TimebackUser extends TimebackUserContext {
3496
+ /**
3497
+ * Fetch TimeBack data from the server (cached for 5 min).
3498
+ * Updates the cached values so subsequent property access returns fresh data.
3499
+ * @param options - Cache options (pass { force: true } to bypass cache)
3500
+ * @returns Promise resolving to fresh user context
3501
+ */
3502
+ fetch(options?: {
3503
+ force?: boolean;
3504
+ }): Promise<TimebackUserContext>;
3505
+ }
3506
+
3306
3507
  /**
3307
3508
  * Core client configuration and lifecycle types
3308
3509
  */
3510
+
3309
3511
  type TokenType = 'session' | 'apiKey' | 'gameJwt';
3310
3512
  interface ClientConfig {
3311
3513
  baseUrl: string;
@@ -3349,6 +3551,8 @@ interface InitPayload {
3349
3551
  gameId: string;
3350
3552
  /** Realtime WebSocket URL */
3351
3553
  realtimeUrl?: string;
3554
+ /** Timeback context (if user has a Timeback account) */
3555
+ timeback?: TimebackInitContext;
3352
3556
  }
3353
3557
  /**
3354
3558
  * Simplified user data passed to games via InitPayload
@@ -3695,29 +3899,37 @@ type DevUploadHooks = {
3695
3899
  /**
3696
3900
  * Platform TimeBack Types
3697
3901
  *
3698
- * Types for TimeBack enrollment and student data on the platform side.
3699
- * These are used by the cademy hub to determine which games users have access to.
3902
+ * Types for TimeBack data on the platform side.
3903
+ * Unlike game types, these include gameId for cross-game enrollment views.
3700
3904
  */
3701
3905
 
3702
3906
  /**
3703
- * Enrollment data for a student in a specific game/course.
3704
- * Represents the mapping between a TimeBack course and a Playcademy game.
3907
+ * Platform TimeBack user context.
3908
+ * Unlike game context, enrollments include gameId for cross-game views.
3705
3909
  */
3706
- interface StudentEnrollment {
3707
- /** The Playcademy game ID the student is enrolled in */
3708
- gameId: string;
3709
- /** The grade level for this enrollment */
3710
- grade: number;
3711
- /** The subject area (e.g., 'Math', 'Science') */
3712
- subject: string;
3713
- /** The TimeBack course ID */
3714
- courseId: string;
3910
+ interface PlatformTimebackUserContext {
3911
+ /** User's TimeBack ID */
3912
+ id: string | undefined;
3913
+ /** User's role in TimeBack (student, parent, teacher, etc.) */
3914
+ role: TimebackUserRole | undefined;
3915
+ /** User's enrollments across ALL games (includes gameId) */
3916
+ enrollments: UserEnrollment[];
3917
+ /** User's organizations (schools/districts) */
3918
+ organizations: UserOrganization[];
3715
3919
  }
3716
3920
  /**
3717
- * Response from the enrollments endpoint
3921
+ * Platform TimeBack user object with both cached getters and fetch method.
3718
3922
  */
3719
- interface EnrollmentsResponse {
3720
- enrollments: StudentEnrollment[];
3923
+ interface PlatformTimebackUser extends PlatformTimebackUserContext {
3924
+ /**
3925
+ * Fetch TimeBack data from the server (cached for 5 min).
3926
+ * Updates the cached values so subsequent property access returns fresh data.
3927
+ * @param options - Cache options (pass { force: true } to bypass cache)
3928
+ * @returns Promise resolving to user context with full enrollment data
3929
+ */
3930
+ fetch(options?: {
3931
+ force?: boolean;
3932
+ }): Promise<PlatformTimebackUserContext>;
3721
3933
  }
3722
3934
  /**
3723
3935
  * Combined response type for xp.summary() method
@@ -3906,4 +4118,4 @@ interface PlaycademyServerClientState {
3906
4118
  }
3907
4119
 
3908
4120
  export { PlaycademyClient };
3909
- export type { Achievement, AchievementCurrent, AchievementProgressResponse, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, AuthenticatedUser, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, CharacterComponent, CharacterComponentWithSpriteUrl, CharacterComponentsOptions, ClientConfig, ClientEvents, ConnectionStatePayload, CreateCharacterData, Currency, DevUploadEvent, DevUploadHooks, DeveloperStatusResponse, DisconnectContext, DisconnectHandler, DisplayAlertPayload, EnrollmentsResponse, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameLeaderboardEntry, GameSession, GameStateData, GameTokenResponse, GameUser, HostedGame, InitPayload, InventoryItem, InventoryItemWithItem, InventoryMutationResponse, Item, KeyEventPayload, LeaderboardEntry, LevelConfig, LoginResponse, ManifestV1, Map, MapElement, MapElementWithGame, MapObject, MapObjectWithItem, PlaycademyServerClientConfig, PlaycademyServerClientState, PlayerCharacter, RealtimeTokenResponse, ScoreSubmission, ShopCurrency, ShopDisplayItem, ShopViewResponse, SpriteTemplate, SpriteTemplateData, StartSessionResponse, StudentEnrollment, TelemetryPayload, TodayXpResponse, TokenRefreshPayload, TokenType, TotalXpResponse, UpdateCharacterData, User, UserInfo, UserLevel, UserLevelWithConfig, UserRank, UserRankResponse, UserRoleEnumType, UserScore, XpHistoryResponse, XpSummaryResponse };
4121
+ export type { Achievement, AchievementCurrent, AchievementProgressResponse, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, AuthenticatedUser, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, CharacterComponent, CharacterComponentWithSpriteUrl, CharacterComponentsOptions, ClientConfig, ClientEvents, ConnectionStatePayload, CreateCharacterData, Currency, DevUploadEvent, DevUploadHooks, DeveloperStatusResponse, DisconnectContext, DisconnectHandler, DisplayAlertPayload, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameLeaderboardEntry, GameSession, GameStateData, GameTokenResponse, GameUser, HostedGame, InitPayload, InventoryItem, InventoryItemWithItem, InventoryMutationResponse, Item, KeyEventPayload, LeaderboardEntry, LevelConfig, LoginResponse, ManifestV1, Map, MapElement, MapElementWithGame, MapObject, MapObjectWithItem, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyServerClientConfig, PlaycademyServerClientState, PlayerCharacter, RealtimeTokenResponse, ScoreSubmission, ShopCurrency, ShopDisplayItem, ShopViewResponse, SpriteTemplate, SpriteTemplateData, StartSessionResponse, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserRole, TodayXpResponse, TokenRefreshPayload, TokenType, TotalXpResponse, UpdateCharacterData, User, UserEnrollment, UserInfo, UserLevel, UserLevelWithConfig, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData, XpHistoryResponse, XpSummaryResponse };