@playcademy/sdk 0.9.0 → 0.9.1-beta.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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import { UserRole, AUTH_PROVIDER_IDS } from '@playcademy/constants';
1
2
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
- import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
3
3
 
4
4
  /**
5
5
  * Base error class for Cademy SDK specific errors.
@@ -384,6 +384,125 @@ interface RetryPolicy {
384
384
  retryableMethods?: readonly Method[];
385
385
  }
386
386
 
387
+ /**
388
+ * User Types
389
+ *
390
+ * Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
391
+ *
392
+ * @module types/user
393
+ */
394
+
395
+ type UserRoleEnumType = UserRole;
396
+ type DeveloperStatusEnumType = 'none' | 'pending' | 'approved';
397
+ type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
398
+ type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
399
+ interface UserEnrollment {
400
+ gameId?: string;
401
+ courseId: string;
402
+ enrollmentIds?: {
403
+ active: string;
404
+ inactive?: string[];
405
+ };
406
+ grade: number;
407
+ subject: string;
408
+ orgId?: string;
409
+ }
410
+ interface UserOrganization {
411
+ id: string;
412
+ name: string | null;
413
+ type: TimebackOrgType | string;
414
+ isPrimary: boolean;
415
+ }
416
+ interface TimebackStudentProfile {
417
+ role: TimebackUserRole;
418
+ organizations: UserOrganization[];
419
+ }
420
+ interface UserTimebackData extends TimebackStudentProfile {
421
+ id: string;
422
+ enrollments: UserEnrollment[];
423
+ }
424
+ /**
425
+ * OpenID Connect UserInfo claims (NOT a database row).
426
+ */
427
+ interface UserInfo {
428
+ sub: string;
429
+ email: string;
430
+ name: string | null;
431
+ email_verified?: boolean;
432
+ given_name?: string;
433
+ family_name?: string;
434
+ issuer?: string;
435
+ lti_roles?: unknown;
436
+ lti_context?: unknown;
437
+ lti_resource_link?: unknown;
438
+ timeback_id?: string;
439
+ }
440
+ interface DemoProfile {
441
+ displayName: string;
442
+ isDefault: boolean;
443
+ }
444
+ /**
445
+ * Update shape for `client.demo.profile.update(...)`.
446
+ *
447
+ * Kept as a named type so callers typed against it pick up new fields
448
+ * automatically, but `displayName` is the only updatable field today and
449
+ * the server's `DemoProfileSchema` requires it — a no-field payload would
450
+ * 400 at runtime, so we model that at the type level too. When additional
451
+ * fields land, make them required/optional individually based on server
452
+ * validation, rather than blanket-optional.
453
+ */
454
+ interface DemoProfileUpdate {
455
+ displayName: string;
456
+ }
457
+ /**
458
+ * Authenticated user for API responses.
459
+ * Differs from UserRow: omits timebackId, adds hasTimebackAccount and timeback.
460
+ */
461
+ interface AuthenticatedUser {
462
+ id: string;
463
+ email: string;
464
+ emailVerified: boolean;
465
+ name: string | null;
466
+ image: string | null;
467
+ username: string | null;
468
+ role: UserRoleEnumType;
469
+ developerStatus: DeveloperStatusEnumType;
470
+ characterCreated: boolean;
471
+ createdAt: Date;
472
+ updatedAt: Date;
473
+ hasTimebackAccount: boolean;
474
+ timeback?: UserTimebackData;
475
+ }
476
+
477
+ /**
478
+ * Leaderboard Types
479
+ *
480
+ * @module types/leaderboard
481
+ */
482
+ type LeaderboardTimeframe = 'all_time' | 'monthly' | 'weekly' | 'daily';
483
+ interface LeaderboardOptions {
484
+ timeframe?: LeaderboardTimeframe;
485
+ limit?: number;
486
+ offset?: number;
487
+ gameId?: string;
488
+ }
489
+ /**
490
+ * Leaderboard entry with required game context.
491
+ * Used when fetching leaderboards for a specific game.
492
+ */
493
+ interface GameLeaderboardEntry {
494
+ rank: number;
495
+ userId: string;
496
+ username: string;
497
+ userImage?: string | null;
498
+ score: number;
499
+ achievedAt: Date;
500
+ metadata?: Record<string, unknown>;
501
+ gameId: string;
502
+ gameTitle: string;
503
+ gameSlug: string;
504
+ }
505
+
387
506
  /**
388
507
  * TimeBack Enums & Literal Types
389
508
  *
@@ -499,117 +618,18 @@ interface AdvanceCourseResponse {
499
618
  }
500
619
 
501
620
  /**
502
- * User Types
503
- *
504
- * Enums, DTOs and API response types. Database row types are in @playcademy/data/types.
505
- *
506
- * @module types/user
507
- */
508
- type UserRoleEnumType = 'admin' | 'player' | 'developer' | 'teacher';
509
- type DeveloperStatusEnumType = 'none' | 'pending' | 'approved';
510
- type TimebackUserRole = 'administrator' | 'aide' | 'guardian' | 'parent' | 'proctor' | 'relative' | 'student' | 'teacher';
511
- type TimebackOrgType = 'department' | 'school' | 'district' | 'local' | 'state' | 'national';
512
- interface UserEnrollment {
513
- gameId?: string;
514
- courseId: string;
515
- grade: number;
516
- subject: string;
517
- orgId?: string;
518
- }
519
- interface UserOrganization {
520
- id: string;
521
- name: string | null;
522
- type: TimebackOrgType | string;
523
- isPrimary: boolean;
524
- }
525
- interface TimebackStudentProfile {
526
- role: TimebackUserRole;
527
- organizations: UserOrganization[];
528
- }
529
- interface UserTimebackData extends TimebackStudentProfile {
530
- id: string;
531
- enrollments: UserEnrollment[];
532
- }
533
- /**
534
- * OpenID Connect UserInfo claims (NOT a database row).
535
- */
536
- interface UserInfo {
537
- sub: string;
538
- email: string;
539
- name: string | null;
540
- email_verified?: boolean;
541
- given_name?: string;
542
- family_name?: string;
543
- issuer?: string;
544
- lti_roles?: unknown;
545
- lti_context?: unknown;
546
- lti_resource_link?: unknown;
547
- timeback_id?: string;
548
- }
549
- interface DemoProfile {
550
- displayName: string;
551
- isDefault: boolean;
552
- }
553
- /**
554
- * Update shape for `client.demo.profile.update(...)`.
555
- *
556
- * Kept as a named type so callers typed against it pick up new fields
557
- * automatically, but `displayName` is the only updatable field today and
558
- * the server's `DemoProfileSchema` requires it — a no-field payload would
559
- * 400 at runtime, so we model that at the type level too. When additional
560
- * fields land, make them required/optional individually based on server
561
- * validation, rather than blanket-optional.
562
- */
563
- interface DemoProfileUpdate {
564
- displayName: string;
565
- }
566
- /**
567
- * Authenticated user for API responses.
568
- * Differs from UserRow: omits timebackId, adds hasTimebackAccount and timeback.
569
- */
570
- interface AuthenticatedUser {
571
- id: string;
572
- email: string;
573
- emailVerified: boolean;
574
- name: string | null;
575
- image: string | null;
576
- username: string | null;
577
- role: UserRoleEnumType;
578
- developerStatus: DeveloperStatusEnumType;
579
- characterCreated: boolean;
580
- createdAt: Date;
581
- updatedAt: Date;
582
- hasTimebackAccount: boolean;
583
- timeback?: UserTimebackData;
584
- }
585
-
586
- /**
587
- * Leaderboard Types
588
- *
589
- * @module types/leaderboard
621
+ * Cache configuration types for runtime customization
590
622
  */
591
- type LeaderboardTimeframe = 'all_time' | 'monthly' | 'weekly' | 'daily';
592
- interface LeaderboardOptions {
593
- timeframe?: LeaderboardTimeframe;
594
- limit?: number;
595
- offset?: number;
596
- gameId?: string;
597
- }
598
623
  /**
599
- * Leaderboard entry with required game context.
600
- * Used when fetching leaderboards for a specific game.
624
+ * Runtime configuration for TTL cache behavior
601
625
  */
602
- interface GameLeaderboardEntry {
603
- rank: number;
604
- userId: string;
605
- username: string;
606
- userImage?: string | null;
607
- score: number;
608
- achievedAt: Date;
609
- metadata?: Record<string, unknown>;
610
- gameId: string;
611
- gameTitle: string;
612
- gameSlug: string;
626
+ interface TTLCacheConfig {
627
+ /** Time-to-live in milliseconds. Set to 0 to disable caching for this call. */
628
+ ttl?: number;
629
+ /** Force refresh, bypassing cache */
630
+ force?: boolean;
631
+ /** Skip cache and fetch fresh data (alias for force) */
632
+ skipCache?: boolean;
613
633
  }
614
634
 
615
635
  declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
@@ -889,40 +909,211 @@ type InventoryItemWithItem = InventoryItemRow & {
889
909
  };
890
910
 
891
911
  /**
892
- * Auto-initializes a PlaycademyClient with context from the environment.
893
- * Works in both iframe mode (production/development) and standalone mode (local dev).
894
- *
895
- * This is the recommended way to initialize the SDK as it automatically:
896
- * - Detects the runtime environment (iframe vs standalone)
897
- * - Configures the client with the appropriate context
898
- * - Sets up event listeners for token refresh
899
- * - Exposes the client for debugging in development mode
900
- *
901
- * @param options - Optional configuration overrides
902
- * @param options.baseUrl - Override the base URL for API requests
903
- * @returns Promise resolving to a fully initialized PlaycademyClient
904
- * @throws Error if not running in a browser context
905
- *
906
- * @example
907
- * ```typescript
908
- * // Default initialization
909
- * const client = await PlaycademyClient.init()
912
+ * @fileoverview Authentication Strategy Pattern
910
913
  *
911
- * // With custom base URL
912
- * const client = await PlaycademyClient.init({ baseUrl: 'https://custom.api.com' })
913
- * ```
914
+ * Provides different authentication strategies for the Playcademy SDK.
915
+ * Each strategy knows how to add its authentication headers to requests.
914
916
  */
915
- declare function init<T extends PlaycademyBaseClient = PlaycademyBaseClient>(this: new (config?: Partial<ClientConfig>) => T, options?: {
916
- baseUrl?: string;
917
- allowedParentOrigins?: string[];
918
- onDisconnect?: DisconnectHandler;
919
- enableConnectionMonitoring?: boolean;
920
- }): Promise<T>;
921
917
 
922
918
  /**
923
- * Authenticates a user with email and password.
924
- *
925
- * This is a standalone authentication method that doesn't require an initialized client.
919
+ * Base interface for authentication strategies
920
+ */
921
+ interface AuthStrategy {
922
+ /** Get the token value */
923
+ getToken(): string | null;
924
+ /** Get the token type */
925
+ getType(): TokenType;
926
+ /** Get authentication headers for a request */
927
+ getHeaders(): Record<string, string>;
928
+ }
929
+
930
+ /**
931
+ * Base Playcademy SDK client with shared infrastructure.
932
+ * Provides authentication, HTTP requests, events, connection monitoring,
933
+ * and fundamental namespaces used by all clients.
934
+ *
935
+ * Extended by PlaycademyClient (game SDK) and PlaycademyInternalClient (platform SDK).
936
+ */
937
+ declare abstract class PlaycademyBaseClient {
938
+ baseUrl: string;
939
+ gameUrl?: string;
940
+ mode: PlaycademyMode;
941
+ protected authStrategy: AuthStrategy;
942
+ protected gameId?: string;
943
+ protected config: Partial<ClientConfig>;
944
+ protected listeners: EventListeners;
945
+ protected internalClientSessionId?: string;
946
+ protected authContext?: {
947
+ isInIframe: boolean;
948
+ };
949
+ protected initPayload?: InitPayload;
950
+ protected connectionManager?: ConnectionManager;
951
+ protected launchId?: string;
952
+ /**
953
+ * Internal session manager for automatic session lifecycle.
954
+ * @private
955
+ * @internal
956
+ */
957
+ protected _sessionManager: {
958
+ startSession: (gameId: string) => Promise<{
959
+ sessionId: string;
960
+ }>;
961
+ endSession: (sessionId: string, gameId: string) => Promise<void>;
962
+ };
963
+ constructor(config?: Partial<ClientConfig>);
964
+ /**
965
+ * Gets the effective base URL for API requests.
966
+ */
967
+ getBaseUrl(): string;
968
+ /**
969
+ * Gets the effective game backend URL for integration requests.
970
+ */
971
+ protected getGameBackendUrl(): string;
972
+ /**
973
+ * Simple ping method for testing connectivity.
974
+ */
975
+ ping(): string;
976
+ /**
977
+ * Sets the authentication token for API requests.
978
+ */
979
+ setToken(token: string | null, tokenType?: TokenType): void;
980
+ setLaunchId(launchId: string | null | undefined): void;
981
+ /**
982
+ * Gets the current token type.
983
+ */
984
+ getTokenType(): TokenType;
985
+ /**
986
+ * Gets the current authentication token.
987
+ */
988
+ getToken(): string | null;
989
+ /**
990
+ * Checks if the client has a valid API token.
991
+ */
992
+ isAuthenticated(): boolean;
993
+ /**
994
+ * Registers a callback to be called when authentication state changes.
995
+ */
996
+ onAuthChange(callback: (token: string | null) => void): void;
997
+ /**
998
+ * Registers a callback to be called when connection issues are detected.
999
+ */
1000
+ onDisconnect(callback: (context: DisconnectContext) => void | Promise<void>): () => void;
1001
+ /**
1002
+ * Gets the current connection state.
1003
+ */
1004
+ getConnectionState(): ConnectionState | 'unknown';
1005
+ /**
1006
+ * Manually triggers a connection check immediately.
1007
+ */
1008
+ checkConnection(): Promise<ConnectionState | 'unknown'>;
1009
+ /**
1010
+ * Sets the authentication context for the client.
1011
+ * @internal
1012
+ */
1013
+ _setAuthContext(context: {
1014
+ isInIframe: boolean;
1015
+ }): void;
1016
+ /**
1017
+ * Registers an event listener for client events.
1018
+ *
1019
+ * @param event - The event name to listen for.
1020
+ * @param callback - The handler invoked when the event fires.
1021
+ * @returns A cleanup function that removes this specific listener.
1022
+ */
1023
+ on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): () => void;
1024
+ /**
1025
+ * Removes a previously registered event listener.
1026
+ *
1027
+ * @param event - The event name to stop listening for.
1028
+ * @param callback - The exact function reference passed to {@link on}.
1029
+ */
1030
+ off<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
1031
+ /**
1032
+ * Emits an event to all registered listeners.
1033
+ */
1034
+ protected emit<E extends keyof ClientEvents>(event: E, payload: ClientEvents[E]): void;
1035
+ /**
1036
+ * Makes an authenticated HTTP request to the platform API.
1037
+ */
1038
+ protected request<T>(path: string, method: Method, options?: {
1039
+ body?: unknown;
1040
+ headers?: Record<string, string>;
1041
+ raw?: boolean;
1042
+ retryPolicy?: RetryPolicy;
1043
+ }): Promise<T>;
1044
+ /**
1045
+ * Makes an authenticated HTTP request to the game's backend Worker.
1046
+ */
1047
+ protected requestGameBackend<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>, options?: {
1048
+ raw?: boolean;
1049
+ retryPolicy?: RetryPolicy;
1050
+ }): Promise<T>;
1051
+ /**
1052
+ * Ensures a gameId is available, throwing an error if not.
1053
+ */
1054
+ protected _ensureGameId(): string;
1055
+ /**
1056
+ * Detects and sets the authentication context (iframe vs standalone).
1057
+ */
1058
+ private _detectAuthContext;
1059
+ /**
1060
+ * Initializes connection monitoring if enabled.
1061
+ */
1062
+ private _initializeConnectionMonitor;
1063
+ private _initializeInternalSession;
1064
+ /**
1065
+ * Current user data and inventory management.
1066
+ * - `me()` - Get authenticated user profile
1067
+ * - `inventory.get()` - List user's items
1068
+ * - `inventory.add(slug, qty)` - Award items to user
1069
+ */
1070
+ users: {
1071
+ me: () => Promise<AuthenticatedUser>;
1072
+ inventory: {
1073
+ get: () => Promise<InventoryItemWithItem[]>;
1074
+ add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1075
+ remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1076
+ quantity: (identifier: string) => Promise<number>;
1077
+ has: (identifier: string, minQuantity?: number) => Promise<boolean>;
1078
+ };
1079
+ };
1080
+ }
1081
+
1082
+ /**
1083
+ * Auto-initializes a PlaycademyClient with context from the environment.
1084
+ * Works in both iframe mode (production/development) and standalone mode (local dev).
1085
+ *
1086
+ * This is the recommended way to initialize the SDK as it automatically:
1087
+ * - Detects the runtime environment (iframe vs standalone)
1088
+ * - Configures the client with the appropriate context
1089
+ * - Sets up event listeners for token refresh
1090
+ * - Exposes the client for debugging in development mode
1091
+ *
1092
+ * @param options - Optional configuration overrides
1093
+ * @param options.baseUrl - Override the base URL for API requests
1094
+ * @returns Promise resolving to a fully initialized PlaycademyClient
1095
+ * @throws Error if not running in a browser context
1096
+ *
1097
+ * @example
1098
+ * ```typescript
1099
+ * // Default initialization
1100
+ * const client = await PlaycademyClient.init()
1101
+ *
1102
+ * // With custom base URL
1103
+ * const client = await PlaycademyClient.init({ baseUrl: 'https://custom.api.com' })
1104
+ * ```
1105
+ */
1106
+ declare function init<T extends PlaycademyBaseClient = PlaycademyBaseClient>(this: new (config?: Partial<ClientConfig>) => T, options?: {
1107
+ baseUrl?: string;
1108
+ allowedParentOrigins?: string[];
1109
+ onDisconnect?: DisconnectHandler;
1110
+ enableConnectionMonitoring?: boolean;
1111
+ }): Promise<T>;
1112
+
1113
+ /**
1114
+ * Authenticates a user with email and password.
1115
+ *
1116
+ * This is a standalone authentication method that doesn't require an initialized client.
926
1117
  * Use this for login flows before creating a client instance.
927
1118
  *
928
1119
  * @deprecated Use client.auth.login() instead for better error handling and automatic token management
@@ -1538,225 +1729,6 @@ declare class PlaycademyMessaging {
1538
1729
  */
1539
1730
  declare const messaging: PlaycademyMessaging;
1540
1731
 
1541
- /**
1542
- * @fileoverview Authentication Strategy Pattern
1543
- *
1544
- * Provides different authentication strategies for the Playcademy SDK.
1545
- * Each strategy knows how to add its authentication headers to requests.
1546
- */
1547
-
1548
- /**
1549
- * Base interface for authentication strategies
1550
- */
1551
- interface AuthStrategy {
1552
- /** Get the token value */
1553
- getToken(): string | null;
1554
- /** Get the token type */
1555
- getType(): TokenType;
1556
- /** Get authentication headers for a request */
1557
- getHeaders(): Record<string, string>;
1558
- }
1559
-
1560
- /**
1561
- * Base Playcademy SDK client with shared infrastructure.
1562
- * Provides authentication, HTTP requests, events, connection monitoring,
1563
- * and fundamental namespaces used by all clients.
1564
- *
1565
- * Extended by PlaycademyClient (game SDK) and PlaycademyInternalClient (platform SDK).
1566
- */
1567
- declare abstract class PlaycademyBaseClient {
1568
- baseUrl: string;
1569
- gameUrl?: string;
1570
- mode: PlaycademyMode;
1571
- protected authStrategy: AuthStrategy;
1572
- protected gameId?: string;
1573
- protected config: Partial<ClientConfig>;
1574
- protected listeners: EventListeners;
1575
- protected internalClientSessionId?: string;
1576
- protected authContext?: {
1577
- isInIframe: boolean;
1578
- };
1579
- protected initPayload?: InitPayload;
1580
- protected connectionManager?: ConnectionManager;
1581
- protected launchId?: string;
1582
- /**
1583
- * Internal session manager for automatic session lifecycle.
1584
- * @private
1585
- * @internal
1586
- */
1587
- protected _sessionManager: {
1588
- startSession: (gameId: string) => Promise<{
1589
- sessionId: string;
1590
- }>;
1591
- endSession: (sessionId: string, gameId: string) => Promise<void>;
1592
- };
1593
- constructor(config?: Partial<ClientConfig>);
1594
- /**
1595
- * Gets the effective base URL for API requests.
1596
- */
1597
- getBaseUrl(): string;
1598
- /**
1599
- * Gets the effective game backend URL for integration requests.
1600
- */
1601
- protected getGameBackendUrl(): string;
1602
- /**
1603
- * Simple ping method for testing connectivity.
1604
- */
1605
- ping(): string;
1606
- /**
1607
- * Sets the authentication token for API requests.
1608
- */
1609
- setToken(token: string | null, tokenType?: TokenType): void;
1610
- setLaunchId(launchId: string | null | undefined): void;
1611
- /**
1612
- * Gets the current token type.
1613
- */
1614
- getTokenType(): TokenType;
1615
- /**
1616
- * Gets the current authentication token.
1617
- */
1618
- getToken(): string | null;
1619
- /**
1620
- * Checks if the client has a valid API token.
1621
- */
1622
- isAuthenticated(): boolean;
1623
- /**
1624
- * Registers a callback to be called when authentication state changes.
1625
- */
1626
- onAuthChange(callback: (token: string | null) => void): void;
1627
- /**
1628
- * Registers a callback to be called when connection issues are detected.
1629
- */
1630
- onDisconnect(callback: (context: DisconnectContext) => void | Promise<void>): () => void;
1631
- /**
1632
- * Gets the current connection state.
1633
- */
1634
- getConnectionState(): ConnectionState | 'unknown';
1635
- /**
1636
- * Manually triggers a connection check immediately.
1637
- */
1638
- checkConnection(): Promise<ConnectionState | 'unknown'>;
1639
- /**
1640
- * Sets the authentication context for the client.
1641
- * @internal
1642
- */
1643
- _setAuthContext(context: {
1644
- isInIframe: boolean;
1645
- }): void;
1646
- /**
1647
- * Registers an event listener for client events.
1648
- *
1649
- * @param event - The event name to listen for.
1650
- * @param callback - The handler invoked when the event fires.
1651
- * @returns A cleanup function that removes this specific listener.
1652
- */
1653
- on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): () => void;
1654
- /**
1655
- * Removes a previously registered event listener.
1656
- *
1657
- * @param event - The event name to stop listening for.
1658
- * @param callback - The exact function reference passed to {@link on}.
1659
- */
1660
- off<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
1661
- /**
1662
- * Emits an event to all registered listeners.
1663
- */
1664
- protected emit<E extends keyof ClientEvents>(event: E, payload: ClientEvents[E]): void;
1665
- /**
1666
- * Makes an authenticated HTTP request to the platform API.
1667
- */
1668
- protected request<T>(path: string, method: Method, options?: {
1669
- body?: unknown;
1670
- headers?: Record<string, string>;
1671
- raw?: boolean;
1672
- retryPolicy?: RetryPolicy;
1673
- }): Promise<T>;
1674
- /**
1675
- * Makes an authenticated HTTP request to the game's backend Worker.
1676
- */
1677
- protected requestGameBackend<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>, options?: {
1678
- raw?: boolean;
1679
- retryPolicy?: RetryPolicy;
1680
- }): Promise<T>;
1681
- /**
1682
- * Ensures a gameId is available, throwing an error if not.
1683
- */
1684
- protected _ensureGameId(): string;
1685
- /**
1686
- * Detects and sets the authentication context (iframe vs standalone).
1687
- */
1688
- private _detectAuthContext;
1689
- /**
1690
- * Initializes connection monitoring if enabled.
1691
- */
1692
- private _initializeConnectionMonitor;
1693
- private _initializeInternalSession;
1694
- /**
1695
- * Current user data and inventory management.
1696
- * - `me()` - Get authenticated user profile
1697
- * - `inventory.get()` - List user's items
1698
- * - `inventory.add(slug, qty)` - Award items to user
1699
- */
1700
- users: {
1701
- me: () => Promise<AuthenticatedUser>;
1702
- inventory: {
1703
- get: () => Promise<InventoryItemWithItem[]>;
1704
- add: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1705
- remove: (identifier: string, qty: number) => Promise<InventoryMutationResponse>;
1706
- quantity: (identifier: string) => Promise<number>;
1707
- has: (identifier: string, minQuantity?: number) => Promise<boolean>;
1708
- };
1709
- };
1710
- }
1711
-
1712
- /**
1713
- * Options for configuring activity tracking behavior.
1714
- */
1715
- interface StartActivityOptions {
1716
- /**
1717
- * How long heartbeats continue after the activity is automatically paused
1718
- * because the tab is hidden or the player is inactive while visible.
1719
- * Defaults to 10 minutes. Set to `Infinity` to keep heartbeats running
1720
- * indefinitely during automatic pauses. Invalid values fall back to the
1721
- * 10-minute default.
1722
- */
1723
- pausedHeartbeatTimeoutMs?: number;
1724
- /**
1725
- * @deprecated Use `pausedHeartbeatTimeoutMs` instead.
1726
- *
1727
- * Backward-compatible alias for callers that still use the old option
1728
- * name from earlier SDK releases.
1729
- */
1730
- hiddenTimeoutMs?: number;
1731
- /**
1732
- * How often to flush periodic heartbeats with accumulated time data.
1733
- * Defaults to 15 seconds. Set to `Infinity` to disable the interval;
1734
- * final unload/endActivity flushes still run. Values must be greater than
1735
- * 0 or `Infinity`; invalid values fall back to the 15-second default.
1736
- */
1737
- heartbeatIntervalMs?: number;
1738
- /**
1739
- * How long the tab can remain visible without keyboard or mouse activity
1740
- * before the activity is marked inactive. Defaults to 10 minutes. Set to
1741
- * `Infinity` to disable keyboard/mouse inactivity tracking. Invalid values
1742
- * fall back to the 10-minute default.
1743
- */
1744
- inactivityTimeoutMs?: number;
1745
- /**
1746
- * Stable identifier for this activity run. When provided, it is used on
1747
- * every heartbeat and on endActivity instead of a freshly-generated UUID.
1748
- *
1749
- * Pass the same `runId` across multiple `startActivity()` calls (for
1750
- * example, after the player closes and reopens a resumable activity) so
1751
- * downstream systems can correlate related sessions into a single run.
1752
- *
1753
- * Must be a UUID (the backend validates it as such) and unique per
1754
- * logical run. If omitted, the SDK generates a new UUID on each call,
1755
- * which means every session is treated as its own run.
1756
- */
1757
- runId?: string;
1758
- }
1759
-
1760
1732
  /**
1761
1733
  * Playcademy SDK client for game developers.
1762
1734
  * Provides namespaced access to platform features for games running inside Cademy.
@@ -1819,19 +1791,22 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1819
1791
  * User context (cached from init, refreshable):
1820
1792
  * - `user.role` - User's role (student, parent, teacher, etc.)
1821
1793
  * - `user.enrollments` - Courses the player is enrolled in for this game
1794
+ * - `user.refresh({ only: ['enrollments'] })` - Refresh enrollments from server
1822
1795
  * - `user.organizations` - Schools/districts the player belongs to
1823
1796
  * - `user.fetch()` - Refresh user context from server
1824
1797
  *
1825
1798
  * Activity tracking:
1826
- * - `startActivity(metadata)` - Begin tracking an activity with automatic
1827
- * hidden-tab and visible-tab inactivity handling, plus configurable
1828
- * paused-heartbeat timeout behavior
1799
+ * - `currentRunId` - Current activity run ID, or undefined when inactive
1800
+ * - `startActivity(metadata)` - Begin tracking an activity, return its run
1801
+ * ID, and automatically handle hidden-tab and visible-tab inactivity
1802
+ * with configurable paused-heartbeat timeout behavior
1829
1803
  * - `pauseActivity()` / `resumeActivity()` - Pause/resume timer
1830
1804
  * - `endActivity(scoreData)` - Submit activity results to TimeBack
1831
1805
  */
1832
1806
  timeback: {
1833
1807
  readonly user: TimebackUser;
1834
- startActivity: (metadata: ActivityData, options?: StartActivityOptions) => void;
1808
+ readonly currentRunId: string | undefined;
1809
+ startActivity: (metadata: ActivityData, options?: StartActivityOptions) => StartActivityResult;
1835
1810
  pauseActivity: () => void;
1836
1811
  resumeActivity: () => void;
1837
1812
  endActivity: (data: EndActivityScoreData) => Promise<EndActivityResponse>;
@@ -1911,6 +1886,57 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1911
1886
  };
1912
1887
  }
1913
1888
 
1889
+ /**
1890
+ * Options for configuring activity tracking behavior.
1891
+ */
1892
+ interface StartActivityOptions {
1893
+ /**
1894
+ * How long heartbeats continue after the activity is automatically paused
1895
+ * because the tab is hidden or the player is inactive while visible.
1896
+ * Defaults to 10 minutes. Set to `Infinity` to keep heartbeats running
1897
+ * indefinitely during automatic pauses. Invalid values fall back to the
1898
+ * 10-minute default.
1899
+ */
1900
+ pausedHeartbeatTimeoutMs?: number;
1901
+ /**
1902
+ * @deprecated Use `pausedHeartbeatTimeoutMs` instead.
1903
+ *
1904
+ * Backward-compatible alias for callers that still use the old option
1905
+ * name from earlier SDK releases.
1906
+ */
1907
+ hiddenTimeoutMs?: number;
1908
+ /**
1909
+ * How often to flush periodic heartbeats with accumulated time data.
1910
+ * Defaults to 15 seconds. Set to `Infinity` to disable the interval;
1911
+ * final unload/endActivity flushes still run. Values must be greater than
1912
+ * 0 or `Infinity`; invalid values fall back to the 15-second default.
1913
+ */
1914
+ heartbeatIntervalMs?: number;
1915
+ /**
1916
+ * How long the tab can remain visible without keyboard or mouse activity
1917
+ * before the activity is marked inactive. Defaults to 10 minutes. Set to
1918
+ * `Infinity` to disable keyboard/mouse inactivity tracking. Invalid values
1919
+ * fall back to the 10-minute default.
1920
+ */
1921
+ inactivityTimeoutMs?: number;
1922
+ /**
1923
+ * Stable identifier for this activity run. When provided, it is used on
1924
+ * every heartbeat and on endActivity instead of a freshly-generated UUID.
1925
+ *
1926
+ * Pass the same `runId` across multiple `startActivity()` calls (for
1927
+ * example, after the player closes and reopens a resumable activity) so
1928
+ * downstream systems can correlate related sessions into a single run.
1929
+ *
1930
+ * Must be a UUID (the backend validates it as such) and unique per
1931
+ * logical run. If omitted, the SDK generates a new UUID on each call,
1932
+ * which means every session is treated as its own run.
1933
+ */
1934
+ runId?: string;
1935
+ }
1936
+ interface StartActivityResult {
1937
+ runId: string;
1938
+ }
1939
+
1914
1940
  /**
1915
1941
  * Type definitions for the game timeback namespace.
1916
1942
  *
@@ -1920,7 +1946,9 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1920
1946
 
1921
1947
  /**
1922
1948
  * A TimeBack enrollment for the current game session.
1923
- * Alias for UserEnrollment without the optional gameId.
1949
+ * Alias for UserEnrollment without the optional gameId. Active enrollment IDs
1950
+ * are available at `enrollment.enrollmentIds?.active` when supplied by the
1951
+ * platform.
1924
1952
  */
1925
1953
  type TimebackEnrollment = Omit<UserEnrollment, 'gameId'>;
1926
1954
  /**
@@ -1956,6 +1984,14 @@ interface TimebackUserContext {
1956
1984
  /** User's organizations (schools/districts) */
1957
1985
  organizations: TimebackOrganization[];
1958
1986
  }
1987
+ /**
1988
+ * Slice options for refreshing the cached TimeBack user context.
1989
+ */
1990
+ type TimebackUserRefreshField = 'enrollments';
1991
+ interface TimebackUserRefreshOptions extends TTLCacheConfig {
1992
+ /** Refresh only these user data fields */
1993
+ only: readonly TimebackUserRefreshField[];
1994
+ }
1959
1995
  /**
1960
1996
  * XP data access for the current user.
1961
1997
  * Results are cached for 5 seconds to avoid redundant network requests.
@@ -1996,7 +2032,7 @@ interface TimebackUserXp {
1996
2032
  fetch(options?: GetXpOptions): Promise<XpResponse>;
1997
2033
  }
1998
2034
  /**
1999
- * TimeBack user object with both cached getters and fetch method.
2035
+ * TimeBack user object with cached getters, fetch, and targeted refresh methods.
2000
2036
  */
2001
2037
  interface TimebackUser extends TimebackUserContext {
2002
2038
  /**
@@ -2005,9 +2041,14 @@ interface TimebackUser extends TimebackUserContext {
2005
2041
  * @param options - Cache options (pass { force: true } to bypass cache)
2006
2042
  * @returns Promise resolving to fresh user context
2007
2043
  */
2008
- fetch(options?: {
2009
- force?: boolean;
2010
- }): Promise<TimebackUserContext>;
2044
+ fetch(options?: TTLCacheConfig): Promise<TimebackUserContext>;
2045
+ /**
2046
+ * Refresh selected TimeBack user data from the server.
2047
+ * Updates the cached user snapshot used by the synchronous getters.
2048
+ * @param options - Refresh fields and cache options
2049
+ * @returns Promise resolving to the updated user context
2050
+ */
2051
+ refresh(options: TimebackUserRefreshOptions): Promise<TimebackUserContext>;
2011
2052
  /**
2012
2053
  * XP data for the current user.
2013
2054
  * Call `xp.fetch()` to get XP from the server.