@playcademy/sdk 0.2.10 → 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/internal.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
  },
@@ -1769,9 +1837,9 @@ function createDevNamespace(client) {
1769
1837
  }
1770
1838
  throw new Error("No deployment actions specified (need metadata, file, or backend)");
1771
1839
  },
1772
- seed: async (slug, code, environment) => {
1840
+ seed: async (slug, code, environment, secrets) => {
1773
1841
  return client["request"](`/games/${slug}/seed`, "POST", {
1774
- body: { code, environment }
1842
+ body: { code, environment, secrets }
1775
1843
  });
1776
1844
  },
1777
1845
  upsert: async (slug, metadata) => client["request"](`/games/${slug}`, "PUT", { body: metadata }),
package/dist/server.d.ts CHANGED
@@ -295,6 +295,27 @@ interface EndActivityResponse {
295
295
  scoreStatus?: string;
296
296
  inProgress?: string;
297
297
  }
298
+ /**
299
+ * XP data for a single course.
300
+ */
301
+ interface StudentCourseXp {
302
+ grade: number;
303
+ subject: string;
304
+ title: string;
305
+ totalXp: number;
306
+ todayXp?: number;
307
+ }
308
+ /**
309
+ * Response from student XP query.
310
+ */
311
+ interface StudentXpResponse {
312
+ /** Total XP across all queried courses */
313
+ totalXp: number;
314
+ /** Today's XP (if requested) */
315
+ todayXp?: number;
316
+ /** Per-course XP breakdown (if requested) */
317
+ courses?: StudentCourseXp[];
318
+ }
298
319
 
299
320
  /**
300
321
  * @fileoverview Server SDK Type Definitions
@@ -588,6 +609,13 @@ declare class PlaycademyClient {
588
609
  * ```
589
610
  */
590
611
  static init(config: PlaycademyServerClientConfig): Promise<PlaycademyClient>;
612
+ /**
613
+ * Fetch gameId from API using the API token.
614
+ *
615
+ * @private
616
+ * @throws {Error} Always throws - gameId fetching not yet implemented
617
+ * @todo Implement API endpoint to fetch gameId from API token
618
+ */
591
619
  private fetchGameId;
592
620
  /**
593
621
  * Makes an authenticated HTTP request to the API.
@@ -617,6 +645,11 @@ declare class PlaycademyClient {
617
645
  /** TimeBack integration methods (endActivity) */
618
646
  timeback: {
619
647
  endActivity: (studentId: string, payload: EndActivityPayload) => Promise<EndActivityResponse>;
648
+ getStudentXp: (studentId: string, options?: {
649
+ grade?: number;
650
+ subject?: string;
651
+ include?: ("perCourse" | "today")[];
652
+ }) => Promise<StudentXpResponse>;
620
653
  };
621
654
  }
622
655
 
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/server/guards.ts
1
+ // src/core/guards.ts
2
2
  var VALID_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
3
3
  var VALID_SUBJECTS = [
4
4
  "Reading",
@@ -17,7 +17,6 @@ function isValidGrade(value) {
17
17
  function isValidSubject(value) {
18
18
  return typeof value === "string" && VALID_SUBJECTS.includes(value);
19
19
  }
20
-
21
20
  // src/server/namespaces/timeback.ts
22
21
  function createTimebackNamespace(client) {
23
22
  function enrichActivityData(data) {
@@ -44,6 +43,33 @@ function createTimebackNamespace(client) {
44
43
  xpEarned: payload.xpEarned,
45
44
  masteredUnits: payload.masteredUnits
46
45
  });
46
+ },
47
+ getStudentXp: async (studentId, options) => {
48
+ const hasGrade = options?.grade !== undefined;
49
+ const hasSubject = options?.subject !== undefined;
50
+ if (hasGrade !== hasSubject) {
51
+ throw new Error("Both grade and subject must be provided together");
52
+ }
53
+ if (hasGrade && !isValidGrade(options.grade)) {
54
+ throw new Error(`Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`);
55
+ }
56
+ if (hasSubject && !isValidSubject(options.subject)) {
57
+ throw new Error(`Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`);
58
+ }
59
+ const params = new URLSearchParams;
60
+ params.set("gameId", client.gameId);
61
+ if (options?.grade !== undefined) {
62
+ params.set("grade", String(options.grade));
63
+ }
64
+ if (options?.subject) {
65
+ params.set("subject", options.subject);
66
+ }
67
+ if (options?.include?.length) {
68
+ params.set("include", options.include.join(","));
69
+ }
70
+ const queryString = params.toString();
71
+ const endpoint = `/api/timeback/student-xp/${studentId}?${queryString}`;
72
+ return client["request"](endpoint, "GET");
47
73
  }
48
74
  };
49
75
  }
package/dist/types.d.ts CHANGED
@@ -800,7 +800,7 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
800
800
  tableName: "user";
801
801
  dataType: "string";
802
802
  columnType: "PgEnumColumn";
803
- data: "admin" | "developer" | "player";
803
+ data: "admin" | "player" | "developer";
804
804
  driverParam: string;
805
805
  notNull: true;
806
806
  hasDefault: true;
@@ -817,7 +817,7 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
817
817
  tableName: "user";
818
818
  dataType: "string";
819
819
  columnType: "PgEnumColumn";
820
- data: "approved" | "none" | "pending";
820
+ data: "none" | "pending" | "approved";
821
821
  driverParam: string;
822
822
  notNull: true;
823
823
  hasDefault: true;
@@ -1006,7 +1006,7 @@ declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
1006
1006
  tableName: "games";
1007
1007
  dataType: "string";
1008
1008
  columnType: "PgEnumColumn";
1009
- data: "external" | "hosted";
1009
+ data: "hosted" | "external";
1010
1010
  driverParam: string;
1011
1011
  notNull: true;
1012
1012
  hasDefault: true;
@@ -1057,7 +1057,7 @@ declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
1057
1057
  tableName: "games";
1058
1058
  dataType: "string";
1059
1059
  columnType: "PgEnumColumn";
1060
- data: "godot" | "unity" | "web";
1060
+ data: "web" | "godot" | "unity";
1061
1061
  driverParam: string;
1062
1062
  notNull: true;
1063
1063
  hasDefault: true;
@@ -1333,7 +1333,7 @@ declare const gameCustomHostnames: drizzle_orm_pg_core.PgTableWithColumns<{
1333
1333
  tableName: "game_custom_hostnames";
1334
1334
  dataType: "string";
1335
1335
  columnType: "PgEnumColumn";
1336
- data: "production" | "staging";
1336
+ data: "staging" | "production";
1337
1337
  driverParam: string;
1338
1338
  notNull: true;
1339
1339
  hasDefault: true;
@@ -1350,7 +1350,7 @@ declare const gameCustomHostnames: drizzle_orm_pg_core.PgTableWithColumns<{
1350
1350
  tableName: "game_custom_hostnames";
1351
1351
  dataType: "string";
1352
1352
  columnType: "PgEnumColumn";
1353
- data: "active" | "blocked" | "deleted" | "pending" | "pending_deletion" | "pending_deployment" | "pending_validation";
1353
+ data: "pending" | "active" | "pending_validation" | "pending_deployment" | "pending_deletion" | "blocked" | "deleted";
1354
1354
  driverParam: string;
1355
1355
  notNull: true;
1356
1356
  hasDefault: true;
@@ -1367,7 +1367,7 @@ declare const gameCustomHostnames: drizzle_orm_pg_core.PgTableWithColumns<{
1367
1367
  tableName: "game_custom_hostnames";
1368
1368
  dataType: "string";
1369
1369
  columnType: "PgEnumColumn";
1370
- data: "active" | "deleted" | "initializing" | "pending_deployment" | "pending_issuance" | "pending_validation";
1370
+ data: "active" | "pending_validation" | "pending_deployment" | "deleted" | "initializing" | "pending_issuance";
1371
1371
  driverParam: string;
1372
1372
  notNull: true;
1373
1373
  hasDefault: true;
@@ -1529,7 +1529,7 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
1529
1529
  tableName: "items";
1530
1530
  dataType: "string";
1531
1531
  columnType: "PgEnumColumn";
1532
- data: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade";
1532
+ data: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other";
1533
1533
  driverParam: string;
1534
1534
  notNull: true;
1535
1535
  hasDefault: true;
@@ -2220,7 +2220,7 @@ declare const mapElements: drizzle_orm_pg_core.PgTableWithColumns<{
2220
2220
  tableName: "map_elements";
2221
2221
  dataType: "string";
2222
2222
  columnType: "PgEnumColumn";
2223
- data: "door_in" | "door_out" | "game_entry" | "game_registry" | "info" | "npc_interaction" | "quest_trigger" | "teleport";
2223
+ data: "info" | "game_entry" | "game_registry" | "teleport" | "door_in" | "door_out" | "npc_interaction" | "quest_trigger";
2224
2224
  driverParam: string;
2225
2225
  notNull: true;
2226
2226
  hasDefault: false;
@@ -2773,7 +2773,7 @@ declare const characterComponents: drizzle_orm_pg_core.PgTableWithColumns<{
2773
2773
  tableName: "character_components";
2774
2774
  dataType: "string";
2775
2775
  columnType: "PgEnumColumn";
2776
- data: "accessory" | "body" | "eyes" | "hairstyle" | "outfit";
2776
+ data: "accessory" | "body" | "outfit" | "hairstyle" | "eyes";
2777
2777
  driverParam: string;
2778
2778
  notNull: true;
2779
2779
  hasDefault: false;
@@ -3299,7 +3299,7 @@ declare const notifications: drizzle_orm_pg_core.PgTableWithColumns<{
3299
3299
  tableName: "notifications";
3300
3300
  dataType: "string";
3301
3301
  columnType: "PgEnumColumn";
3302
- data: "high" | "low" | "normal" | "urgent";
3302
+ data: "low" | "normal" | "high" | "urgent";
3303
3303
  driverParam: string;
3304
3304
  notNull: true;
3305
3305
  hasDefault: true;
@@ -3316,7 +3316,7 @@ declare const notifications: drizzle_orm_pg_core.PgTableWithColumns<{
3316
3316
  tableName: "notifications";
3317
3317
  dataType: "string";
3318
3318
  columnType: "PgEnumColumn";
3319
- data: "clicked" | "delivered" | "dismissed" | "expired" | "pending" | "seen";
3319
+ data: "pending" | "delivered" | "seen" | "clicked" | "dismissed" | "expired";
3320
3320
  driverParam: string;
3321
3321
  notNull: true;
3322
3322
  hasDefault: true;
@@ -3477,33 +3477,33 @@ declare const UpsertGameMetadataSchema: z.ZodEffects<z.ZodObject<{
3477
3477
  gameType: z.ZodDefault<z.ZodOptional<z.ZodEnum<["hosted", "external"]>>>;
3478
3478
  externalUrl: z.ZodOptional<z.ZodString>;
3479
3479
  }, "strip", z.ZodTypeAny, {
3480
- displayName: string;
3481
- mapElementId?: string | null | undefined;
3482
- platform: "godot" | "unity" | "web";
3483
3480
  metadata: Record<string, unknown>;
3484
- gameType: "external" | "hosted";
3481
+ displayName: string;
3482
+ gameType: "hosted" | "external";
3483
+ platform: "web" | "godot" | "unity";
3485
3484
  externalUrl?: string | undefined;
3485
+ mapElementId?: string | null | undefined;
3486
3486
  }, {
3487
3487
  displayName: string;
3488
- mapElementId?: string | null | undefined;
3489
- platform: "godot" | "unity" | "web";
3488
+ platform: "web" | "godot" | "unity";
3490
3489
  metadata?: Record<string, unknown> | undefined;
3491
- gameType?: "external" | "hosted" | undefined;
3490
+ gameType?: "hosted" | "external" | undefined;
3492
3491
  externalUrl?: string | undefined;
3493
- }>, {
3494
- displayName: string;
3495
3492
  mapElementId?: string | null | undefined;
3496
- platform: "godot" | "unity" | "web";
3493
+ }>, {
3497
3494
  metadata: Record<string, unknown>;
3498
- gameType: "external" | "hosted";
3495
+ displayName: string;
3496
+ gameType: "hosted" | "external";
3497
+ platform: "web" | "godot" | "unity";
3499
3498
  externalUrl?: string | undefined;
3499
+ mapElementId?: string | null | undefined;
3500
3500
  }, {
3501
3501
  displayName: string;
3502
- mapElementId?: string | null | undefined;
3503
- platform: "godot" | "unity" | "web";
3502
+ platform: "web" | "godot" | "unity";
3504
3503
  metadata?: Record<string, unknown> | undefined;
3505
- gameType?: "external" | "hosted" | undefined;
3504
+ gameType?: "hosted" | "external" | undefined;
3506
3505
  externalUrl?: string | undefined;
3506
+ mapElementId?: string | null | undefined;
3507
3507
  }>;
3508
3508
 
3509
3509
  declare const InsertItemSchema: drizzle_zod.BuildSchema<"insert", {
@@ -3597,7 +3597,7 @@ declare const InsertItemSchema: drizzle_zod.BuildSchema<"insert", {
3597
3597
  tableName: "items";
3598
3598
  dataType: "string";
3599
3599
  columnType: "PgEnumColumn";
3600
- data: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade";
3600
+ data: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other";
3601
3601
  driverParam: string;
3602
3602
  notNull: true;
3603
3603
  hasDefault: true;
@@ -3692,24 +3692,24 @@ declare const UpdateItemSchema: z.ZodObject<Omit<{
3692
3692
  imageUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
3693
3693
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
3694
3694
  createdAt: z.ZodOptional<z.ZodDate>;
3695
- }, "createdAt" | "id">, "strip", z.ZodTypeAny, {
3695
+ }, "id" | "createdAt">, "strip", z.ZodTypeAny, {
3696
+ description?: string | null | undefined;
3697
+ metadata?: Record<string, unknown> | undefined;
3698
+ type?: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other" | undefined;
3696
3699
  slug?: string | undefined;
3697
- gameId?: string | null | undefined;
3698
3700
  displayName?: string | undefined;
3699
- description?: string | null | undefined;
3700
- type?: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade" | undefined;
3701
+ gameId?: string | null | undefined;
3701
3702
  isPlaceable?: boolean | undefined;
3702
3703
  imageUrl?: string | null | undefined;
3703
- metadata?: Record<string, unknown> | undefined;
3704
3704
  }, {
3705
+ description?: string | null | undefined;
3706
+ metadata?: Record<string, unknown> | undefined;
3707
+ type?: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other" | undefined;
3705
3708
  slug?: string | undefined;
3706
- gameId?: string | null | undefined;
3707
3709
  displayName?: string | undefined;
3708
- description?: string | null | undefined;
3709
- type?: "accessory" | "badge" | "collectible" | "consumable" | "currency" | "other" | "trophy" | "unlock" | "upgrade" | undefined;
3710
+ gameId?: string | null | undefined;
3710
3711
  isPlaceable?: boolean | undefined;
3711
3712
  imageUrl?: string | null | undefined;
3712
- metadata?: Record<string, unknown> | undefined;
3713
3713
  }>;
3714
3714
  declare const InsertCurrencySchema: drizzle_zod.BuildSchema<"insert", {
3715
3715
  id: drizzle_orm_pg_core.PgColumn<{
@@ -3819,16 +3819,16 @@ declare const InsertCurrencySchema: drizzle_zod.BuildSchema<"insert", {
3819
3819
  isPrimary: z.ZodDefault<z.ZodBoolean>;
3820
3820
  }>;
3821
3821
  declare const UpdateCurrencySchema: z.ZodObject<{
3822
- itemId: z.ZodOptional<z.ZodString>;
3823
3822
  symbol: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
3823
+ itemId: z.ZodOptional<z.ZodString>;
3824
3824
  isPrimary: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
3825
3825
  }, "strip", z.ZodTypeAny, {
3826
- itemId?: string | undefined;
3827
3826
  symbol?: string | null | undefined;
3827
+ itemId?: string | undefined;
3828
3828
  isPrimary?: boolean | undefined;
3829
3829
  }, {
3830
- itemId?: string | undefined;
3831
3830
  symbol?: string | null | undefined;
3831
+ itemId?: string | undefined;
3832
3832
  isPrimary?: boolean | undefined;
3833
3833
  }>;
3834
3834
  declare const InsertShopListingSchema: drizzle_zod.BuildSchema<"insert", {
@@ -4039,28 +4039,28 @@ declare const UpdateShopListingSchema: z.ZodObject<{
4039
4039
  updatedAt: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodDate>>>;
4040
4040
  }, "strip", z.ZodTypeAny, {
4041
4041
  id?: string | undefined;
4042
+ createdAt?: Date | undefined;
4043
+ updatedAt?: Date | null | undefined;
4044
+ isActive?: boolean | undefined;
4042
4045
  itemId?: string | undefined;
4043
4046
  currencyId?: string | undefined;
4044
4047
  price?: number | undefined;
4045
4048
  sellBackPercentage?: number | null | undefined;
4046
4049
  stock?: number | null | undefined;
4047
- isActive?: boolean | undefined;
4048
4050
  availableFrom?: Date | null | undefined;
4049
4051
  availableUntil?: Date | null | undefined;
4050
- createdAt?: Date | undefined;
4051
- updatedAt?: Date | null | undefined;
4052
4052
  }, {
4053
4053
  id?: string | undefined;
4054
+ createdAt?: Date | undefined;
4055
+ updatedAt?: Date | null | undefined;
4056
+ isActive?: boolean | undefined;
4054
4057
  itemId?: string | undefined;
4055
4058
  currencyId?: string | undefined;
4056
4059
  price?: number | undefined;
4057
4060
  sellBackPercentage?: number | null | undefined;
4058
4061
  stock?: number | null | undefined;
4059
- isActive?: boolean | undefined;
4060
4062
  availableFrom?: Date | null | undefined;
4061
4063
  availableUntil?: Date | null | undefined;
4062
- createdAt?: Date | undefined;
4063
- updatedAt?: Date | null | undefined;
4064
4064
  }>;
4065
4065
 
4066
4066
  type GameRow = typeof games.$inferSelect;
@@ -4873,6 +4873,9 @@ declare abstract class PlaycademyBaseClient {
4873
4873
  * Initializes connection monitoring if enabled.
4874
4874
  */
4875
4875
  private _initializeConnectionMonitor;
4876
+ /**
4877
+ * Initializes an internal game session for automatic session management.
4878
+ */
4876
4879
  private _initializeInternalSession;
4877
4880
  /**
4878
4881
  * Current user data and inventory management.
@@ -4916,8 +4919,8 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
4916
4919
  */
4917
4920
  runtime: {
4918
4921
  getGameToken: (gameId: string, options?: {
4919
- apply?: boolean | undefined;
4920
- } | undefined) => Promise<GameTokenResponse>;
4922
+ apply?: boolean;
4923
+ }) => Promise<GameTokenResponse>;
4921
4924
  exit: () => Promise<void>;
4922
4925
  onInit: (handler: (context: GameContextPayload) => void) => void;
4923
4926
  onTokenRefresh: (handler: (data: {
@@ -4941,7 +4944,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
4941
4944
  getListenerCounts: () => Record<string, number>;
4942
4945
  assets: {
4943
4946
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
4944
- fetch: (path: string, options?: RequestInit | undefined) => Promise<Response>;
4947
+ fetch: (path: string, options?: RequestInit) => Promise<Response>;
4945
4948
  json: <T = unknown>(path: string) => Promise<T>;
4946
4949
  blob: (path: string) => Promise<Blob>;
4947
4950
  text: (path: string) => Promise<string>;
@@ -4984,7 +4987,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
4984
4987
  * - `submit(gameId, score, metadata?)` - Record a game score
4985
4988
  */
4986
4989
  scores: {
4987
- submit: (gameId: string, score: number, metadata?: Record<string, unknown> | undefined) => Promise<ScoreSubmission>;
4990
+ submit: (gameId: string, score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
4988
4991
  };
4989
4992
  /**
4990
4993
  * Realtime multiplayer authentication.
@@ -5001,13 +5004,13 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5001
5004
  * - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
5002
5005
  */
5003
5006
  backend: {
5004
- get<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
5005
- post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5006
- put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5007
- patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5008
- delete<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
5009
- request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5010
- download(path: string, method?: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
5007
+ get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
5008
+ post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5009
+ put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5010
+ patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5011
+ delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
5012
+ request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
5013
+ download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
5011
5014
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
5012
5015
  };
5013
5016
  /** Auto-initializes a PlaycademyClient with context from the environment */
@@ -5065,6 +5068,45 @@ interface TimebackUserContext {
5065
5068
  /** User's organizations (schools/districts) */
5066
5069
  organizations: TimebackOrganization[];
5067
5070
  }
5071
+ /**
5072
+ * XP data access for the current user.
5073
+ * Results are cached for 5 seconds to avoid redundant network requests.
5074
+ */
5075
+ interface TimebackUserXp {
5076
+ /**
5077
+ * Fetch XP data from the server.
5078
+ * Returns XP for all courses in this game, or filter by grade/subject.
5079
+ * Results are cached for 5 seconds (use `force: true` to bypass).
5080
+ *
5081
+ * @param options - Query options
5082
+ * @param options.grade - Grade level to filter (must be used with subject)
5083
+ * @param options.subject - Subject to filter (must be used with grade)
5084
+ * @param options.include - Additional data to include: 'perCourse', 'today'
5085
+ * @param options.force - Bypass cache and fetch fresh data (default: false)
5086
+ * @returns Promise resolving to XP data
5087
+ *
5088
+ * @example
5089
+ * ```typescript
5090
+ * // Get total XP for all game courses
5091
+ * const xp = await client.timeback.user.xp.fetch()
5092
+ *
5093
+ * // Get XP for a specific grade/subject
5094
+ * const xp = await client.timeback.user.xp.fetch({
5095
+ * grade: 3,
5096
+ * subject: 'Math'
5097
+ * })
5098
+ *
5099
+ * // Get XP with per-course breakdown
5100
+ * const xp = await client.timeback.user.xp.fetch({
5101
+ * include: ['perCourse', 'today']
5102
+ * })
5103
+ *
5104
+ * // Force fresh data
5105
+ * const xp = await client.timeback.user.xp.fetch({ force: true })
5106
+ * ```
5107
+ */
5108
+ fetch(options?: GetXpOptions): Promise<XpResponse>;
5109
+ }
5068
5110
  /**
5069
5111
  * TimeBack user object with both cached getters and fetch method.
5070
5112
  */
@@ -5078,6 +5120,42 @@ interface TimebackUser extends TimebackUserContext {
5078
5120
  fetch(options?: {
5079
5121
  force?: boolean;
5080
5122
  }): Promise<TimebackUserContext>;
5123
+ /**
5124
+ * XP data for the current user.
5125
+ * Call `xp.fetch()` to get XP from the server.
5126
+ */
5127
+ xp: TimebackUserXp;
5128
+ }
5129
+ /**
5130
+ * Options for querying student XP.
5131
+ */
5132
+ interface GetXpOptions {
5133
+ /** Grade level to filter (must be used with subject) */
5134
+ grade?: TimebackGrade;
5135
+ /** Subject to filter (must be used with grade) */
5136
+ subject?: TimebackSubject;
5137
+ /** Additional data to include: 'perCourse', 'today' */
5138
+ include?: ('perCourse' | 'today')[];
5139
+ /** Bypass cache and fetch fresh data (default: false) */
5140
+ force?: boolean;
5141
+ }
5142
+ /**
5143
+ * XP data for a single course.
5144
+ */
5145
+ interface CourseXp {
5146
+ grade: TimebackGrade;
5147
+ subject: TimebackSubject;
5148
+ title: string;
5149
+ totalXp: number;
5150
+ todayXp?: number;
5151
+ }
5152
+ /**
5153
+ * Response from XP query.
5154
+ */
5155
+ interface XpResponse {
5156
+ totalXp: number;
5157
+ todayXp?: number;
5158
+ courses?: CourseXp[];
5081
5159
  }
5082
5160
 
5083
5161
  /**
@@ -5716,4 +5794,4 @@ interface PlaycademyServerClientState {
5716
5794
  }
5717
5795
 
5718
5796
  export { AchievementCompletionType, NotificationStatus, NotificationType, PlaycademyClient };
5719
- export type { AchievementCurrent, AchievementHistoryEntry, AchievementProgressResponse, AchievementScopeType, AchievementWithStatus, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, AuthenticatedUser, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, CharacterComponentRow as CharacterComponent, CharacterComponentType, CharacterComponentWithSpriteUrl, CharacterComponentsOptions, ClientConfig, ClientEvents, ConnectionStatePayload, CreateCharacterData, CreateMapObjectData, CurrencyRow as Currency, DevUploadEvent, DevUploadHooks, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, DisconnectContext, DisconnectHandler, DisplayAlertPayload, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameLeaderboardEntry, MapRow as GameMap, GamePlatform, GameRow as GameRecord, GameSessionRow as GameSession, GameTimebackIntegration, GameTokenResponse, GameType, GameUser, HostedGame, InitPayload, InsertCurrencyInput, InsertItemInput, InsertShopListingInput, InteractionType, InventoryItemRow as InventoryItem, InventoryItemWithItem, InventoryMutationResponse, ItemRow as Item, ItemType, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LevelConfigRow as LevelConfig, LevelProgressResponse, LevelUpCheckResult, LoginResponse, ManifestV1, MapData, MapElementRow as MapElement, MapElementMetadata, MapElementWithGame, MapObjectRow as MapObject, MapObjectWithItem, NotificationRow as Notification, NotificationStats, PlaceableItemMetadata, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyServerClientConfig, PlaycademyServerClientState, PlayerCharacterRow as PlayerCharacter, PlayerCharacterAccessoryRow as PlayerCharacterAccessory, PlayerCurrency, PlayerInventoryItem, PlayerProfile, PlayerSessionPayload, PopulateStudentResponse, RealtimeTokenResponse, ScoreSubmission, ShopCurrency, ShopDisplayItem, ShopListingRow as ShopListing, ShopViewResponse, SpriteAnimationFrame, SpriteConfigWithDimensions, SpriteTemplateRow as SpriteTemplate, SpriteTemplateData, StartSessionResponse, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TodayXpResponse, TokenRefreshPayload, TokenType, TotalXpResponse, UpdateCharacterData, UpdateCurrencyInput, UpdateItemInput, UpdateShopListingInput, UpsertGameMetadataInput, UserRow as User, UserEnrollment, UserInfo, UserLevelRow as UserLevel, UserLevelWithConfig, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData, XPAddResult, XpHistoryResponse, XpSummaryResponse };
5797
+ export type { AchievementCurrent, AchievementHistoryEntry, AchievementProgressResponse, AchievementScopeType, AchievementWithStatus, AuthCallbackPayload, AuthOptions, AuthProviderType, AuthResult, AuthServerMessage, AuthStateChangePayload, AuthStateUpdate, AuthenticatedUser, BetterAuthApiKey, BetterAuthApiKeyResponse, BetterAuthSignInResponse, BucketFile, CharacterComponentRow as CharacterComponent, CharacterComponentType, CharacterComponentWithSpriteUrl, CharacterComponentsOptions, ClientConfig, ClientEvents, ConnectionStatePayload, CourseXp, CreateCharacterData, CreateMapObjectData, CurrencyRow as Currency, DevUploadEvent, DevUploadHooks, DeveloperStatusEnumType, DeveloperStatusResponse, DeveloperStatusValue, DisconnectContext, DisconnectHandler, DisplayAlertPayload, EventListeners, ExternalGame, FetchedGame, Game, GameContextPayload, GameCustomHostname, GameInitUser, GameLeaderboardEntry, MapRow as GameMap, GamePlatform, GameRow as GameRecord, GameSessionRow as GameSession, GameTimebackIntegration, GameTokenResponse, GameType, GameUser, GetXpOptions, HostedGame, InitPayload, InsertCurrencyInput, InsertItemInput, InsertShopListingInput, InteractionType, InventoryItemRow as InventoryItem, InventoryItemWithItem, InventoryMutationResponse, ItemRow as Item, ItemType, KVKeyEntry, KVKeyMetadata, KVSeedEntry, KVStatsResponse, KeyEventPayload, LeaderboardEntry, LeaderboardOptions, LeaderboardTimeframe, LevelConfigRow as LevelConfig, LevelProgressResponse, LevelUpCheckResult, LoginResponse, ManifestV1, MapData, MapElementRow as MapElement, MapElementMetadata, MapElementWithGame, MapObjectRow as MapObject, MapObjectWithItem, NotificationRow as Notification, NotificationStats, PlaceableItemMetadata, PlatformTimebackUser, PlatformTimebackUserContext, PlaycademyServerClientConfig, PlaycademyServerClientState, PlayerCharacterRow as PlayerCharacter, PlayerCharacterAccessoryRow as PlayerCharacterAccessory, PlayerCurrency, PlayerInventoryItem, PlayerProfile, PlayerSessionPayload, PopulateStudentResponse, RealtimeTokenResponse, ScoreSubmission, ShopCurrency, ShopDisplayItem, ShopListingRow as ShopListing, ShopViewResponse, SpriteAnimationFrame, SpriteConfigWithDimensions, SpriteTemplateRow as SpriteTemplate, SpriteTemplateData, StartSessionResponse, TelemetryPayload, TimebackEnrollment, TimebackInitContext, TimebackOrganization, TimebackUser, TimebackUserContext, TimebackUserXp, TodayXpResponse, TokenRefreshPayload, TokenType, TotalXpResponse, UpdateCharacterData, UpdateCurrencyInput, UpdateItemInput, UpdateShopListingInput, UpsertGameMetadataInput, UserRow as User, UserEnrollment, UserInfo, UserLevelRow as UserLevel, UserLevelWithConfig, UserOrganization, UserRank, UserRankResponse, UserRoleEnumType, UserScore, UserTimebackData, XPAddResult, XpHistoryResponse, XpResponse, XpSummaryResponse };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sdk",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -44,12 +44,12 @@
44
44
  "@playcademy/data": "0.0.1",
45
45
  "@playcademy/logger": "0.0.1",
46
46
  "@playcademy/types": "0.0.1",
47
- "@playcademy/sandbox": "0.3.11",
47
+ "@playcademy/sandbox": "0.3.13",
48
48
  "@playcademy/test": "0.0.1",
49
49
  "@playcademy/timeback": "0.0.1",
50
50
  "@playcademy/utils": "0.0.1",
51
51
  "@types/bun": "latest",
52
- "playcademy": "0.16.3",
52
+ "playcademy": "0.16.5",
53
53
  "rollup": "^4.50.2",
54
54
  "rollup-plugin-dts": "^6.2.3",
55
55
  "typescript": "^5.7.2"