playcademy 0.22.2-beta.1 → 0.23.0-beta.1

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/cli.js CHANGED
@@ -1065,7 +1065,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
1065
1065
  // ../better-auth/package.json
1066
1066
  var package_default = {
1067
1067
  name: "@playcademy/better-auth",
1068
- version: "0.0.15-beta.1",
1068
+ version: "0.0.15-beta.3",
1069
1069
  type: "module",
1070
1070
  exports: {
1071
1071
  "./server": {
@@ -1262,6 +1262,17 @@ var TIMEBACK_COMPONENT_RESOURCE_DEFAULTS = {
1262
1262
  sortOrder: 1,
1263
1263
  lessonType: "quiz"
1264
1264
  };
1265
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
1266
+ xp: 1,
1267
+ mastery: 0,
1268
+ score: 2
1269
+ };
1270
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
1271
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
1272
+ mastery: 0,
1273
+ time: 60,
1274
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
1275
+ };
1265
1276
 
1266
1277
  // ../constants/src/cloudflare.ts
1267
1278
  var WORKER_NAMING = {
@@ -2909,7 +2920,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as re
2909
2920
  import { join as join13 } from "node:path";
2910
2921
 
2911
2922
  // src/version.ts
2912
- var cliVersion = false ? "0.0.0-dev" : "0.22.2-beta.1";
2923
+ var cliVersion = false ? "0.0.0-dev" : "0.23.0-beta.1";
2913
2924
 
2914
2925
  // src/lib/init/database.ts
2915
2926
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
package/dist/constants.js CHANGED
@@ -20,7 +20,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
20
20
  // ../better-auth/package.json
21
21
  var package_default = {
22
22
  name: "@playcademy/better-auth",
23
- version: "0.0.15-beta.1",
23
+ version: "0.0.15-beta.3",
24
24
  type: "module",
25
25
  exports: {
26
26
  "./server": {
@@ -247,6 +247,19 @@ var GAME_WORKER_DOMAINS = {
247
247
  staging: "staging.playcademy.gg"
248
248
  };
249
249
 
250
+ // ../constants/src/timeback.ts
251
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
252
+ xp: 1,
253
+ mastery: 0,
254
+ score: 2
255
+ };
256
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
257
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
258
+ mastery: 0,
259
+ time: 60,
260
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
261
+ };
262
+
250
263
  // src/constants/urls.ts
251
264
  var DOCS_URL = "https://docs.dev.playcademy.net/platform";
252
265
  export {
package/dist/db.js CHANGED
@@ -36,7 +36,7 @@ var DEFAULT_API_ROUTES_DIRECTORY = join2(SERVER_ROOT_DIRECTORY, "api");
36
36
  // ../better-auth/package.json
37
37
  var package_default = {
38
38
  name: "@playcademy/better-auth",
39
- version: "0.0.15-beta.1",
39
+ version: "0.0.15-beta.3",
40
40
  type: "module",
41
41
  exports: {
42
42
  "./server": {
@@ -147,6 +147,19 @@ var CLI_FILES = {
147
147
  INITIAL_DATABASE: "initial.sqlite"
148
148
  };
149
149
 
150
+ // ../constants/src/timeback.ts
151
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
152
+ xp: 1,
153
+ mastery: 0,
154
+ score: 2
155
+ };
156
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
157
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
158
+ mastery: 0,
159
+ time: 60,
160
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
161
+ };
162
+
150
163
  // src/lib/db/path.ts
151
164
  var DB_DIRECTORY = CLI_DIRECTORIES.DATABASE;
152
165
  var INITIAL_DB_NAME = CLI_FILES.INITIAL_DATABASE;
package/dist/index.js CHANGED
@@ -326,7 +326,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
326
326
  // ../better-auth/package.json
327
327
  var package_default = {
328
328
  name: "@playcademy/better-auth",
329
- version: "0.0.15-beta.1",
329
+ version: "0.0.15-beta.3",
330
330
  type: "module",
331
331
  exports: {
332
332
  "./server": {
@@ -577,6 +577,17 @@ var TIMEBACK_COMPONENT_RESOURCE_DEFAULTS = {
577
577
  sortOrder: 1,
578
578
  lessonType: "quiz"
579
579
  };
580
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
581
+ xp: 1,
582
+ mastery: 0,
583
+ score: 2
584
+ };
585
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
586
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
587
+ mastery: 0,
588
+ time: 60,
589
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
590
+ };
580
591
 
581
592
  // ../constants/src/cloudflare.ts
582
593
  var WORKER_NAMING = {
@@ -4172,7 +4183,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as rea
4172
4183
  import { join as join13 } from "node:path";
4173
4184
 
4174
4185
  // src/version.ts
4175
- var cliVersion = false ? "0.0.0-dev" : "0.22.2-beta.1";
4186
+ var cliVersion = false ? "0.0.0-dev" : "0.23.0-beta.1";
4176
4187
 
4177
4188
  // src/lib/init/database.ts
4178
4189
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -8,6 +8,75 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // ../utils/src/timeback.ts
12
+ function formatGradeLabel(grade) {
13
+ switch (grade) {
14
+ case -1: {
15
+ return "Pre-K";
16
+ }
17
+ case 0: {
18
+ return "Kindergarten";
19
+ }
20
+ case 13: {
21
+ return "AP";
22
+ }
23
+ default: {
24
+ return `Grade ${grade}`;
25
+ }
26
+ }
27
+ }
28
+ var init_timeback = __esm({
29
+ "../utils/src/timeback.ts"() {
30
+ }
31
+ });
32
+
33
+ // ../edge-play/src/lib/validation.ts
34
+ function isNonNegativeNumber(value) {
35
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
36
+ }
37
+ function isNonNegativeInteger(value) {
38
+ return isNonNegativeNumber(value) && Number.isInteger(value);
39
+ }
40
+ function isValidGrade(value) {
41
+ return typeof value === "number" && Number.isInteger(value) && VALID_GRADES.includes(value);
42
+ }
43
+ function isValidSubject(value) {
44
+ return typeof value === "string" && VALID_SUBJECTS.includes(value);
45
+ }
46
+ function validateCourseConfig(params) {
47
+ const { grade, subject, config } = params;
48
+ const timebackConfig = config.integrations?.timeback;
49
+ const configuredCourse = timebackConfig?.courses?.find(
50
+ (course) => course.grade === grade && course.subject === subject
51
+ );
52
+ if (!configuredCourse) {
53
+ const configured = timebackConfig?.courses?.map((c) => `${c.subject} (${formatGradeLabel(c.grade)})`).join(", ");
54
+ return {
55
+ error: `Invalid grade/subject combination: ${subject} (${formatGradeLabel(grade)}). Configured courses: ${configured || "none"}`
56
+ };
57
+ }
58
+ return null;
59
+ }
60
+ var VALID_GRADES, VALID_SUBJECTS;
61
+ var init_validation = __esm({
62
+ "../edge-play/src/lib/validation.ts"() {
63
+ "use strict";
64
+ init_timeback();
65
+ VALID_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
66
+ VALID_SUBJECTS = [
67
+ "Reading",
68
+ "Language",
69
+ "Vocabulary",
70
+ "Social Studies",
71
+ "Writing",
72
+ "Science",
73
+ "FastMath",
74
+ "Math",
75
+ "None"
76
+ ];
77
+ }
78
+ });
79
+
11
80
  // ../constants/src/auth.ts
12
81
  var init_auth = __esm({
13
82
  "../constants/src/auth.ts"() {
@@ -67,8 +136,8 @@ var init_system = __esm({
67
136
  });
68
137
 
69
138
  // ../constants/src/timeback.ts
70
- var TIMEBACK_ROUTES;
71
- var init_timeback = __esm({
139
+ var TIMEBACK_ROUTES, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
140
+ var init_timeback2 = __esm({
72
141
  "../constants/src/timeback.ts"() {
73
142
  "use strict";
74
143
  TIMEBACK_ROUTES = {
@@ -78,6 +147,17 @@ var init_timeback = __esm({
78
147
  HEARTBEAT: "/integrations/timeback/heartbeat",
79
148
  ADVANCE_COURSE: "/integrations/timeback/advance-course"
80
149
  };
150
+ TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
151
+ xp: 1,
152
+ mastery: 0,
153
+ score: 2
154
+ };
155
+ TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
156
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
157
+ mastery: 0,
158
+ time: 60,
159
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
160
+ };
81
161
  }
82
162
  });
83
163
 
@@ -101,7 +181,7 @@ var init_src = __esm({
101
181
  init_game();
102
182
  init_platform();
103
183
  init_system();
104
- init_timeback();
184
+ init_timeback2();
105
185
  init_cloudflare();
106
186
  }
107
187
  });
@@ -214,9 +294,9 @@ var init_routes = __esm({
214
294
  // ../edge-play/src/entry/metadata.ts
215
295
  function getRuntimeMetadata() {
216
296
  return {
217
- cliVersion: true ? "0.22.2-beta.1" : "0.0.0-dev",
218
- sdkVersion: true ? "0.10.1-beta.1" : "0.0.0-dev",
219
- buildId: true ? "feb12ff2fcf5" : "dev-source"
297
+ cliVersion: true ? "0.23.0-beta.1" : "0.0.0-dev",
298
+ sdkVersion: true ? "0.11.0-beta.1" : "0.0.0-dev",
299
+ buildId: true ? "c5d963f65838" : "dev-source"
220
300
  };
221
301
  }
222
302
  var init_metadata = __esm({
@@ -366,69 +446,6 @@ var init_errors = __esm({
366
446
  }
367
447
  });
368
448
 
369
- // ../utils/src/timeback.ts
370
- function formatGradeLabel(grade) {
371
- switch (grade) {
372
- case -1: {
373
- return "Pre-K";
374
- }
375
- case 0: {
376
- return "Kindergarten";
377
- }
378
- case 13: {
379
- return "AP";
380
- }
381
- default: {
382
- return `Grade ${grade}`;
383
- }
384
- }
385
- }
386
- var init_timeback2 = __esm({
387
- "../utils/src/timeback.ts"() {
388
- }
389
- });
390
-
391
- // ../edge-play/src/lib/validation.ts
392
- function isValidGrade2(value) {
393
- return typeof value === "number" && Number.isInteger(value) && VALID_GRADES2.includes(value);
394
- }
395
- function isValidSubject2(value) {
396
- return typeof value === "string" && VALID_SUBJECTS2.includes(value);
397
- }
398
- function validateCourseConfig(params) {
399
- const { grade, subject, config } = params;
400
- const timebackConfig = config.integrations?.timeback;
401
- const configuredCourse = timebackConfig?.courses?.find(
402
- (course) => course.grade === grade && course.subject === subject
403
- );
404
- if (!configuredCourse) {
405
- const configured = timebackConfig?.courses?.map((c) => `${c.subject} (${formatGradeLabel(c.grade)})`).join(", ");
406
- return {
407
- error: `Invalid grade/subject combination: ${subject} (${formatGradeLabel(grade)}). Configured courses: ${configured || "none"}`
408
- };
409
- }
410
- return null;
411
- }
412
- var VALID_GRADES2, VALID_SUBJECTS2;
413
- var init_validation = __esm({
414
- "../edge-play/src/lib/validation.ts"() {
415
- "use strict";
416
- init_timeback2();
417
- VALID_GRADES2 = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
418
- VALID_SUBJECTS2 = [
419
- "Reading",
420
- "Language",
421
- "Vocabulary",
422
- "Social Studies",
423
- "Writing",
424
- "Science",
425
- "FastMath",
426
- "Math",
427
- "None"
428
- ];
429
- }
430
- });
431
-
432
449
  // ../edge-play/src/lib/index.ts
433
450
  var init_lib = __esm({
434
451
  "../edge-play/src/lib/index.ts"() {
@@ -503,7 +520,7 @@ async function POST(c) {
503
520
  assertTimebackIntegrated(c);
504
521
  const config = getConfig(c);
505
522
  const structuredActivityData = isActivityDataInput(activityData) ? activityData : void 0;
506
- if (structuredActivityData && isValidGrade2(structuredActivityData.grade) && isValidSubject2(structuredActivityData.subject)) {
523
+ if (structuredActivityData && isValidGrade(structuredActivityData.grade) && isValidSubject(structuredActivityData.subject)) {
507
524
  const courseValidationError = validateCourseConfig({
508
525
  grade: structuredActivityData.grade,
509
526
  subject: structuredActivityData.subject,
@@ -585,12 +602,12 @@ async function GET3(c) {
585
602
  }
586
603
  grade = parseInt(gradeParam, 10);
587
604
  subject = subjectParam;
588
- if (!isValidGrade2(grade)) {
589
- const message2 = `grade must be a valid grade level (${VALID_GRADES2.join(", ")})`;
605
+ if (!isValidGrade(grade)) {
606
+ const message2 = `grade must be a valid grade level (${VALID_GRADES.join(", ")})`;
590
607
  return c.json(buildErrorResponse(c, message2, message2), 400);
591
608
  }
592
- if (!isValidSubject2(subject)) {
593
- const message2 = `subject must be a valid subject (${VALID_SUBJECTS2.join(", ")})`;
609
+ if (!isValidSubject(subject)) {
610
+ const message2 = `subject must be a valid subject (${VALID_SUBJECTS.join(", ")})`;
594
611
  return c.json(buildErrorResponse(c, message2, message2), 400);
595
612
  }
596
613
  const courseValidationError = validateCourseConfig({ grade, subject, config });
@@ -653,12 +670,12 @@ async function GET4(c) {
653
670
  }
654
671
  grade = parseInt(gradeParam, 10);
655
672
  subject = subjectParam;
656
- if (!isValidGrade2(grade)) {
657
- const message2 = `grade must be a valid grade level (${VALID_GRADES2.join(", ")})`;
673
+ if (!isValidGrade(grade)) {
674
+ const message2 = `grade must be a valid grade level (${VALID_GRADES.join(", ")})`;
658
675
  return c.json(buildErrorResponse(c, message2, message2), 400);
659
676
  }
660
- if (!isValidSubject2(subject)) {
661
- const message2 = `subject must be a valid subject (${VALID_SUBJECTS2.join(", ")})`;
677
+ if (!isValidSubject(subject)) {
678
+ const message2 = `subject must be a valid subject (${VALID_SUBJECTS.join(", ")})`;
662
679
  return c.json(buildErrorResponse(c, message2, message2), 400);
663
680
  }
664
681
  const courseValidationError = validateCourseConfig({ grade, subject, config });
@@ -724,7 +741,7 @@ async function POST2(c) {
724
741
  assertTimebackIntegrated(c);
725
742
  const config = getConfig(c);
726
743
  const structuredActivityData = isActivityDataInput(activityData) ? activityData : void 0;
727
- if (structuredActivityData && isValidGrade2(structuredActivityData.grade) && isValidSubject2(structuredActivityData.subject)) {
744
+ if (structuredActivityData && isValidGrade(structuredActivityData.grade) && isValidSubject(structuredActivityData.subject)) {
728
745
  const courseValidationError = validateCourseConfig({
729
746
  grade: structuredActivityData.grade,
730
747
  subject: structuredActivityData.subject,
@@ -788,7 +805,7 @@ async function POST3(c) {
788
805
  }
789
806
  const body = await c.req.json().catch(() => ({}));
790
807
  const subject = body.subject;
791
- if (subject !== void 0 && !isValidSubject2(subject)) {
808
+ if (subject !== void 0 && !isValidSubject(subject)) {
792
809
  const message2 = "Invalid subject: must be one of the supported TimeBack subjects";
793
810
  console.error("[TimeBack Advance] Error:", message2);
794
811
  return c.json(buildErrorResponse(c, message2, message2), 400);
@@ -3556,22 +3573,10 @@ function createLocalJWKSet(jwks) {
3556
3573
  return localJWKSet;
3557
3574
  }
3558
3575
 
3559
- // ../edge-play/src/entry/metrics.ts
3576
+ // ../edge-play/src/lib/jwt.ts
3560
3577
  var SERVICE_JWT_ISSUER = "playcademy-platform";
3561
3578
  var SERVICE_JWT_AUDIENCE = "playcademy-game-metrics";
3562
3579
  var SERVICE_JWT_CLOCK_TOLERANCE = "10s";
3563
- var TIMEBACK_GRADES = /* @__PURE__ */ new Set([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
3564
- var TIMEBACK_SUBJECTS = /* @__PURE__ */ new Set([
3565
- "Reading",
3566
- "Language",
3567
- "Vocabulary",
3568
- "Social Studies",
3569
- "Writing",
3570
- "Science",
3571
- "FastMath",
3572
- "Math",
3573
- "None"
3574
- ]);
3575
3580
  function getBearerToken(c) {
3576
3581
  const header = c.req.header("Authorization");
3577
3582
  if (!header?.startsWith("Bearer ")) {
@@ -3666,22 +3671,24 @@ async function verifyMetricsToken(c) {
3666
3671
  return null;
3667
3672
  }
3668
3673
  }
3669
- function isNonNegativeNumber(value) {
3670
- return typeof value === "number" && Number.isFinite(value) && value >= 0;
3671
- }
3672
- function isNonNegativeInteger(value) {
3673
- return isNonNegativeNumber(value) && Number.isInteger(value);
3674
- }
3675
- function isIsoDateString(value) {
3676
- return typeof value === "string" && !Number.isNaN(Date.parse(value));
3674
+
3675
+ // ../utils/src/uuid.ts
3676
+ var UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
3677
+ function isValidUUID(value) {
3678
+ if (!value || typeof value !== "string") {
3679
+ return false;
3680
+ }
3681
+ return UUID_REGEX.test(value);
3677
3682
  }
3678
- function isValidActivity(value) {
3683
+
3684
+ // ../edge-play/src/lib/metrics.ts
3685
+ init_validation();
3686
+ function isValidRunMetrics(value) {
3679
3687
  if (!value || typeof value !== "object") {
3680
3688
  return false;
3681
3689
  }
3682
3690
  const activity = value;
3683
- const completionCount = activity.completionCount;
3684
- return typeof activity.activityId === "string" && activity.activityId.length > 0 && (activity.activityName === void 0 || typeof activity.activityName === "string") && isNonNegativeNumber(activity.totalXp) && isNonNegativeInteger(activity.masteredUnits) && isNonNegativeNumber(activity.activeTimeSeconds) && Number.isInteger(completionCount) && typeof completionCount === "number" && completionCount >= 0 && (activity.lastCompletedAt === void 0 || isIsoDateString(activity.lastCompletedAt));
3691
+ return typeof activity.runId === "string" && isValidUUID(activity.runId) && typeof activity.activityId === "string" && activity.activityId.length > 0 && (activity.activityName === void 0 || typeof activity.activityName === "string") && (activity.totalXp === void 0 || isNonNegativeNumber(activity.totalXp)) && (activity.masteredUnits === void 0 || isNonNegativeInteger(activity.masteredUnits)) && (activity.activeTimeSeconds === void 0 || isNonNegativeNumber(activity.activeTimeSeconds)) && (activity.score === void 0 || isNonNegativeNumber(activity.score) && activity.score <= 100);
3685
3692
  }
3686
3693
  function isValidGameMetricsResponse(value) {
3687
3694
  if (!value || typeof value !== "object") {
@@ -3698,9 +3705,15 @@ function isValidGameMetricsResponse(value) {
3698
3705
  }
3699
3706
  const metrics = course;
3700
3707
  const activities = metrics.activities;
3701
- return TIMEBACK_GRADES.has(metrics.grade) && TIMEBACK_SUBJECTS.has(metrics.subject) && isNonNegativeNumber(metrics.totalXp) && isNonNegativeInteger(metrics.masteredUnits) && isNonNegativeNumber(metrics.activeTimeSeconds) && (activities === void 0 || Array.isArray(activities) && activities.every(isValidActivity));
3708
+ return isValidGrade(metrics.grade) && isValidSubject(metrics.subject) && (metrics.totalXp === void 0 || isNonNegativeNumber(metrics.totalXp)) && (metrics.masteredUnits === void 0 || isNonNegativeInteger(metrics.masteredUnits)) && (metrics.activeTimeSeconds === void 0 || isNonNegativeNumber(metrics.activeTimeSeconds)) && (activities === void 0 || Array.isArray(activities) && activities.every(isValidRunMetrics));
3702
3709
  });
3703
3710
  }
3711
+ function getRequestedRunIds(request) {
3712
+ const runIds = new URL(request.url).searchParams.getAll("runId").filter((runId) => isValidUUID(runId));
3713
+ return [...new Set(runIds)];
3714
+ }
3715
+
3716
+ // ../edge-play/src/entry/metrics.ts
3704
3717
  function registerMetricsRoute(app, metricsModule) {
3705
3718
  app.get("/__playcademy/metrics", async (c) => {
3706
3719
  const claims = await verifyMetricsToken(c);
@@ -3717,6 +3730,7 @@ function registerMetricsRoute(app, metricsModule) {
3717
3730
  try {
3718
3731
  metrics = await metricsModule.getMetrics({
3719
3732
  studentId: claims.studentId,
3733
+ runIds: getRequestedRunIds(c.req.raw),
3720
3734
  env: c.env,
3721
3735
  sdk: c.get("sdk"),
3722
3736
  request: c.req.raw,
@@ -3818,8 +3832,8 @@ var cors = (options) => {
3818
3832
  };
3819
3833
 
3820
3834
  // ../sdk/src/core/guards.ts
3821
- var VALID_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
3822
- var VALID_SUBJECTS = [
3835
+ var VALID_GRADES2 = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
3836
+ var VALID_SUBJECTS2 = [
3823
3837
  "Reading",
3824
3838
  "Language",
3825
3839
  "Vocabulary",
@@ -3830,11 +3844,11 @@ var VALID_SUBJECTS = [
3830
3844
  "Math",
3831
3845
  "None"
3832
3846
  ];
3833
- function isValidGrade(value) {
3834
- return typeof value === "number" && Number.isInteger(value) && VALID_GRADES.includes(value);
3847
+ function isValidGrade2(value) {
3848
+ return typeof value === "number" && Number.isInteger(value) && VALID_GRADES2.includes(value);
3835
3849
  }
3836
- function isValidSubject(value) {
3837
- return typeof value === "string" && VALID_SUBJECTS.includes(value);
3850
+ function isValidSubject2(value) {
3851
+ return typeof value === "string" && VALID_SUBJECTS2.includes(value);
3838
3852
  }
3839
3853
 
3840
3854
  // ../sdk/src/server/namespaces/timeback.ts
@@ -3892,10 +3906,10 @@ function createTimebackNamespace(client) {
3892
3906
  * ```
3893
3907
  */
3894
3908
  endActivity: async (studentId, payload) => {
3895
- if (!isValidGrade(payload.activityData.grade)) {
3909
+ if (!isValidGrade2(payload.activityData.grade)) {
3896
3910
  throw new Error("activityData.grade must be a valid grade level (-1 to 13)");
3897
3911
  }
3898
- if (!isValidSubject(payload.activityData.subject)) {
3912
+ if (!isValidSubject2(payload.activityData.subject)) {
3899
3913
  throw new Error("activityData.subject must be a valid subject");
3900
3914
  }
3901
3915
  const enrichedActivityData = enrichActivityData2(payload.activityData);
@@ -3951,14 +3965,14 @@ function createTimebackNamespace(client) {
3951
3965
  if (hasGrade !== hasSubject) {
3952
3966
  throw new Error("Both grade and subject must be provided together");
3953
3967
  }
3954
- if (hasGrade && !isValidGrade(options.grade)) {
3968
+ if (hasGrade && !isValidGrade2(options.grade)) {
3955
3969
  throw new Error(
3956
- `Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`
3970
+ `Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES2.join(", ")}`
3957
3971
  );
3958
3972
  }
3959
- if (hasSubject && !isValidSubject(options.subject)) {
3973
+ if (hasSubject && !isValidSubject2(options.subject)) {
3960
3974
  throw new Error(
3961
- `Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`
3975
+ `Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS2.join(", ")}`
3962
3976
  );
3963
3977
  }
3964
3978
  const params = new URLSearchParams();
@@ -3982,14 +3996,14 @@ function createTimebackNamespace(client) {
3982
3996
  if (hasGrade !== hasSubject) {
3983
3997
  throw new Error("Both grade and subject must be provided together");
3984
3998
  }
3985
- if (hasGrade && !isValidGrade(options.grade)) {
3999
+ if (hasGrade && !isValidGrade2(options.grade)) {
3986
4000
  throw new Error(
3987
- `Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES.join(", ")}`
4001
+ `Invalid grade: ${options.grade}. Valid grades: ${VALID_GRADES2.join(", ")}`
3988
4002
  );
3989
4003
  }
3990
- if (hasSubject && !isValidSubject(options.subject)) {
4004
+ if (hasSubject && !isValidSubject2(options.subject)) {
3991
4005
  throw new Error(
3992
- `Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS.join(", ")}`
4006
+ `Invalid subject: ${options.subject}. Valid subjects: ${VALID_SUBJECTS2.join(", ")}`
3993
4007
  );
3994
4008
  }
3995
4009
  const params = new URLSearchParams();
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.22.2-beta.1",
3
- "sdkVersion": "0.10.1-beta.1",
4
- "runtimeBuildId": "feb12ff2fcf5",
5
- "inputFingerprint": "feb12ff2fcf5a733422381410fd83b75e2931f921befb3e53bc1cc73de20eb6e",
2
+ "cliVersion": "0.23.0-beta.1",
3
+ "sdkVersion": "0.11.0-beta.1",
4
+ "runtimeBuildId": "c5d963f65838",
5
+ "inputFingerprint": "c5d963f658382315a4b41a182bd357b42a61e01c604fab221ac78f8137be514c",
6
6
  "entry": "index.js"
7
7
  }
@@ -41,6 +41,7 @@ declare global {
41
41
 
42
42
  interface MetricsResolverContext {
43
43
  studentId: string
44
+ runIds: string[]
44
45
  env: PlaycademyEnv
45
46
  }
46
47
  }
package/dist/utils.js CHANGED
@@ -309,6 +309,17 @@ var TIMEBACK_ROUTES = {
309
309
  HEARTBEAT: "/integrations/timeback/heartbeat",
310
310
  ADVANCE_COURSE: "/integrations/timeback/advance-course"
311
311
  };
312
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
313
+ xp: 1,
314
+ mastery: 0,
315
+ score: 2
316
+ };
317
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
318
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
319
+ mastery: 0,
320
+ time: 60,
321
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
322
+ };
312
323
 
313
324
  // ../constants/src/cloudflare.ts
314
325
  var WORKER_NAMING = {
@@ -346,7 +357,7 @@ var DEFAULT_API_ROUTES_DIRECTORY = join2(SERVER_ROOT_DIRECTORY, "api");
346
357
  // ../better-auth/package.json
347
358
  var package_default = {
348
359
  name: "@playcademy/better-auth",
349
- version: "0.0.15-beta.1",
360
+ version: "0.0.15-beta.3",
350
361
  type: "module",
351
362
  exports: {
352
363
  "./server": {
@@ -2457,7 +2468,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as wr
2457
2468
  import { dirname as dirname4, join as join14 } from "node:path";
2458
2469
 
2459
2470
  // src/version.ts
2460
- var cliVersion = false ? "0.0.0-dev" : "0.22.2-beta.1";
2471
+ var cliVersion = false ? "0.0.0-dev" : "0.23.0-beta.1";
2461
2472
 
2462
2473
  // src/lib/build/binary-resource.ts
2463
2474
  function writeFileTree(baseDir, files) {
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var cliVersion = false ? "0.0.0-dev" : "0.22.2-beta.1";
2
+ var cliVersion = false ? "0.0.0-dev" : "0.23.0-beta.1";
3
3
  export {
4
4
  cliVersion
5
5
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playcademy",
3
- "version": "0.22.2-beta.1",
3
+ "version": "0.23.0-beta.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@inquirer/prompts": "^7.8.6",
53
- "@playcademy/sdk": "0.10.0",
53
+ "@playcademy/sdk": "0.11.0",
54
54
  "chokidar": "^4.0.3",
55
55
  "colorette": "^2.0.20",
56
56
  "commander": "^14.0.1",