@playcademy/sdk 0.2.11 → 0.2.12

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
@@ -450,6 +450,23 @@ interface AuthenticatedUser {
450
450
  timeback?: UserTimebackData;
451
451
  }
452
452
 
453
+ /**
454
+ * TimeBack Enums & Literal Types
455
+ *
456
+ * Basic type definitions used throughout the TimeBack integration.
457
+ *
458
+ * @module types/timeback/types
459
+ */
460
+ /**
461
+ * Valid TimeBack subject values for course configuration.
462
+ * These are the supported subject values for OneRoster courses.
463
+ */
464
+ type TimebackSubject = 'Reading' | 'Language' | 'Vocabulary' | 'Social Studies' | 'Writing' | 'Science' | 'FastMath' | 'Math' | 'None';
465
+ /**
466
+ * Grade levels per AE OneRoster GradeEnum.
467
+ * -1 = Pre-K, 0 = Kindergarten, 1-12 = Grades 1-12, 13 = AP
468
+ */
469
+ type TimebackGrade = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
453
470
  /**
454
471
  * Valid Caliper subject values.
455
472
  * Matches OneRoster subjects, with "None" as a Caliper-specific fallback.
@@ -618,7 +635,7 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
618
635
  tableName: "items";
619
636
  dataType: "string";
620
637
  columnType: "PgEnumColumn";
621
- data: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade";
638
+ data: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other";
622
639
  driverParam: string;
623
640
  notNull: true;
624
641
  hasDefault: true;
@@ -999,6 +1016,9 @@ declare abstract class PlaycademyBaseClient {
999
1016
  * Initializes connection monitoring if enabled.
1000
1017
  */
1001
1018
  private _initializeConnectionMonitor;
1019
+ /**
1020
+ * Initializes an internal game session for automatic session management.
1021
+ */
1002
1022
  private _initializeInternalSession;
1003
1023
  /**
1004
1024
  * Current user data and inventory management.
@@ -1042,8 +1062,8 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1042
1062
  */
1043
1063
  runtime: {
1044
1064
  getGameToken: (gameId: string, options?: {
1045
- apply?: boolean | undefined;
1046
- } | undefined) => Promise<GameTokenResponse>;
1065
+ apply?: boolean;
1066
+ }) => Promise<GameTokenResponse>;
1047
1067
  exit: () => Promise<void>;
1048
1068
  onInit: (handler: (context: GameContextPayload) => void) => void;
1049
1069
  onTokenRefresh: (handler: (data: {
@@ -1067,7 +1087,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1067
1087
  getListenerCounts: () => Record<string, number>;
1068
1088
  assets: {
1069
1089
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
1070
- fetch: (path: string, options?: RequestInit | undefined) => Promise<Response>;
1090
+ fetch: (path: string, options?: RequestInit) => Promise<Response>;
1071
1091
  json: <T = unknown>(path: string) => Promise<T>;
1072
1092
  blob: (path: string) => Promise<Blob>;
1073
1093
  text: (path: string) => Promise<string>;
@@ -1110,7 +1130,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1110
1130
  * - `submit(gameId, score, metadata?)` - Record a game score
1111
1131
  */
1112
1132
  scores: {
1113
- submit: (gameId: string, score: number, metadata?: Record<string, unknown> | undefined) => Promise<ScoreSubmission>;
1133
+ submit: (gameId: string, score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
1114
1134
  };
1115
1135
  /**
1116
1136
  * Realtime multiplayer authentication.
@@ -1127,13 +1147,13 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
1127
1147
  * - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
1128
1148
  */
1129
1149
  backend: {
1130
- get<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
1131
- post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
1132
- put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
1133
- patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
1134
- delete<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
1135
- request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
1136
- download(path: string, method?: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
1150
+ get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
1151
+ post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
1152
+ put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
1153
+ patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
1154
+ delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
1155
+ request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
1156
+ download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
1137
1157
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
1138
1158
  };
1139
1159
  /** Auto-initializes a PlaycademyClient with context from the environment */
@@ -1191,6 +1211,45 @@ interface TimebackUserContext {
1191
1211
  /** User's organizations (schools/districts) */
1192
1212
  organizations: TimebackOrganization[];
1193
1213
  }
1214
+ /**
1215
+ * XP data access for the current user.
1216
+ * Results are cached for 5 seconds to avoid redundant network requests.
1217
+ */
1218
+ interface TimebackUserXp {
1219
+ /**
1220
+ * Fetch XP data from the server.
1221
+ * Returns XP for all courses in this game, or filter by grade/subject.
1222
+ * Results are cached for 5 seconds (use `force: true` to bypass).
1223
+ *
1224
+ * @param options - Query options
1225
+ * @param options.grade - Grade level to filter (must be used with subject)
1226
+ * @param options.subject - Subject to filter (must be used with grade)
1227
+ * @param options.include - Additional data to include: 'perCourse', 'today'
1228
+ * @param options.force - Bypass cache and fetch fresh data (default: false)
1229
+ * @returns Promise resolving to XP data
1230
+ *
1231
+ * @example
1232
+ * ```typescript
1233
+ * // Get total XP for all game courses
1234
+ * const xp = await client.timeback.user.xp.fetch()
1235
+ *
1236
+ * // Get XP for a specific grade/subject
1237
+ * const xp = await client.timeback.user.xp.fetch({
1238
+ * grade: 3,
1239
+ * subject: 'Math'
1240
+ * })
1241
+ *
1242
+ * // Get XP with per-course breakdown
1243
+ * const xp = await client.timeback.user.xp.fetch({
1244
+ * include: ['perCourse', 'today']
1245
+ * })
1246
+ *
1247
+ * // Force fresh data
1248
+ * const xp = await client.timeback.user.xp.fetch({ force: true })
1249
+ * ```
1250
+ */
1251
+ fetch(options?: GetXpOptions): Promise<XpResponse>;
1252
+ }
1194
1253
  /**
1195
1254
  * TimeBack user object with both cached getters and fetch method.
1196
1255
  */
@@ -1204,6 +1263,42 @@ interface TimebackUser extends TimebackUserContext {
1204
1263
  fetch(options?: {
1205
1264
  force?: boolean;
1206
1265
  }): Promise<TimebackUserContext>;
1266
+ /**
1267
+ * XP data for the current user.
1268
+ * Call `xp.fetch()` to get XP from the server.
1269
+ */
1270
+ xp: TimebackUserXp;
1271
+ }
1272
+ /**
1273
+ * Options for querying student XP.
1274
+ */
1275
+ interface GetXpOptions {
1276
+ /** Grade level to filter (must be used with subject) */
1277
+ grade?: TimebackGrade;
1278
+ /** Subject to filter (must be used with grade) */
1279
+ subject?: TimebackSubject;
1280
+ /** Additional data to include: 'perCourse', 'today' */
1281
+ include?: ('perCourse' | 'today')[];
1282
+ /** Bypass cache and fetch fresh data (default: false) */
1283
+ force?: boolean;
1284
+ }
1285
+ /**
1286
+ * XP data for a single course.
1287
+ */
1288
+ interface CourseXp {
1289
+ grade: TimebackGrade;
1290
+ subject: TimebackSubject;
1291
+ title: string;
1292
+ totalXp: number;
1293
+ todayXp?: number;
1294
+ }
1295
+ /**
1296
+ * Response from XP query.
1297
+ */
1298
+ interface XpResponse {
1299
+ totalXp: number;
1300
+ todayXp?: number;
1301
+ courses?: CourseXp[];
1207
1302
  }
1208
1303
 
1209
1304
  /**
package/dist/index.js CHANGED
@@ -1185,7 +1185,7 @@ var ACHIEVEMENT_DEFINITIONS = [
1185
1185
  }
1186
1186
  ];
1187
1187
  // ../constants/src/typescript.ts
1188
- var TSC_PACKAGE = "@typescript/native-preview";
1188
+ var TSC_PACKAGE = "typescript";
1189
1189
  var USE_NATIVE_TSC = TSC_PACKAGE.includes("native-preview");
1190
1190
  // ../constants/src/overworld.ts
1191
1191
  var ITEM_SLUGS = {
@@ -1212,7 +1212,8 @@ var BADGES = {
1212
1212
  };
1213
1213
  // ../constants/src/timeback.ts
1214
1214
  var TIMEBACK_ROUTES = {
1215
- END_ACTIVITY: "/integrations/timeback/end-activity"
1215
+ END_ACTIVITY: "/integrations/timeback/end-activity",
1216
+ GET_XP: "/integrations/timeback/xp"
1216
1217
  };
1217
1218
  // src/core/cache/singleton-cache.ts
1218
1219
  function createSingletonCache() {
@@ -1390,6 +1391,26 @@ function createTTLCache(options) {
1390
1391
  return { get, clear, size, prune, getKeys, has };
1391
1392
  }
1392
1393
 
1394
+ // src/core/guards.ts
1395
+ var VALID_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
1396
+ var VALID_SUBJECTS = [
1397
+ "Reading",
1398
+ "Language",
1399
+ "Vocabulary",
1400
+ "Social Studies",
1401
+ "Writing",
1402
+ "Science",
1403
+ "FastMath",
1404
+ "Math",
1405
+ "None"
1406
+ ];
1407
+ function isValidGrade(value) {
1408
+ return typeof value === "number" && Number.isInteger(value) && VALID_GRADES.includes(value);
1409
+ }
1410
+ function isValidSubject(value) {
1411
+ return typeof value === "string" && VALID_SUBJECTS.includes(value);
1412
+ }
1413
+
1393
1414
  // src/namespaces/game/timeback.ts
1394
1415
  function createTimebackNamespace(client) {
1395
1416
  let currentActivity = null;
@@ -1397,6 +1418,10 @@ function createTimebackNamespace(client) {
1397
1418
  ttl: 5 * 60 * 1000,
1398
1419
  keyPrefix: "game.timeback.user"
1399
1420
  });
1421
+ const xpCache = createTTLCache({
1422
+ ttl: 5000,
1423
+ keyPrefix: "game.timeback.xp"
1424
+ });
1400
1425
  const getTimeback = () => client["initPayload"]?.timeback;
1401
1426
  return {
1402
1427
  get user() {
@@ -1427,6 +1452,49 @@ function createTimebackNamespace(client) {
1427
1452
  organizations: response.organizations
1428
1453
  };
1429
1454
  }, options);
1455
+ },
1456
+ xp: {
1457
+ fetch: async (options) => {
1458
+ const hasGrade = options?.grade !== undefined;
1459
+ const hasSubject = options?.subject !== undefined;
1460
+ if (hasGrade !== hasSubject) {
1461
+ throw new Error("Both grade and subject must be provided together");
1462
+ }
1463
+ if (hasGrade && !isValidGrade(options.grade)) {
1464
+ throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
1465
+ }
1466
+ if (hasSubject && !isValidSubject(options.subject)) {
1467
+ throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
1468
+ }
1469
+ const validIncludeOptions = ["perCourse", "today"];
1470
+ if (options?.include?.length) {
1471
+ for (const opt of options.include) {
1472
+ if (!validIncludeOptions.includes(opt)) {
1473
+ throw new Error(`Invalid include option: ${opt}. Valid options: ${validIncludeOptions.join(", ")}`);
1474
+ }
1475
+ }
1476
+ }
1477
+ const cacheKey = [
1478
+ options?.grade ?? "",
1479
+ options?.subject ?? "",
1480
+ options?.include?.sort().join(",") ?? ""
1481
+ ].join(":");
1482
+ return xpCache.get(cacheKey, async () => {
1483
+ const params = new URLSearchParams;
1484
+ if (hasGrade) {
1485
+ params.set("grade", String(options.grade));
1486
+ }
1487
+ if (hasSubject) {
1488
+ params.set("subject", options.subject);
1489
+ }
1490
+ if (options?.include?.length) {
1491
+ params.set("include", options.include.join(","));
1492
+ }
1493
+ const queryString = params.toString();
1494
+ const endpoint = `${TIMEBACK_ROUTES.GET_XP}${queryString ? `?${queryString}` : ""}`;
1495
+ return client["requestGameBackend"](endpoint, "GET");
1496
+ }, { force: options?.force });
1497
+ }
1430
1498
  }
1431
1499
  };
1432
1500
  },