@playcademy/vite-plugin 0.2.26-beta.3 → 0.2.26-beta.5

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.js CHANGED
@@ -24333,10 +24333,10 @@ var init_typescript = __esm(() => {
24333
24333
  TypeScriptPackages = {
24334
24334
  tsc: "tsc",
24335
24335
  nativePreview: "@typescript/native-preview",
24336
- nativePreviewPinned: "@typescript/native-preview@7.0.0-dev.20260221.1"
24336
+ nativePreviewBeta: "@typescript/native-preview@beta"
24337
24337
  };
24338
24338
  TYPESCRIPT_RUNNER = {
24339
- package: TypeScriptPackages.nativePreviewPinned,
24339
+ package: TypeScriptPackages.nativePreviewBeta,
24340
24340
  bin: "tsgo"
24341
24341
  };
24342
24342
  });
@@ -24423,7 +24423,8 @@ var init_timeback2 = __esm(() => {
24423
24423
  TIMEBACK_ROUTES = {
24424
24424
  END_ACTIVITY: "/integrations/timeback/end-activity",
24425
24425
  GET_XP: "/integrations/timeback/xp",
24426
- HEARTBEAT: "/integrations/timeback/heartbeat"
24426
+ HEARTBEAT: "/integrations/timeback/heartbeat",
24427
+ ADVANCE_COURSE: "/integrations/timeback/advance-course"
24427
24428
  };
24428
24429
  TIMEBACK_COURSE_DEFAULTS = {
24429
24430
  gradingScheme: "STANDARD",
@@ -25369,7 +25370,7 @@ var package_default;
25369
25370
  var init_package = __esm(() => {
25370
25371
  package_default = {
25371
25372
  name: "@playcademy/sandbox",
25372
- version: "0.3.17-beta.18",
25373
+ version: "0.3.17-beta.20",
25373
25374
  description: "Local development server for Playcademy game development",
25374
25375
  type: "module",
25375
25376
  exports: {
@@ -25706,7 +25707,7 @@ function formatJSONSingleLine(level, message, context, scope) {
25706
25707
  ...context && Object.keys(context).length > 0 && { context }
25707
25708
  };
25708
25709
  const consoleMethod = getConsoleMethod(level);
25709
- consoleMethod(JSON.stringify(logEntry));
25710
+ consoleMethod(safeJSONStringify(logEntry));
25710
25711
  }
25711
25712
  function formatJSONPretty(level, message, context, scope) {
25712
25713
  const timestamp = new Date().toISOString();
@@ -25718,7 +25719,30 @@ function formatJSONPretty(level, message, context, scope) {
25718
25719
  ...context && Object.keys(context).length > 0 && { context }
25719
25720
  };
25720
25721
  const consoleMethod = getConsoleMethod(level);
25721
- consoleMethod(JSON.stringify(logEntry, null, 2));
25722
+ consoleMethod(safeJSONStringify(logEntry, 2));
25723
+ }
25724
+ function safeJSONStringify(value, space) {
25725
+ const seen = new WeakSet;
25726
+ try {
25727
+ return JSON.stringify(value, (_key, currentValue) => {
25728
+ if (typeof currentValue === "bigint") {
25729
+ return currentValue.toString();
25730
+ }
25731
+ if (typeof currentValue === "object" && currentValue !== null) {
25732
+ if (seen.has(currentValue)) {
25733
+ return "[Circular]";
25734
+ }
25735
+ seen.add(currentValue);
25736
+ }
25737
+ return currentValue;
25738
+ }, space);
25739
+ } catch {
25740
+ return JSON.stringify({
25741
+ timestamp: new Date().toISOString(),
25742
+ level: "ERROR",
25743
+ message: "[Logger] Failed to serialize log entry"
25744
+ });
25745
+ }
25722
25746
  }
25723
25747
  function getConsoleMethod(level) {
25724
25748
  switch (level) {
@@ -52738,7 +52762,8 @@ var init_constants3 = __esm(() => {
52738
52762
  TIMEBACK: {
52739
52763
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
52740
52764
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
52741
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`
52765
+ HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
52766
+ ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`
52742
52767
  }
52743
52768
  };
52744
52769
  });
@@ -55131,6 +55156,7 @@ class TimebackAdminService {
55131
55156
  await client.edubridge.enrollments.enroll(data.studentId, data.courseId, {
55132
55157
  role: "student"
55133
55158
  });
55159
+ client.invalidateEnrollments(data.studentId);
55134
55160
  return { status: "ok" };
55135
55161
  }
55136
55162
  async unenrollStudent(data, user) {
@@ -55143,6 +55169,7 @@ class TimebackAdminService {
55143
55169
  throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
55144
55170
  }
55145
55171
  await client.edubridge.enrollments.unenroll(data.studentId, data.courseId);
55172
+ client.invalidateEnrollments(data.studentId);
55146
55173
  return { status: "ok" };
55147
55174
  }
55148
55175
  async getCompletionStatus(client, courseId, studentId) {
@@ -55198,7 +55225,103 @@ var init_timeback_admin_service = __esm(() => {
55198
55225
  init_timeback_util();
55199
55226
  logger16 = log.scope("TimebackAdminService");
55200
55227
  });
55228
+ async function promoteCompletedCourse({
55229
+ db: db2,
55230
+ client,
55231
+ currentIntegration,
55232
+ studentId,
55233
+ enrollments: prefetchedEnrollments
55234
+ }) {
55235
+ const subjectIntegrations = await db2.query.gameTimebackIntegrations.findMany({
55236
+ where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject))
55237
+ });
55238
+ const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
55239
+ if (!nextIntegration) {
55240
+ logger17.debug("Skipping promotion because no next course is configured", {
55241
+ gameId: currentIntegration.gameId,
55242
+ studentId,
55243
+ grade: currentIntegration.grade,
55244
+ subject: currentIntegration.subject,
55245
+ currentCourseId: currentIntegration.courseId
55246
+ });
55247
+ return {
55248
+ status: "no-next-course",
55249
+ currentCourseId: currentIntegration.courseId
55250
+ };
55251
+ }
55252
+ const enrollments = prefetchedEnrollments ?? await client.edubridge.enrollments.listByUser(studentId);
55253
+ const currentEnrollment = enrollments.find((enrollment) => enrollment.course.id === currentIntegration.courseId);
55254
+ const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
55255
+ if (!currentEnrollment) {
55256
+ if (nextEnrollment) {
55257
+ logger17.debug("Skipping promotion because student is already on the next course", {
55258
+ gameId: currentIntegration.gameId,
55259
+ studentId,
55260
+ grade: currentIntegration.grade,
55261
+ subject: currentIntegration.subject,
55262
+ currentCourseId: currentIntegration.courseId,
55263
+ nextCourseId: nextIntegration.courseId
55264
+ });
55265
+ return {
55266
+ status: "already-promoted",
55267
+ currentCourseId: currentIntegration.courseId,
55268
+ nextCourseId: nextIntegration.courseId
55269
+ };
55270
+ }
55271
+ logger17.debug("Skipping promotion because student is not enrolled in the current course", {
55272
+ gameId: currentIntegration.gameId,
55273
+ studentId,
55274
+ grade: currentIntegration.grade,
55275
+ subject: currentIntegration.subject,
55276
+ currentCourseId: currentIntegration.courseId,
55277
+ nextCourseId: nextIntegration.courseId
55278
+ });
55279
+ return {
55280
+ status: "not-enrolled",
55281
+ currentCourseId: currentIntegration.courseId,
55282
+ nextCourseId: nextIntegration.courseId
55283
+ };
55284
+ }
55285
+ const schoolId = currentEnrollment.school.id;
55286
+ let mutatedEnrollments = false;
55287
+ try {
55288
+ if (!nextEnrollment) {
55289
+ await client.edubridge.enrollments.enroll(studentId, nextIntegration.courseId, {
55290
+ role: "student",
55291
+ ...schoolId ? { schoolId } : {}
55292
+ });
55293
+ mutatedEnrollments = true;
55294
+ }
55295
+ await client.edubridge.enrollments.unenroll(studentId, currentIntegration.courseId, schoolId ? { schoolId } : {});
55296
+ mutatedEnrollments = true;
55297
+ } finally {
55298
+ if (mutatedEnrollments) {
55299
+ client.invalidateEnrollments(studentId);
55300
+ }
55301
+ }
55302
+ logger17.info("Promoted student to next course", {
55303
+ gameId: currentIntegration.gameId,
55304
+ studentId,
55305
+ subject: currentIntegration.subject,
55306
+ fromGrade: currentIntegration.grade,
55307
+ toGrade: nextIntegration.grade,
55308
+ currentCourseId: currentIntegration.courseId,
55309
+ nextCourseId: nextIntegration.courseId
55310
+ });
55311
+ return {
55312
+ status: "promoted",
55313
+ currentCourseId: currentIntegration.courseId,
55314
+ nextCourseId: nextIntegration.courseId
55315
+ };
55316
+ }
55201
55317
  var logger17;
55318
+ var init_timeback_promotion_util = __esm(() => {
55319
+ init_drizzle_orm();
55320
+ init_tables_index();
55321
+ init_src2();
55322
+ logger17 = log.scope("TimebackPromotion");
55323
+ });
55324
+ var logger18;
55202
55325
  var TimebackService;
55203
55326
  var init_timeback_service = __esm(() => {
55204
55327
  init_drizzle_orm();
@@ -55208,8 +55331,9 @@ var init_timeback_service = __esm(() => {
55208
55331
  init_types4();
55209
55332
  init_src4();
55210
55333
  init_errors();
55334
+ init_timeback_promotion_util();
55211
55335
  init_timeback_util();
55212
- logger17 = log.scope("TimebackService");
55336
+ logger18 = log.scope("TimebackService");
55213
55337
  TimebackService = class TimebackService2 {
55214
55338
  static HEARTBEAT_DEDUPE_TTL_MS = 300000;
55215
55339
  static processedHeartbeatWindows = new Map;
@@ -55255,7 +55379,7 @@ var init_timeback_service = __esm(() => {
55255
55379
  }
55256
55380
  requireClient() {
55257
55381
  if (!this.deps.timeback) {
55258
- logger17.error("Timeback client not available in context");
55382
+ logger18.error("Timeback client not available in context");
55259
55383
  throw new ValidationError("Timeback integration not available in this environment");
55260
55384
  }
55261
55385
  return this.deps.timeback;
@@ -55308,7 +55432,7 @@ var init_timeback_service = __esm(() => {
55308
55432
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
55309
55433
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
55310
55434
  if (!result) {
55311
- logger17.error("Daily XP upsert returned no rows", { userId, date: targetDate });
55435
+ logger18.error("Daily XP upsert returned no rows", { userId, date: targetDate });
55312
55436
  throw new InternalError("Failed to update daily XP record");
55313
55437
  }
55314
55438
  return { xp: result.xp, date: result.date.toISOString() };
@@ -55339,7 +55463,7 @@ var init_timeback_service = __esm(() => {
55339
55463
  columns: { id: true, timebackId: true }
55340
55464
  });
55341
55465
  if (dbUser?.timebackId) {
55342
- logger17.info("Student already onboarded", { userId: user.id });
55466
+ logger18.info("Student already onboarded", { userId: user.id });
55343
55467
  return { status: "already_populated" };
55344
55468
  }
55345
55469
  let timebackId;
@@ -55348,7 +55472,7 @@ var init_timeback_service = __esm(() => {
55348
55472
  const existingUser = await client.oneroster.users.findByEmail(user.email);
55349
55473
  timebackId = existingUser.sourcedId;
55350
55474
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
55351
- logger17.info("Found existing student in OneRoster", {
55475
+ logger18.info("Found existing student in OneRoster", {
55352
55476
  userId: user.id,
55353
55477
  timebackId
55354
55478
  });
@@ -55377,7 +55501,7 @@ var init_timeback_service = __esm(() => {
55377
55501
  }
55378
55502
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
55379
55503
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
55380
- logger17.info("Created student in OneRoster", { userId: user.id, timebackId });
55504
+ logger18.info("Created student in OneRoster", { userId: user.id, timebackId });
55381
55505
  }
55382
55506
  const assessments = await this.fetchAssessments(timebackId);
55383
55507
  await db2.transaction(async (tx) => {
@@ -55411,7 +55535,7 @@ var init_timeback_service = __esm(() => {
55411
55535
  }
55412
55536
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
55413
55537
  if (!updated) {
55414
- logger17.error("User Timeback ID update returned no rows", {
55538
+ logger18.error("User Timeback ID update returned no rows", {
55415
55539
  userId: user.id,
55416
55540
  timebackId
55417
55541
  });
@@ -55435,13 +55559,13 @@ var init_timeback_service = __esm(() => {
55435
55559
  }
55436
55560
  offset += limit;
55437
55561
  }
55438
- logger17.debug("Fetched assessments", {
55562
+ logger18.debug("Fetched assessments", {
55439
55563
  studentSourcedId,
55440
55564
  totalCount: allAssessments.length
55441
55565
  });
55442
55566
  return allAssessments;
55443
55567
  } catch (error) {
55444
- logger17.warn("Failed to fetch assessments", { studentSourcedId, error });
55568
+ logger18.warn("Failed to fetch assessments", { studentSourcedId, error });
55445
55569
  return [];
55446
55570
  }
55447
55571
  }
@@ -55554,7 +55678,7 @@ var init_timeback_service = __esm(() => {
55554
55678
  masterableUnits: derivedMasterableUnits
55555
55679
  } = courseConfig;
55556
55680
  if (!isTimebackSubject(subjectInput)) {
55557
- logger17.warn("Invalid Timeback subject in course config", {
55681
+ logger18.warn("Invalid Timeback subject in course config", {
55558
55682
  subject: subjectInput,
55559
55683
  courseCode,
55560
55684
  title
@@ -55562,7 +55686,7 @@ var init_timeback_service = __esm(() => {
55562
55686
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
55563
55687
  }
55564
55688
  if (!isTimebackGrade(grade)) {
55565
- logger17.warn("Invalid Timeback grade in course config", {
55689
+ logger18.warn("Invalid Timeback grade in course config", {
55566
55690
  grade,
55567
55691
  courseCode,
55568
55692
  title
@@ -55574,7 +55698,7 @@ var init_timeback_service = __esm(() => {
55574
55698
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
55575
55699
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
55576
55700
  if (typeof totalXp !== "number") {
55577
- logger17.warn("Course missing totalXp in Timeback config", {
55701
+ logger18.warn("Course missing totalXp in Timeback config", {
55578
55702
  courseCode,
55579
55703
  title
55580
55704
  });
@@ -55775,7 +55899,7 @@ var init_timeback_service = __esm(() => {
55775
55899
  ...runId ? { runId } : {}
55776
55900
  });
55777
55901
  }
55778
- logger17.info("Recorded activity completion", {
55902
+ logger18.info("Recorded activity completion", {
55779
55903
  gameId,
55780
55904
  courseId: integration.courseId,
55781
55905
  studentId,
@@ -55792,6 +55916,72 @@ var init_timeback_service = __esm(() => {
55792
55916
  inProgress: result.inProgress
55793
55917
  };
55794
55918
  }
55919
+ async advanceCourse({
55920
+ gameId,
55921
+ studentId,
55922
+ subject,
55923
+ user
55924
+ }) {
55925
+ const client = this.requireClient();
55926
+ const db2 = this.deps.db;
55927
+ await this.deps.validateDeveloperAccess(user, gameId);
55928
+ const integrations = await db2.query.gameTimebackIntegrations.findMany({
55929
+ where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject)) : eq(gameTimebackIntegrations.gameId, gameId)
55930
+ });
55931
+ if (integrations.length === 0) {
55932
+ throw new NotFoundError(subject ? `Timeback integrations for game (subject ${subject})` : "Timeback integrations for game");
55933
+ }
55934
+ const enrollments = await client.edubridge.enrollments.listByUser(studentId);
55935
+ const enrolledCourseIds = new Set(enrollments.map((e) => e.course.id));
55936
+ const enrolledIntegrations = integrations.filter((integration) => enrolledCourseIds.has(integration.courseId));
55937
+ if (enrolledIntegrations.length === 0) {
55938
+ throw new NotFoundError(subject ? `Active enrollment for game ladder (subject ${subject})` : "Active enrollment for game ladder");
55939
+ }
55940
+ const subjectsInPlay = new Set(enrolledIntegrations.map((i2) => i2.subject));
55941
+ if (subjectsInPlay.size > 1) {
55942
+ throw new ValidationError(`Ambiguous Timeback advance: student is enrolled in ${subjectsInPlay.size} parallel ladders (${[...subjectsInPlay].join(", ")}); pass { subject } to disambiguate`);
55943
+ }
55944
+ const currentIntegration = enrolledIntegrations.toSorted((left, right) => left.grade - right.grade)[0];
55945
+ const masteryStatus = await client.getMasteryStatus(currentIntegration.courseId, studentId);
55946
+ if (!masteryStatus) {
55947
+ throw new ValidationError(`Cannot advance course: mastery status is unavailable for course ${currentIntegration.courseId}. Ensure the course has mastery configuration and the student has enrollment analytics before calling client.timeback.advanceCourse().`);
55948
+ }
55949
+ if (!masteryStatus.isComplete) {
55950
+ const promotion2 = {
55951
+ status: "not-mastered",
55952
+ currentCourseId: currentIntegration.courseId,
55953
+ masteredUnits: masteryStatus.masteredUnits,
55954
+ masterableUnits: masteryStatus.masterableUnits
55955
+ };
55956
+ logger18.debug("Skipping course advancement because mastery is incomplete", {
55957
+ gameId,
55958
+ studentId,
55959
+ subject: currentIntegration.subject,
55960
+ grade: currentIntegration.grade,
55961
+ currentCourseId: currentIntegration.courseId,
55962
+ masteredUnits: masteryStatus.masteredUnits,
55963
+ masterableUnits: masteryStatus.masterableUnits
55964
+ });
55965
+ return { status: "ok", promotion: promotion2 };
55966
+ }
55967
+ const promotion = await promoteCompletedCourse({
55968
+ db: db2,
55969
+ client,
55970
+ currentIntegration,
55971
+ studentId,
55972
+ enrollments
55973
+ });
55974
+ logger18.info("Manually advanced student", {
55975
+ gameId,
55976
+ studentId,
55977
+ subject: currentIntegration.subject,
55978
+ grade: currentIntegration.grade,
55979
+ promotionStatus: promotion.status,
55980
+ currentCourseId: promotion.currentCourseId,
55981
+ nextCourseId: promotion.nextCourseId
55982
+ });
55983
+ return { status: "ok", promotion };
55984
+ }
55795
55985
  async recordHeartbeat({
55796
55986
  gameId,
55797
55987
  studentId,
@@ -55814,7 +56004,7 @@ var init_timeback_service = __esm(() => {
55814
56004
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
55815
56005
  const effectiveResumeId = resumeId ?? runId;
55816
56006
  if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
55817
- logger17.debug("Skipping duplicate heartbeat window", {
56007
+ logger18.debug("Skipping duplicate heartbeat window", {
55818
56008
  gameId,
55819
56009
  studentId,
55820
56010
  runId,
@@ -55827,7 +56017,7 @@ var init_timeback_service = __esm(() => {
55827
56017
  await this.deps.validateDeveloperAccess(user, gameId);
55828
56018
  const inFlightHeartbeat = TimebackService2.getInFlightHeartbeatWindow(heartbeatWindowKey);
55829
56019
  if (inFlightHeartbeat) {
55830
- logger17.debug("Joining in-flight heartbeat window", {
56020
+ logger18.debug("Joining in-flight heartbeat window", {
55831
56021
  gameId,
55832
56022
  studentId,
55833
56023
  runId,
@@ -55864,7 +56054,7 @@ var init_timeback_service = __esm(() => {
55864
56054
  });
55865
56055
  }
55866
56056
  TimebackService2.markHeartbeatWindowProcessed(heartbeatWindowKey);
55867
- logger17.debug("Recorded heartbeat", {
56057
+ logger18.debug("Recorded heartbeat", {
55868
56058
  gameId,
55869
56059
  courseId: integration.courseId,
55870
56060
  studentId,
@@ -55899,7 +56089,7 @@ var init_timeback_service = __esm(() => {
55899
56089
  });
55900
56090
  courseIds = integrations.map((i2) => i2.courseId);
55901
56091
  if (courseIds.length === 0) {
55902
- logger17.debug("No integrations found for game, returning 0 XP", {
56092
+ logger18.debug("No integrations found for game, returning 0 XP", {
55903
56093
  timebackId,
55904
56094
  gameId: options.gameId,
55905
56095
  grade: options.grade,
@@ -55916,7 +56106,7 @@ var init_timeback_service = __esm(() => {
55916
56106
  courseIds: courseIds.length > 0 ? courseIds : undefined,
55917
56107
  include: options?.include
55918
56108
  });
55919
- logger17.debug("Retrieved student XP", {
56109
+ logger18.debug("Retrieved student XP", {
55920
56110
  timebackId,
55921
56111
  gameId: options?.gameId,
55922
56112
  grade: options?.grade,
@@ -55944,15 +56134,15 @@ class UploadService {
55944
56134
  const { fileName, gameId } = request;
55945
56135
  const bucketName = this.deps.uploadBucket;
55946
56136
  if (!bucketName) {
55947
- logger18.error("Upload bucket not configured in environment");
56137
+ logger19.error("Upload bucket not configured in environment");
55948
56138
  throw new ValidationError("Upload bucket not configured");
55949
56139
  }
55950
56140
  await this.deps.validateDeveloperAccess(user, gameId);
55951
56141
  const version2 = ulid();
55952
56142
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
55953
- logger18.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
56143
+ logger19.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
55954
56144
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
55955
- logger18.info("Presigned URL generated", {
56145
+ logger19.info("Presigned URL generated", {
55956
56146
  userId: user.id,
55957
56147
  gameId,
55958
56148
  version: version2
@@ -55965,12 +56155,12 @@ class UploadService {
55965
56155
  };
55966
56156
  }
55967
56157
  }
55968
- var logger18;
56158
+ var logger19;
55969
56159
  var init_upload_service = __esm(() => {
55970
56160
  init_node();
55971
56161
  init_src2();
55972
56162
  init_errors();
55973
- logger18 = log.scope("UploadService");
56163
+ logger19 = log.scope("UploadService");
55974
56164
  });
55975
56165
  function createPlatformServices(deps) {
55976
56166
  const {
@@ -56262,7 +56452,7 @@ class AchievementService {
56262
56452
  results.push(result);
56263
56453
  }
56264
56454
  }
56265
- logger19.debug("Listed current achievements", { userId: user.id, count: results.length });
56455
+ logger20.debug("Listed current achievements", { userId: user.id, count: results.length });
56266
56456
  return results;
56267
56457
  }
56268
56458
  async listHistory(user, limit) {
@@ -56280,14 +56470,14 @@ class AchievementService {
56280
56470
  createdAt: c.createdAt,
56281
56471
  scopeKey: c.scopeKey
56282
56472
  }));
56283
- logger19.debug("Listed achievement history", { userId: user.id, count: results.length });
56473
+ logger20.debug("Listed achievement history", { userId: user.id, count: results.length });
56284
56474
  return results;
56285
56475
  }
56286
56476
  async submitProgress(achievementId, user) {
56287
56477
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
56288
56478
  broadcast: false
56289
56479
  });
56290
- logger19.debug("Submitted progress", {
56480
+ logger20.debug("Submitted progress", {
56291
56481
  userId: user.id,
56292
56482
  achievementId,
56293
56483
  wasNewClaim
@@ -56319,7 +56509,7 @@ class AchievementService {
56319
56509
  rewardCredits
56320
56510
  }).returning();
56321
56511
  if (!newClaim) {
56322
- logger19.error("Achievement claim insert returned no rows", {
56512
+ logger20.error("Achievement claim insert returned no rows", {
56323
56513
  userId,
56324
56514
  achievementId,
56325
56515
  scopeKey
@@ -56328,7 +56518,7 @@ class AchievementService {
56328
56518
  }
56329
56519
  await this.deps.addCredits(userId, rewardCredits);
56330
56520
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
56331
- logger19.info("Awarded achievement", {
56521
+ logger20.info("Awarded achievement", {
56332
56522
  userId,
56333
56523
  achievementId,
56334
56524
  scopeKey,
@@ -56365,7 +56555,7 @@ class AchievementService {
56365
56555
  return { title, body: body2 };
56366
56556
  }
56367
56557
  }
56368
- var logger19;
56558
+ var logger20;
56369
56559
  var init_achievement_service = __esm(() => {
56370
56560
  init_drizzle_orm();
56371
56561
  init_tables_index();
@@ -56374,7 +56564,7 @@ var init_achievement_service = __esm(() => {
56374
56564
  init_errors();
56375
56565
  init_leaderboard_util();
56376
56566
  init_scope_util();
56377
- logger19 = log.scope("AchievementService");
56567
+ logger20 = log.scope("AchievementService");
56378
56568
  });
56379
56569
 
56380
56570
  class InventoryService {
@@ -56401,7 +56591,7 @@ class InventoryService {
56401
56591
  },
56402
56592
  updatedAt: inventoryItems.updatedAt
56403
56593
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
56404
- logger20.debug("Listed inventory", { userId: user.id, count: inventory.length });
56594
+ logger21.debug("Listed inventory", { userId: user.id, count: inventory.length });
56405
56595
  return inventory;
56406
56596
  }
56407
56597
  async addItem(itemId, quantity, user) {
@@ -56418,7 +56608,7 @@ class InventoryService {
56418
56608
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
56419
56609
  return inserted?.quantity ?? 0;
56420
56610
  });
56421
- logger20.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
56611
+ logger21.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
56422
56612
  return { newTotal };
56423
56613
  }
56424
56614
  async removeItem(itemId, quantity, user) {
@@ -56429,7 +56619,7 @@ class InventoryService {
56429
56619
  }
56430
56620
  const [currentItem] = await tx.select({ id: inventoryItems.id, quantity: inventoryItems.quantity }).from(inventoryItems).where(and(eq(inventoryItems.userId, user.id), eq(inventoryItems.itemId, itemId), gte(inventoryItems.quantity, quantity))).limit(1);
56431
56621
  if (!currentItem) {
56432
- logger20.warn("Insufficient inventory for removal", {
56622
+ logger21.warn("Insufficient inventory for removal", {
56433
56623
  userId: user.id,
56434
56624
  itemId,
56435
56625
  requestedQuantity: quantity
@@ -56439,13 +56629,13 @@ class InventoryService {
56439
56629
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
56440
56630
  return updated?.quantity ?? 0;
56441
56631
  });
56442
- logger20.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
56632
+ logger21.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
56443
56633
  return { newTotal };
56444
56634
  }
56445
56635
  async addCredits(userId, amount) {
56446
56636
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
56447
56637
  if (!creditsItem) {
56448
- logger20.error("Primary currency not found", {
56638
+ logger21.error("Primary currency not found", {
56449
56639
  userId,
56450
56640
  amount
56451
56641
  });
@@ -56458,17 +56648,17 @@ class InventoryService {
56458
56648
  updatedAt: new Date
56459
56649
  }
56460
56650
  });
56461
- logger20.debug("Added credits", { userId, amount });
56651
+ logger21.debug("Added credits", { userId, amount });
56462
56652
  }
56463
56653
  }
56464
- var logger20;
56654
+ var logger21;
56465
56655
  var init_inventory_service = __esm(() => {
56466
56656
  init_drizzle_orm();
56467
56657
  init_src();
56468
56658
  init_tables_index();
56469
56659
  init_src2();
56470
56660
  init_errors();
56471
- logger20 = log.scope("InventoryService");
56661
+ logger21 = log.scope("InventoryService");
56472
56662
  });
56473
56663
  var NotificationType;
56474
56664
  var NotificationStatus;
@@ -56522,7 +56712,7 @@ class LeaderboardService {
56522
56712
  sessionId
56523
56713
  }).returning();
56524
56714
  if (!newScore) {
56525
- logger21.error("Score insert returned no rows", { userId, gameId, score: input.score });
56715
+ logger22.error("Score insert returned no rows", { userId, gameId, score: input.score });
56526
56716
  throw new InternalError("Failed to insert score");
56527
56717
  }
56528
56718
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -56550,7 +56740,7 @@ class LeaderboardService {
56550
56740
  movedUpWithinTop3
56551
56741
  });
56552
56742
  }
56553
- logger21.info("Score submitted", {
56743
+ logger22.info("Score submitted", {
56554
56744
  gameId,
56555
56745
  userId,
56556
56746
  isAnonymousUser,
@@ -56627,7 +56817,7 @@ class LeaderboardService {
56627
56817
  });
56628
56818
  }
56629
56819
  } catch (error) {
56630
- logger21.warn("Failed to publish notification", { error });
56820
+ logger22.warn("Failed to publish notification", { error });
56631
56821
  }
56632
56822
  }
56633
56823
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -56746,7 +56936,7 @@ class LeaderboardService {
56746
56936
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
56747
56937
  }
56748
56938
  }
56749
- var logger21;
56939
+ var logger22;
56750
56940
  var init_leaderboard_service = __esm(() => {
56751
56941
  init_drizzle_orm();
56752
56942
  init_src();
@@ -56756,7 +56946,7 @@ var init_leaderboard_service = __esm(() => {
56756
56946
  init_notification();
56757
56947
  init_errors();
56758
56948
  init_leaderboard_util();
56759
- logger21 = log.scope("LeaderboardService");
56949
+ logger22 = log.scope("LeaderboardService");
56760
56950
  });
56761
56951
 
56762
56952
  class LevelService {
@@ -56771,9 +56961,9 @@ class LevelService {
56771
56961
  for (const config2 of configs) {
56772
56962
  levelConfigCache.set(config2.level, config2);
56773
56963
  }
56774
- logger22.info("Cache pre-warmed", { count: configs.length });
56964
+ logger23.info("Cache pre-warmed", { count: configs.length });
56775
56965
  } catch (error) {
56776
- logger22.error("Cache pre-warm failed", { error });
56966
+ logger23.error("Cache pre-warm failed", { error });
56777
56967
  }
56778
56968
  }
56779
56969
  async getConfig(level) {
@@ -56807,7 +56997,7 @@ class LevelService {
56807
56997
  totalXP
56808
56998
  }).returning();
56809
56999
  if (!newUserLevel) {
56810
- logger22.error("User level insert returned no rows", { userId: user.id });
57000
+ logger23.error("User level insert returned no rows", { userId: user.id });
56811
57001
  throw new InternalError("Failed to create user level cache record");
56812
57002
  }
56813
57003
  userLevel = newUserLevel;
@@ -56822,7 +57012,7 @@ class LevelService {
56822
57012
  userLevel = updatedUserLevel;
56823
57013
  }
56824
57014
  }
56825
- logger22.debug("Retrieved user level", {
57015
+ logger23.debug("Retrieved user level", {
56826
57016
  userId: user.id,
56827
57017
  totalXP,
56828
57018
  currentLevel,
@@ -56833,7 +57023,7 @@ class LevelService {
56833
57023
  async getProgress(user) {
56834
57024
  const userLevel = await this.getByUser(user);
56835
57025
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
56836
- logger22.debug("Retrieved progress", { userId: user.id });
57026
+ logger23.debug("Retrieved progress", { userId: user.id });
56837
57027
  return {
56838
57028
  level: userLevel.currentLevel,
56839
57029
  currentXp: userLevel.currentXp,
@@ -56867,7 +57057,7 @@ class LevelService {
56867
57057
  if (leveledUp && previousUserLevel) {
56868
57058
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
56869
57059
  }
56870
- logger22.info("Synced from Timeback", {
57060
+ logger23.info("Synced from Timeback", {
56871
57061
  userId,
56872
57062
  totalXP,
56873
57063
  currentLevel,
@@ -56908,7 +57098,7 @@ class LevelService {
56908
57098
  }
56909
57099
  if (totalCredits > 0) {
56910
57100
  await this.deps.addCredits(userId, totalCredits);
56911
- logger22.info("Awarded level-up credits", {
57101
+ logger23.info("Awarded level-up credits", {
56912
57102
  userId,
56913
57103
  fromLevel,
56914
57104
  toLevel,
@@ -56946,7 +57136,7 @@ class LevelService {
56946
57136
  };
56947
57137
  }
56948
57138
  }
56949
- var logger22;
57139
+ var logger23;
56950
57140
  var levelConfigCache = null;
56951
57141
  var init_level_service = __esm(() => {
56952
57142
  init_drizzle_orm();
@@ -56954,7 +57144,7 @@ var init_level_service = __esm(() => {
56954
57144
  init_tables_index();
56955
57145
  init_src2();
56956
57146
  init_errors();
56957
- logger22 = log.scope("LevelService");
57147
+ logger23 = log.scope("LevelService");
56958
57148
  });
56959
57149
  var init_events = () => {};
56960
57150
  function convertWebSocketUrlToHttp(wsUrl) {
@@ -56971,13 +57161,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
56971
57161
  });
56972
57162
  if (!res.ok) {
56973
57163
  const text3 = await res.text().catch(() => "");
56974
- logger23.warn("Failed to publish to user", {
57164
+ logger24.warn("Failed to publish to user", {
56975
57165
  status: res.status,
56976
57166
  body: text3
56977
57167
  });
56978
57168
  }
56979
57169
  } catch (error) {
56980
- logger23.error("Publish to user error", { error });
57170
+ logger24.error("Publish to user error", { error });
56981
57171
  }
56982
57172
  }
56983
57173
 
@@ -56999,7 +57189,7 @@ class NotificationService {
56999
57189
  conditions2.push(eq(notifications.type, type));
57000
57190
  }
57001
57191
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
57002
- logger23.debug("Listed notifications", { userId: user.id, count: results.length });
57192
+ logger24.debug("Listed notifications", { userId: user.id, count: results.length });
57003
57193
  return results;
57004
57194
  }
57005
57195
  async updateStatus(notificationId, status, method) {
@@ -57018,7 +57208,7 @@ class NotificationService {
57018
57208
  if (!updated) {
57019
57209
  throw new NotFoundError("Notification", notificationId);
57020
57210
  }
57021
- logger23.debug("Updated status", { notificationId, status });
57211
+ logger24.debug("Updated status", { notificationId, status });
57022
57212
  return updated;
57023
57213
  }
57024
57214
  async getStats(user, options) {
@@ -57044,7 +57234,7 @@ class NotificationService {
57044
57234
  const clicked = statsMap.clicked || 0;
57045
57235
  const dismissed = statsMap.dismissed || 0;
57046
57236
  const expired = statsMap.expired || 0;
57047
- logger23.debug("Retrieved stats", { userId: user.id, total });
57237
+ logger24.debug("Retrieved stats", { userId: user.id, total });
57048
57238
  return {
57049
57239
  total,
57050
57240
  delivered,
@@ -57093,7 +57283,7 @@ class NotificationService {
57093
57283
  options: { data, clickUrl, metadata: metadata2 }
57094
57284
  });
57095
57285
  }
57096
- logger23.debug("Created notification", {
57286
+ logger24.debug("Created notification", {
57097
57287
  userId,
57098
57288
  type,
57099
57289
  id: notificationId,
@@ -57101,7 +57291,7 @@ class NotificationService {
57101
57291
  });
57102
57292
  return notificationId;
57103
57293
  } catch (error) {
57104
- logger23.error("Failed to create notification", { userId, type, error });
57294
+ logger24.error("Failed to create notification", { userId, type, error });
57105
57295
  return null;
57106
57296
  }
57107
57297
  }
@@ -57115,7 +57305,7 @@ class NotificationService {
57115
57305
  }) {
57116
57306
  const realtimeConfig = this.deps.realtime;
57117
57307
  if (!realtimeConfig) {
57118
- logger23.warn("No realtime config for publish");
57308
+ logger24.warn("No realtime config for publish");
57119
57309
  return;
57120
57310
  }
57121
57311
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -57157,13 +57347,13 @@ class NotificationService {
57157
57347
  metadata: data.metadata || {}
57158
57348
  }).returning();
57159
57349
  if (!notification) {
57160
- logger23.error("Notification insert returned no rows", {
57350
+ logger24.error("Notification insert returned no rows", {
57161
57351
  userId: data.userId,
57162
57352
  type: data.type
57163
57353
  });
57164
57354
  throw new InternalError("Failed to create notification");
57165
57355
  }
57166
- logger23.info("Inserted notification", {
57356
+ logger24.info("Inserted notification", {
57167
57357
  notificationId: notification.id,
57168
57358
  userId: notification.userId,
57169
57359
  type: notification.type
@@ -57173,7 +57363,7 @@ class NotificationService {
57173
57363
  async deliverPending(userId) {
57174
57364
  const realtimeConfig = this.deps.realtime;
57175
57365
  if (!realtimeConfig) {
57176
- logger23.warn("No realtime config for delivery");
57366
+ logger24.warn("No realtime config for delivery");
57177
57367
  return;
57178
57368
  }
57179
57369
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -57200,13 +57390,13 @@ class NotificationService {
57200
57390
  metadata: notification.metadata,
57201
57391
  clickUrl: notification.clickUrl
57202
57392
  });
57203
- logger23.info("Delivered notification", {
57393
+ logger24.info("Delivered notification", {
57204
57394
  notificationId: notification.id,
57205
57395
  userId,
57206
57396
  type: notification.type
57207
57397
  });
57208
57398
  } catch (error) {
57209
- logger23.warn("Failed to deliver", {
57399
+ logger24.warn("Failed to deliver", {
57210
57400
  notificationId: notification.id,
57211
57401
  error
57212
57402
  });
@@ -57214,7 +57404,7 @@ class NotificationService {
57214
57404
  }
57215
57405
  }
57216
57406
  }
57217
- var logger23;
57407
+ var logger24;
57218
57408
  var init_notification_service = __esm(() => {
57219
57409
  init_drizzle_orm();
57220
57410
  init_src();
@@ -57223,7 +57413,7 @@ var init_notification_service = __esm(() => {
57223
57413
  init_events();
57224
57414
  init_notification();
57225
57415
  init_errors();
57226
- logger23 = log.scope("NotificationService");
57416
+ logger24 = log.scope("NotificationService");
57227
57417
  });
57228
57418
  function createPlayerServices(deps) {
57229
57419
  const { db: db2, realtime } = deps;
@@ -57342,7 +57532,7 @@ class CharacterService {
57342
57532
  createdAt: characterComponents.createdAt,
57343
57533
  updatedAt: characterComponents.updatedAt
57344
57534
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
57345
- logger24.debug("Listed available components", {
57535
+ logger25.debug("Listed available components", {
57346
57536
  level,
57347
57537
  count: components.length
57348
57538
  });
@@ -57360,7 +57550,7 @@ class CharacterService {
57360
57550
  }
57361
57551
  }
57362
57552
  });
57363
- logger24.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
57553
+ logger25.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
57364
57554
  return pc3 ?? null;
57365
57555
  }
57366
57556
  async getByUserId(userId) {
@@ -57375,7 +57565,7 @@ class CharacterService {
57375
57565
  }
57376
57566
  }
57377
57567
  });
57378
- logger24.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
57568
+ logger25.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
57379
57569
  return pc3 ?? null;
57380
57570
  }
57381
57571
  async create(input, user) {
@@ -57390,13 +57580,13 @@ class CharacterService {
57390
57580
  }
57391
57581
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
57392
57582
  if (!characterRow) {
57393
- logger24.error("Character insert returned no rows", { userId: user.id });
57583
+ logger25.error("Character insert returned no rows", { userId: user.id });
57394
57584
  throw new InternalError("Failed to create character in database");
57395
57585
  }
57396
57586
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
57397
57587
  return characterRow;
57398
57588
  });
57399
- logger24.info("Created character", { userId: user.id, characterId: result.id });
57589
+ logger25.info("Created character", { userId: user.id, characterId: result.id });
57400
57590
  return result;
57401
57591
  }
57402
57592
  async update(input, user) {
@@ -57408,7 +57598,7 @@ class CharacterService {
57408
57598
  if (!row) {
57409
57599
  throw new NotFoundError("Player character");
57410
57600
  }
57411
- logger24.info("Updated character", {
57601
+ logger25.info("Updated character", {
57412
57602
  userId: user.id,
57413
57603
  characterId: row.id,
57414
57604
  updatedFields: Object.keys(input)
@@ -57430,7 +57620,7 @@ class CharacterService {
57430
57620
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
57431
57621
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
57432
57622
  if (!validation.isValid) {
57433
- logger24.warn("Accessory validation failed", {
57623
+ logger25.warn("Accessory validation failed", {
57434
57624
  userId: user.id,
57435
57625
  slot,
57436
57626
  accessoryComponentId,
@@ -57446,14 +57636,14 @@ class CharacterService {
57446
57636
  slot
57447
57637
  }).returning();
57448
57638
  if (!result) {
57449
- logger24.error("Accessory insert returned no rows", {
57639
+ logger25.error("Accessory insert returned no rows", {
57450
57640
  userId: user.id,
57451
57641
  slot,
57452
57642
  accessoryComponentId
57453
57643
  });
57454
57644
  throw new InternalError("Failed to equip accessory");
57455
57645
  }
57456
- logger24.info("Equipped accessory", {
57646
+ logger25.info("Equipped accessory", {
57457
57647
  userId: user.id,
57458
57648
  slot,
57459
57649
  accessoryComponentId
@@ -57474,7 +57664,7 @@ class CharacterService {
57474
57664
  const playerLevel = userLevel?.currentLevel ?? 1;
57475
57665
  const validation = validateAccessoryRemoval(slot, playerLevel);
57476
57666
  if (!validation.isValid) {
57477
- logger24.warn("Accessory removal validation failed", {
57667
+ logger25.warn("Accessory removal validation failed", {
57478
57668
  userId: user.id,
57479
57669
  slot,
57480
57670
  playerLevel,
@@ -57483,17 +57673,17 @@ class CharacterService {
57483
57673
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
57484
57674
  }
57485
57675
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
57486
- logger24.info("Removed accessory", { userId: user.id, slot });
57676
+ logger25.info("Removed accessory", { userId: user.id, slot });
57487
57677
  }
57488
57678
  }
57489
- var logger24;
57679
+ var logger25;
57490
57680
  var init_character_service = __esm(() => {
57491
57681
  init_drizzle_orm();
57492
57682
  init_tables_index();
57493
57683
  init_src2();
57494
57684
  init_errors();
57495
57685
  init_accessory_util();
57496
- logger24 = log.scope("CharacterService");
57686
+ logger25 = log.scope("CharacterService");
57497
57687
  });
57498
57688
 
57499
57689
  class CurrencyService {
@@ -57504,7 +57694,7 @@ class CurrencyService {
57504
57694
  async list() {
57505
57695
  const db2 = this.deps.db;
57506
57696
  const allCurrencies = await db2.query.currencies.findMany();
57507
- logger25.debug("Listed currencies", { count: allCurrencies.length });
57697
+ logger26.debug("Listed currencies", { count: allCurrencies.length });
57508
57698
  return allCurrencies;
57509
57699
  }
57510
57700
  async getById(currencyId) {
@@ -57515,7 +57705,7 @@ class CurrencyService {
57515
57705
  if (!currency) {
57516
57706
  throw new NotFoundError("Currency", currencyId);
57517
57707
  }
57518
- logger25.debug("Retrieved currency", { currencyId });
57708
+ logger26.debug("Retrieved currency", { currencyId });
57519
57709
  return currency;
57520
57710
  }
57521
57711
  async create(data) {
@@ -57523,13 +57713,13 @@ class CurrencyService {
57523
57713
  try {
57524
57714
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
57525
57715
  if (!newCurrency) {
57526
- logger25.error("Currency insert returned no rows", {
57716
+ logger26.error("Currency insert returned no rows", {
57527
57717
  itemId: data.itemId,
57528
57718
  symbol: data.symbol
57529
57719
  });
57530
57720
  throw new InternalError("Failed to create currency");
57531
57721
  }
57532
- logger25.info("Created currency", {
57722
+ logger26.info("Created currency", {
57533
57723
  currencyId: newCurrency.id,
57534
57724
  itemId: newCurrency.itemId,
57535
57725
  symbol: newCurrency.symbol,
@@ -57558,7 +57748,7 @@ class CurrencyService {
57558
57748
  if (!updatedCurrency) {
57559
57749
  throw new NotFoundError("Currency", currencyId);
57560
57750
  }
57561
- logger25.info("Updated currency", {
57751
+ logger26.info("Updated currency", {
57562
57752
  currencyId: updatedCurrency.id,
57563
57753
  updatedFields: Object.keys(data)
57564
57754
  });
@@ -57584,16 +57774,16 @@ class CurrencyService {
57584
57774
  if (result.length === 0) {
57585
57775
  throw new NotFoundError("Currency", currencyId);
57586
57776
  }
57587
- logger25.info("Deleted currency", { currencyId });
57777
+ logger26.info("Deleted currency", { currencyId });
57588
57778
  }
57589
57779
  }
57590
- var logger25;
57780
+ var logger26;
57591
57781
  var init_currency_service = __esm(() => {
57592
57782
  init_drizzle_orm();
57593
57783
  init_tables_index();
57594
57784
  init_src2();
57595
57785
  init_errors();
57596
- logger25 = log.scope("CurrencyService");
57786
+ logger26 = log.scope("CurrencyService");
57597
57787
  });
57598
57788
 
57599
57789
  class LogsService {
@@ -57611,11 +57801,11 @@ class LogsService {
57611
57801
  if (!game) {
57612
57802
  throw new NotFoundError("Game", slug2);
57613
57803
  }
57614
- logger26.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
57804
+ logger27.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
57615
57805
  } else {
57616
57806
  const isApprovedDev = user.developerStatus === "approved";
57617
57807
  if (!isApprovedDev) {
57618
- logger26.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
57808
+ logger27.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
57619
57809
  throw new AccessDeniedError("Must be an approved developer");
57620
57810
  }
57621
57811
  const game = await db2.query.games.findFirst({
@@ -57623,7 +57813,7 @@ class LogsService {
57623
57813
  columns: { id: true }
57624
57814
  });
57625
57815
  if (!game) {
57626
- logger26.warn("Developer attempted access to unowned game logs", {
57816
+ logger27.warn("Developer attempted access to unowned game logs", {
57627
57817
  userId: user.id,
57628
57818
  slug: slug2
57629
57819
  });
@@ -57633,7 +57823,7 @@ class LogsService {
57633
57823
  const isProduction3 = environment === "production";
57634
57824
  const workerId = getDeploymentId(slug2, isProduction3);
57635
57825
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
57636
- logger26.debug("Generated log stream token", {
57826
+ logger27.debug("Generated log stream token", {
57637
57827
  userId: user.id,
57638
57828
  slug: slug2,
57639
57829
  workerId
@@ -57641,14 +57831,14 @@ class LogsService {
57641
57831
  return { token, workerId };
57642
57832
  }
57643
57833
  }
57644
- var logger26;
57834
+ var logger27;
57645
57835
  var init_logs_service = __esm(() => {
57646
57836
  init_drizzle_orm();
57647
57837
  init_tables_index();
57648
57838
  init_src2();
57649
57839
  init_errors();
57650
57840
  init_deployment_util();
57651
- logger26 = log.scope("LogsService");
57841
+ logger27 = log.scope("LogsService");
57652
57842
  });
57653
57843
 
57654
57844
  class LtiService {
@@ -57699,7 +57889,7 @@ class MapService {
57699
57889
  if (!mapDetails) {
57700
57890
  throw new NotFoundError("Map", identifier);
57701
57891
  }
57702
- logger27.debug("Retrieved map", { identifier });
57892
+ logger28.debug("Retrieved map", { identifier });
57703
57893
  return mapDetails;
57704
57894
  }
57705
57895
  async getElements(mapId) {
@@ -57715,7 +57905,7 @@ class MapService {
57715
57905
  }
57716
57906
  }
57717
57907
  });
57718
- logger27.debug("Retrieved elements", { mapId, count: elements.length });
57908
+ logger28.debug("Retrieved elements", { mapId, count: elements.length });
57719
57909
  return elements;
57720
57910
  }
57721
57911
  async getObjects(mapId, userId) {
@@ -57736,7 +57926,7 @@ class MapService {
57736
57926
  }
57737
57927
  }
57738
57928
  });
57739
- logger27.debug("Retrieved objects", { mapId, userId, count: objects.length });
57929
+ logger28.debug("Retrieved objects", { mapId, userId, count: objects.length });
57740
57930
  return objects.map((object) => this.formatMapObjectWithItem(object));
57741
57931
  }
57742
57932
  async createObject(mapId, data, user) {
@@ -57763,7 +57953,7 @@ class MapService {
57763
57953
  throw new NotFoundError("Item", data.itemId);
57764
57954
  }
57765
57955
  if (!item.isPlaceable) {
57766
- logger27.warn("Attempted to place non-placeable item", {
57956
+ logger28.warn("Attempted to place non-placeable item", {
57767
57957
  userId: user.id,
57768
57958
  itemId: data.itemId,
57769
57959
  mapId
@@ -57777,7 +57967,7 @@ class MapService {
57777
57967
  };
57778
57968
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
57779
57969
  if (!createdObject) {
57780
- logger27.error("Map object insert returned no rows", {
57970
+ logger28.error("Map object insert returned no rows", {
57781
57971
  userId: user.id,
57782
57972
  mapId,
57783
57973
  itemId: data.itemId
@@ -57801,12 +57991,12 @@ class MapService {
57801
57991
  }
57802
57992
  });
57803
57993
  if (!objectWithItem) {
57804
- logger27.error("Map object query after insert returned no rows", {
57994
+ logger28.error("Map object query after insert returned no rows", {
57805
57995
  objectId: createdObject.id
57806
57996
  });
57807
57997
  throw new InternalError("Failed to retrieve created object");
57808
57998
  }
57809
- logger27.info("Created object", {
57999
+ logger28.info("Created object", {
57810
58000
  userId: user.id,
57811
58001
  mapId,
57812
58002
  objectId: createdObject.id,
@@ -57830,7 +58020,7 @@ class MapService {
57830
58020
  if (result.length === 0) {
57831
58021
  throw new NotFoundError("MapObject", objectId);
57832
58022
  }
57833
- logger27.info("Deleted object", {
58023
+ logger28.info("Deleted object", {
57834
58024
  userId: user.id,
57835
58025
  mapId,
57836
58026
  objectId
@@ -57859,13 +58049,13 @@ class MapService {
57859
58049
  };
57860
58050
  }
57861
58051
  }
57862
- var logger27;
58052
+ var logger28;
57863
58053
  var init_map_service = __esm(() => {
57864
58054
  init_drizzle_orm();
57865
58055
  init_tables_index();
57866
58056
  init_src2();
57867
58057
  init_errors();
57868
- logger27 = log.scope("MapService");
58058
+ logger28 = log.scope("MapService");
57869
58059
  });
57870
58060
 
57871
58061
  class RealtimeService {
@@ -57899,20 +58089,20 @@ class RealtimeService {
57899
58089
  }
57900
58090
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
57901
58091
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
57902
- logger28.info("Generated token", {
58092
+ logger29.info("Generated token", {
57903
58093
  userId: user.id,
57904
58094
  gameId: resolvedGameId || "global"
57905
58095
  });
57906
58096
  return { token };
57907
58097
  }
57908
58098
  }
57909
- var logger28;
58099
+ var logger29;
57910
58100
  var init_realtime_service = __esm(() => {
57911
58101
  init_drizzle_orm();
57912
58102
  init_tables_index();
57913
58103
  init_src2();
57914
58104
  init_errors();
57915
- logger28 = log.scope("RealtimeService");
58105
+ logger29 = log.scope("RealtimeService");
57916
58106
  });
57917
58107
 
57918
58108
  class SessionService {
@@ -57946,10 +58136,10 @@ class SessionService {
57946
58136
  };
57947
58137
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
57948
58138
  if (!newSession?.sessionId) {
57949
- logger29.error("Game session insert returned no rows", { userId, gameId });
58139
+ logger30.error("Game session insert returned no rows", { userId, gameId });
57950
58140
  throw new InternalError("Failed to create game session");
57951
58141
  }
57952
- logger29.info("Started new session", {
58142
+ logger30.info("Started new session", {
57953
58143
  sessionId: newSession.sessionId,
57954
58144
  gameId,
57955
58145
  userId
@@ -57970,23 +58160,23 @@ class SessionService {
57970
58160
  return { success: true, message: "Session already ended" };
57971
58161
  }
57972
58162
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
57973
- logger29.info("Ended session", { sessionId, gameId, userId });
58163
+ logger30.info("Ended session", { sessionId, gameId, userId });
57974
58164
  return { success: true };
57975
58165
  }
57976
58166
  async mintToken(gameIdOrSlug, userId) {
57977
58167
  const gameId = await this.resolveGameId(gameIdOrSlug);
57978
58168
  const result = await this.deps.mintGameToken(gameId, userId);
57979
- logger29.debug("Minted game token", { gameId, userId });
58169
+ logger30.debug("Minted game token", { gameId, userId });
57980
58170
  return result;
57981
58171
  }
57982
58172
  }
57983
- var logger29;
58173
+ var logger30;
57984
58174
  var init_session_service = __esm(() => {
57985
58175
  init_drizzle_orm();
57986
58176
  init_tables_index();
57987
58177
  init_src2();
57988
58178
  init_errors();
57989
- logger29 = log.scope("SessionService");
58179
+ logger30 = log.scope("SessionService");
57990
58180
  });
57991
58181
 
57992
58182
  class ShopService {
@@ -58031,7 +58221,7 @@ class ShopService {
58031
58221
  const shopItems = [];
58032
58222
  for (const listing of listingsWithRelations) {
58033
58223
  if (!listing.item || !listing.currency) {
58034
- logger30.warn("Listing missing item or currency, skipping", {
58224
+ logger31.warn("Listing missing item or currency, skipping", {
58035
58225
  listingId: listing.id
58036
58226
  });
58037
58227
  } else {
@@ -58048,7 +58238,7 @@ class ShopService {
58048
58238
  });
58049
58239
  }
58050
58240
  }
58051
- logger30.debug("Retrieved shop view", {
58241
+ logger31.debug("Retrieved shop view", {
58052
58242
  userId: user.id,
58053
58243
  itemCount: shopItems.length,
58054
58244
  currencyCount: shopCurrencies.length
@@ -58059,12 +58249,12 @@ class ShopService {
58059
58249
  };
58060
58250
  }
58061
58251
  }
58062
- var logger30;
58252
+ var logger31;
58063
58253
  var init_shop_service = __esm(() => {
58064
58254
  init_drizzle_orm();
58065
58255
  init_tables_index();
58066
58256
  init_src2();
58067
- logger30 = log.scope("ShopService");
58257
+ logger31 = log.scope("ShopService");
58068
58258
  });
58069
58259
 
58070
58260
  class SpriteService {
@@ -58080,17 +58270,17 @@ class SpriteService {
58080
58270
  if (!template) {
58081
58271
  throw new NotFoundError("SpriteTemplate", slug2);
58082
58272
  }
58083
- logger31.debug("Retrieved sprite", { slug: slug2 });
58273
+ logger32.debug("Retrieved sprite", { slug: slug2 });
58084
58274
  return template;
58085
58275
  }
58086
58276
  }
58087
- var logger31;
58277
+ var logger32;
58088
58278
  var init_sprite_service = __esm(() => {
58089
58279
  init_drizzle_orm();
58090
58280
  init_tables_index();
58091
58281
  init_src2();
58092
58282
  init_errors();
58093
- logger31 = log.scope("SpriteService");
58283
+ logger32 = log.scope("SpriteService");
58094
58284
  });
58095
58285
 
58096
58286
  class UserService {
@@ -58104,12 +58294,12 @@ class UserService {
58104
58294
  where: eq(users.id, user.id)
58105
58295
  });
58106
58296
  if (!userData) {
58107
- logger32.error("User not found", { userId: user.id });
58297
+ logger33.error("User not found", { userId: user.id });
58108
58298
  throw new NotFoundError("User", user.id);
58109
58299
  }
58110
58300
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
58111
58301
  if (gameId) {
58112
- logger32.debug("Fetched user profile (game context)", { userId: user.id, gameId });
58302
+ logger33.debug("Fetched user profile (game context)", { userId: user.id, gameId });
58113
58303
  return {
58114
58304
  id: userData.id,
58115
58305
  name: userData.name,
@@ -58122,7 +58312,7 @@ class UserService {
58122
58312
  const timebackAccount = await db2.query.accounts.findFirst({
58123
58313
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
58124
58314
  });
58125
- logger32.debug("Fetched user profile (platform context)", { userId: user.id });
58315
+ logger33.debug("Fetched user profile (platform context)", { userId: user.id });
58126
58316
  return {
58127
58317
  id: userData.id,
58128
58318
  name: userData.name,
@@ -58145,7 +58335,7 @@ class UserService {
58145
58335
  columns: { name: true }
58146
58336
  });
58147
58337
  if (!userData) {
58148
- logger32.error("Demo user not found", { userId });
58338
+ logger33.error("Demo user not found", { userId });
58149
58339
  throw new NotFoundError("User", userId);
58150
58340
  }
58151
58341
  return {
@@ -58159,10 +58349,10 @@ class UserService {
58159
58349
  updatedAt: new Date
58160
58350
  }).where(eq(users.id, userId)).returning({ name: users.name });
58161
58351
  if (!updatedUser) {
58162
- logger32.error("Demo user not found for profile update", { userId });
58352
+ logger33.error("Demo user not found for profile update", { userId });
58163
58353
  throw new NotFoundError("User", userId);
58164
58354
  }
58165
- logger32.debug("Updated demo profile", { userId, displayName });
58355
+ logger33.debug("Updated demo profile", { userId, displayName });
58166
58356
  return {
58167
58357
  displayName: updatedUser.name,
58168
58358
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -58175,7 +58365,7 @@ class UserService {
58175
58365
  ]);
58176
58366
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
58177
58367
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
58178
- logger32.debug("Fetched Timeback data", {
58368
+ logger33.debug("Fetched Timeback data", {
58179
58369
  timebackId,
58180
58370
  role,
58181
58371
  enrollmentCount: enrollments.length,
@@ -58184,9 +58374,9 @@ class UserService {
58184
58374
  return { id: timebackId, role, enrollments, organizations };
58185
58375
  }
58186
58376
  async fetchStudentProfile(timebackId) {
58187
- logger32.debug("Fetching student profile", { timebackId });
58377
+ logger33.debug("Fetching student profile", { timebackId });
58188
58378
  if (!this.deps.timeback) {
58189
- logger32.warn("Timeback client not available");
58379
+ logger33.warn("Timeback client not available");
58190
58380
  return { role: "student", organizations: [] };
58191
58381
  }
58192
58382
  try {
@@ -58214,14 +58404,14 @@ class UserService {
58214
58404
  }
58215
58405
  return { role, organizations: [...orgMap.values()] };
58216
58406
  } catch (error) {
58217
- logger32.warn("Failed to fetch student profile", { error, timebackId });
58407
+ logger33.warn("Failed to fetch student profile", { error, timebackId });
58218
58408
  return { role: "student", organizations: [] };
58219
58409
  }
58220
58410
  }
58221
58411
  async fetchEnrollments(timebackId) {
58222
- logger32.debug("Fetching enrollments", { timebackId });
58412
+ logger33.debug("Fetching enrollments", { timebackId });
58223
58413
  if (!this.deps.timeback) {
58224
- logger32.warn("Timeback client not available");
58414
+ logger33.warn("Timeback client not available");
58225
58415
  return [];
58226
58416
  }
58227
58417
  try {
@@ -58242,7 +58432,7 @@ class UserService {
58242
58432
  orgId: courseToSchool.get(i2.courseId)
58243
58433
  }));
58244
58434
  } catch (error) {
58245
- logger32.warn("Failed to fetch enrollments", { error, timebackId });
58435
+ logger33.warn("Failed to fetch enrollments", { error, timebackId });
58246
58436
  return [];
58247
58437
  }
58248
58438
  }
@@ -58257,14 +58447,14 @@ class UserService {
58257
58447
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
58258
58448
  }
58259
58449
  }
58260
- var logger32;
58450
+ var logger33;
58261
58451
  var init_user_service = __esm(() => {
58262
58452
  init_drizzle_orm();
58263
58453
  init_src();
58264
58454
  init_tables_index();
58265
58455
  init_src2();
58266
58456
  init_errors();
58267
- logger32 = log.scope("UserService");
58457
+ logger33 = log.scope("UserService");
58268
58458
  });
58269
58459
 
58270
58460
  class VerifyService {
@@ -58273,16 +58463,16 @@ class VerifyService {
58273
58463
  this.deps = deps;
58274
58464
  }
58275
58465
  async verifyGameToken(token) {
58276
- logger33.debug("Verifying game token");
58466
+ logger34.debug("Verifying game token");
58277
58467
  const payload = await this.deps.validateGameToken(token);
58278
58468
  if (!payload) {
58279
- logger33.warn("Invalid or expired game token presented");
58469
+ logger34.warn("Invalid or expired game token presented");
58280
58470
  throw new ValidationError("Invalid or expired token");
58281
58471
  }
58282
58472
  const gameId = payload.sub;
58283
58473
  const userId = payload.uid;
58284
58474
  if (typeof gameId !== "string" || typeof userId !== "string") {
58285
- logger33.warn("Game token missing required claims", {
58475
+ logger34.warn("Game token missing required claims", {
58286
58476
  hasGameId: typeof gameId === "string",
58287
58477
  hasUserId: typeof userId === "string"
58288
58478
  });
@@ -58293,7 +58483,7 @@ class VerifyService {
58293
58483
  where: eq(users.id, userId)
58294
58484
  });
58295
58485
  if (!userData) {
58296
- logger33.error("User not found for valid token", {
58486
+ logger34.error("User not found for valid token", {
58297
58487
  userId
58298
58488
  });
58299
58489
  throw new NotFoundError("User", userId);
@@ -58307,7 +58497,7 @@ class VerifyService {
58307
58497
  family_name: undefined,
58308
58498
  timeback_id: userData.timebackId || undefined
58309
58499
  };
58310
- logger33.info("Token verified", { gameId, userId });
58500
+ logger34.info("Token verified", { gameId, userId });
58311
58501
  return {
58312
58502
  claims: payload,
58313
58503
  gameId,
@@ -58315,13 +58505,13 @@ class VerifyService {
58315
58505
  };
58316
58506
  }
58317
58507
  }
58318
- var logger33;
58508
+ var logger34;
58319
58509
  var init_verify_service = __esm(() => {
58320
58510
  init_drizzle_orm();
58321
58511
  init_tables_index();
58322
58512
  init_src2();
58323
58513
  init_errors();
58324
- logger33 = log.scope("VerifyService");
58514
+ logger34 = log.scope("VerifyService");
58325
58515
  });
58326
58516
  function createStandaloneServices(deps) {
58327
58517
  const { db: db2, auth: auth2, timeback: timeback2 } = deps;
@@ -58939,7 +59129,6 @@ async function request({
58939
59129
  method,
58940
59130
  headers,
58941
59131
  body: payload,
58942
- credentials: "omit",
58943
59132
  signal: controller.signal
58944
59133
  });
58945
59134
  clearTimeout(timeoutId);
@@ -59523,10 +59712,11 @@ function createOneRosterNamespace(client) {
59523
59712
  const url = `${ONEROSTER_ENDPOINTS4.assessmentResults}?filter=${encodeURIComponent(filter)}`;
59524
59713
  const response = await client["request"](url, "GET");
59525
59714
  const results = response.assessmentResults || [];
59526
- if (results.length === 0) {
59715
+ const firstResult = results[0];
59716
+ if (!firstResult) {
59527
59717
  return null;
59528
59718
  }
59529
- let maxAttemptResult = results[0];
59719
+ let maxAttemptResult = firstResult;
59530
59720
  let maxAttemptNumber = maxAttemptResult.metadata?.attemptNumber || 0;
59531
59721
  let activeAttemptCount = 0;
59532
59722
  for (const result of results) {
@@ -59560,10 +59750,11 @@ function createOneRosterNamespace(client) {
59560
59750
  if (!response || !response.users || !Array.isArray(response.users)) {
59561
59751
  throw new Error(`Invalid response format from OneRoster API when searching for user with email: ${email}. Expected { users: [...] } but received: ${JSON.stringify(response)}`);
59562
59752
  }
59563
- if (response.users.length === 0) {
59753
+ const user = response.users[0];
59754
+ if (!user) {
59564
59755
  throw new Error(`User not found with email: ${email}. Ensure the user exists in OneRoster and has a valid email address. If this is a new user, they must be created in OneRoster first.`);
59565
59756
  }
59566
- return response.users[0];
59757
+ return user;
59567
59758
  },
59568
59759
  listBySourcedIds: async (sourcedIds, batchSize = 50) => {
59569
59760
  if (sourcedIds.length === 0) {
@@ -59845,6 +60036,9 @@ class TimebackCache {
59845
60036
  return;
59846
60037
  }
59847
60038
  const oldestKey = this.accessOrder[0];
60039
+ if (!oldestKey) {
60040
+ return;
60041
+ }
59848
60042
  this.delete(oldestKey);
59849
60043
  log.debug(`[${this.name}] Evicted LRU entry`, { key: oldestKey });
59850
60044
  }
@@ -59901,6 +60095,9 @@ class TimebackCacheManager {
59901
60095
  setEnrollments(studentId, enrollments) {
59902
60096
  this.enrollmentCache.set(studentId, enrollments);
59903
60097
  }
60098
+ clearEnrollments(studentId) {
60099
+ return this.enrollmentCache.delete(studentId);
60100
+ }
59904
60101
  clearAll() {
59905
60102
  this.studentCache.clear();
59906
60103
  this.assessmentLineItemCache.clear();
@@ -59939,6 +60136,41 @@ class MasteryTracker {
59939
60136
  if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
59940
60137
  return;
59941
60138
  }
60139
+ const status = await this.calculateStatus({
60140
+ studentId,
60141
+ courseId,
60142
+ resourceId,
60143
+ additionalMasteredUnits: masteredUnits
60144
+ });
60145
+ if (!status) {
60146
+ return;
60147
+ }
60148
+ return {
60149
+ pctCompleteApp: status.pctCompleteApp,
60150
+ masteryAchieved: status.historicalMasteredUnits < status.masterableUnits && status.isComplete
60151
+ };
60152
+ }
60153
+ async getStatus(input) {
60154
+ const status = await this.calculateStatus({
60155
+ ...input,
60156
+ additionalMasteredUnits: 0
60157
+ });
60158
+ if (!status) {
60159
+ return;
60160
+ }
60161
+ return {
60162
+ masteredUnits: status.masteredUnits,
60163
+ masterableUnits: status.masterableUnits,
60164
+ pctCompleteApp: status.pctCompleteApp,
60165
+ isComplete: status.isComplete
60166
+ };
60167
+ }
60168
+ async calculateStatus({
60169
+ studentId,
60170
+ courseId,
60171
+ resourceId,
60172
+ additionalMasteredUnits
60173
+ }) {
59942
60174
  const masterableUnits = await this.resolveMasterableUnits(resourceId);
59943
60175
  if (!masterableUnits || masterableUnits <= 0) {
59944
60176
  log.warn("[MasteryTracker] No masterableUnits configured for course", {
@@ -59956,11 +60188,16 @@ class MasteryTracker {
59956
60188
  return;
59957
60189
  }
59958
60190
  const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
59959
- const totalMastered = historicalMasteredUnits + masteredUnits;
60191
+ const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
59960
60192
  const rawPct = totalMastered / masterableUnits * 100;
59961
60193
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
59962
- const masteryAchieved = historicalMasteredUnits < masterableUnits && totalMastered >= masterableUnits;
59963
- return { pctCompleteApp, masteryAchieved };
60194
+ return {
60195
+ masteredUnits: totalMastered,
60196
+ masterableUnits,
60197
+ pctCompleteApp,
60198
+ isComplete: totalMastered >= masterableUnits,
60199
+ historicalMasteredUnits
60200
+ };
59964
60201
  }
59965
60202
  async createCompletionEntry(studentId, courseId, classId, appName) {
59966
60203
  const ids = deriveSourcedIds2(courseId);
@@ -60556,6 +60793,7 @@ class TimebackClient {
60556
60793
  progressRecorder;
60557
60794
  sessionRecorder;
60558
60795
  adminEventRecorder;
60796
+ masteryTracker;
60559
60797
  constructor(config2) {
60560
60798
  this.baseUrl = TimebackClient.resolveBaseUrl(config2?.baseUrl);
60561
60799
  this.environment = process.env[ENV_VARS4.environment] === "staging" ? "staging" : "production";
@@ -60573,8 +60811,8 @@ class TimebackClient {
60573
60811
  this.edubridge = createEduBridgeNamespace(this);
60574
60812
  this.cacheManager = new TimebackCacheManager;
60575
60813
  this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
60576
- const masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
60577
- this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, masteryTracker);
60814
+ this.masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
60815
+ this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, this.masteryTracker);
60578
60816
  this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
60579
60817
  this.adminEventRecorder = new AdminEventRecorder(this.studentResolver, this.oneroster, this.caliper, this.environment);
60580
60818
  if (this.credentials) {
@@ -60732,6 +60970,18 @@ class TimebackClient {
60732
60970
  this.cacheManager.setEnrollments(studentId, enrollments);
60733
60971
  return enrollments;
60734
60972
  }
60973
+ invalidateEnrollments(studentId) {
60974
+ this.cacheManager.clearEnrollments(studentId);
60975
+ }
60976
+ async getMasteryStatus(courseId, studentId) {
60977
+ await this._ensureAuthenticated();
60978
+ const ids = deriveSourcedIds2(courseId);
60979
+ return this.masteryTracker.getStatus({
60980
+ studentId,
60981
+ courseId,
60982
+ resourceId: ids.resource
60983
+ });
60984
+ }
60735
60985
  async getStudentXp(studentId, options) {
60736
60986
  await this._ensureAuthenticated();
60737
60987
  const enrollments = await this.edubridge.enrollments.listByUser(studentId);
@@ -63277,7 +63527,7 @@ var colorStatus = async (status) => {
63277
63527
  }
63278
63528
  return `${status}`;
63279
63529
  };
63280
- var logger34 = (fn = console.log) => {
63530
+ var logger35 = (fn = console.log) => {
63281
63531
  return async function logger2(c, next) {
63282
63532
  const { method, url } = c.req;
63283
63533
  const path2 = url.slice(url.indexOf("/", 8));
@@ -63454,7 +63704,7 @@ function createApp(db2, options) {
63454
63704
  const app = new Hono2;
63455
63705
  app.use("*", cors({ origin: "*", credentials: true }));
63456
63706
  if (options.verbose && !options.quiet) {
63457
- app.use("*", logger34());
63707
+ app.use("*", logger35());
63458
63708
  }
63459
63709
  app.use("/api/*", async (c, next) => {
63460
63710
  c.set("db", db2);
@@ -70156,12 +70406,12 @@ var init_session2 = __esm(() => {
70156
70406
  init_utils();
70157
70407
  init_dist5();
70158
70408
  PglitePreparedQuery = class PglitePreparedQuery2 extends PgPreparedQuery {
70159
- constructor(client, queryString, params, logger35, fields, name3, _isResponseInArrayMode, customResultMapper) {
70409
+ constructor(client, queryString, params, logger36, fields, name3, _isResponseInArrayMode, customResultMapper) {
70160
70410
  super({ sql: queryString, params });
70161
70411
  this.client = client;
70162
70412
  this.queryString = queryString;
70163
70413
  this.params = params;
70164
- this.logger = logger35;
70414
+ this.logger = logger36;
70165
70415
  this.fields = fields;
70166
70416
  this._isResponseInArrayMode = _isResponseInArrayMode;
70167
70417
  this.customResultMapper = customResultMapper;
@@ -70263,11 +70513,11 @@ var init_session2 = __esm(() => {
70263
70513
  });
70264
70514
  function construct(client, config2 = {}) {
70265
70515
  const dialect2 = new PgDialect({ casing: config2.casing });
70266
- let logger35;
70516
+ let logger36;
70267
70517
  if (config2.logger === true) {
70268
- logger35 = new DefaultLogger;
70518
+ logger36 = new DefaultLogger;
70269
70519
  } else if (config2.logger !== false) {
70270
- logger35 = config2.logger;
70520
+ logger36 = config2.logger;
70271
70521
  }
70272
70522
  let schema2;
70273
70523
  if (config2.schema) {
@@ -70278,7 +70528,7 @@ function construct(client, config2 = {}) {
70278
70528
  tableNamesMap: tablesConfig.tableNamesMap
70279
70529
  };
70280
70530
  }
70281
- const driver = new PgliteDriver(client, dialect2, { logger: logger35 });
70531
+ const driver = new PgliteDriver(client, dialect2, { logger: logger36 });
70282
70532
  const session2 = driver.createSession(schema2);
70283
70533
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
70284
70534
  db2.$client = client;
@@ -118923,8 +119173,8 @@ var init_currencies = __esm(() => {
118923
119173
  init_tables_index();
118924
119174
  init_constants();
118925
119175
  });
118926
- function setLogger(logger35) {
118927
- customLogger = logger35;
119176
+ function setLogger(logger36) {
119177
+ customLogger = logger36;
118928
119178
  }
118929
119179
  function getLogger() {
118930
119180
  if (customLogger) {
@@ -118937,10 +119187,10 @@ function getLogger() {
118937
119187
  };
118938
119188
  }
118939
119189
  var customLogger;
118940
- var logger35;
119190
+ var logger36;
118941
119191
  var init_adapter = __esm(() => {
118942
119192
  init_config();
118943
- logger35 = {
119193
+ logger36 = {
118944
119194
  info: (msg) => {
118945
119195
  if (customLogger || !config.embedded) {
118946
119196
  getLogger().info(msg);
@@ -119020,7 +119270,7 @@ async function seedCoreGames(db2) {
119020
119270
  try {
119021
119271
  await db2.insert(games).values(gameData).onConflictDoNothing();
119022
119272
  } catch (error2) {
119023
- logger35.error(`Error seeding core game '${gameData.slug}': ${error2}`);
119273
+ logger36.error(`Error seeding core game '${gameData.slug}': ${error2}`);
119024
119274
  }
119025
119275
  }
119026
119276
  }
@@ -119066,7 +119316,7 @@ async function seedCurrentProjectGame(db2, project) {
119066
119316
  }
119067
119317
  return newGame;
119068
119318
  } catch (error2) {
119069
- logger35.error(`❌ Error seeding project game: ${error2}`);
119319
+ logger36.error(`❌ Error seeding project game: ${error2}`);
119070
119320
  throw error2;
119071
119321
  }
119072
119322
  }
@@ -120427,6 +120677,7 @@ var TimebackSubjectSchema;
120427
120677
  var UpdateTimebackXpRequestSchema;
120428
120678
  var TimebackActivityDataSchema;
120429
120679
  var EndActivityRequestSchema;
120680
+ var AdvanceCourseRequestSchema;
120430
120681
  var HeartbeatRequestSchema;
120431
120682
  var PopulateStudentRequestSchema;
120432
120683
  var DerivedPlatformCourseConfigSchema;
@@ -120494,6 +120745,11 @@ var init_schemas11 = __esm(() => {
120494
120745
  masteredUnits: exports_external.number().nonnegative().optional(),
120495
120746
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
120496
120747
  });
120748
+ AdvanceCourseRequestSchema = exports_external.object({
120749
+ gameId: exports_external.string().uuid(),
120750
+ studentId: exports_external.string().min(1),
120751
+ subject: TimebackSubjectSchema.optional()
120752
+ });
120497
120753
  HeartbeatRequestSchema = exports_external.object({
120498
120754
  gameId: exports_external.string().uuid(),
120499
120755
  studentId: exports_external.string().min(1),
@@ -120763,7 +121019,7 @@ async function provisionLtiUser(db2, claims) {
120763
121019
  where: eq(users.id, existingAccount.userId)
120764
121020
  });
120765
121021
  if (user) {
120766
- logger36.info("Found user by LTI account", {
121022
+ logger37.info("Found user by LTI account", {
120767
121023
  userId: user.id,
120768
121024
  ltiTimebackId
120769
121025
  });
@@ -120792,13 +121048,13 @@ async function provisionLtiUser(db2, claims) {
120792
121048
  updatedAt: new Date
120793
121049
  }).returning({ id: accounts.id });
120794
121050
  if (!account) {
120795
- logger36.error("LTI account link insert returned no rows", {
121051
+ logger37.error("LTI account link insert returned no rows", {
120796
121052
  userId: existingUser.id,
120797
121053
  ltiTimebackId
120798
121054
  });
120799
121055
  throw new InternalError("Failed to link LTI account");
120800
121056
  }
120801
- logger36.info("Linked LTI account to existing user", {
121057
+ logger37.info("Linked LTI account to existing user", {
120802
121058
  userId: existingUser.id,
120803
121059
  ltiTimebackId
120804
121060
  });
@@ -120818,7 +121074,7 @@ async function provisionLtiUser(db2, claims) {
120818
121074
  updatedAt: new Date
120819
121075
  }).returning();
120820
121076
  if (!insertedUser) {
120821
- logger36.error("LTI user insert returned no rows", { email, ltiTimebackId });
121077
+ logger37.error("LTI user insert returned no rows", { email, ltiTimebackId });
120822
121078
  throw new InternalError("Failed to create user");
120823
121079
  }
120824
121080
  await tx.insert(accounts).values({
@@ -120833,7 +121089,7 @@ async function provisionLtiUser(db2, claims) {
120833
121089
  createdAt: new Date,
120834
121090
  updatedAt: new Date
120835
121091
  });
120836
- logger36.info("Provisioned new user from LTI", {
121092
+ logger37.info("Provisioned new user from LTI", {
120837
121093
  userId: insertedUser.id,
120838
121094
  ltiTimebackId
120839
121095
  });
@@ -120841,7 +121097,7 @@ async function provisionLtiUser(db2, claims) {
120841
121097
  });
120842
121098
  return createdUser;
120843
121099
  }
120844
- var logger36;
121100
+ var logger37;
120845
121101
  var init_lti_provisioning = __esm(() => {
120846
121102
  init_drizzle_orm();
120847
121103
  init_src();
@@ -120849,7 +121105,7 @@ var init_lti_provisioning = __esm(() => {
120849
121105
  init_src2();
120850
121106
  init_errors();
120851
121107
  init_lti_util();
120852
- logger36 = log.scope("LtiProvisioning");
121108
+ logger37 = log.scope("LtiProvisioning");
120853
121109
  });
120854
121110
  function formatZodError(error2) {
120855
121111
  const flat = error2.flatten();
@@ -120892,7 +121148,7 @@ var init_utils11 = __esm(() => {
120892
121148
  init_timeback_util();
120893
121149
  init_validation_util();
120894
121150
  });
120895
- var logger37;
121151
+ var logger38;
120896
121152
  var listCurrent;
120897
121153
  var listHistory;
120898
121154
  var postProgress;
@@ -120903,14 +121159,14 @@ var init_achievement_controller = __esm(() => {
120903
121159
  init_src2();
120904
121160
  init_errors();
120905
121161
  init_utils11();
120906
- logger37 = log.scope("AchievementController");
121162
+ logger38 = log.scope("AchievementController");
120907
121163
  listCurrent = requireNonAnonymous(async (ctx) => {
120908
- logger37.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
121164
+ logger38.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
120909
121165
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
120910
121166
  });
120911
121167
  listHistory = requireNonAnonymous(async (ctx) => {
120912
121168
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
120913
- logger37.debug("Listing achievement history", { userId: ctx.user.id, limit });
121169
+ logger38.debug("Listing achievement history", { userId: ctx.user.id, limit });
120914
121170
  return ctx.services.achievement.listHistory(ctx.user, limit);
120915
121171
  });
120916
121172
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -120921,12 +121177,12 @@ var init_achievement_controller = __esm(() => {
120921
121177
  } catch (error2) {
120922
121178
  if (error2 instanceof exports_external.ZodError) {
120923
121179
  const details = formatZodError(error2);
120924
- logger37.warn("Submit achievement progress validation failed", { details });
121180
+ logger38.warn("Submit achievement progress validation failed", { details });
120925
121181
  throw ApiError.unprocessableEntity("Invalid request body", details);
120926
121182
  }
120927
121183
  throw ApiError.badRequest("Invalid JSON body");
120928
121184
  }
120929
- logger37.debug("Submitting progress", {
121185
+ logger38.debug("Submitting progress", {
120930
121186
  userId: ctx.user.id,
120931
121187
  achievementId: body2.achievementId
120932
121188
  });
@@ -120938,15 +121194,15 @@ var init_achievement_controller = __esm(() => {
120938
121194
  postProgress
120939
121195
  };
120940
121196
  });
120941
- var logger38;
121197
+ var logger39;
120942
121198
  var getAllowedOrigins;
120943
121199
  var init_admin_controller = __esm(() => {
120944
121200
  init_src2();
120945
121201
  init_utils11();
120946
- logger38 = log.scope("AdminController");
121202
+ logger39 = log.scope("AdminController");
120947
121203
  getAllowedOrigins = requireAdmin(async (ctx) => {
120948
121204
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
120949
- logger38.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
121205
+ logger39.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
120950
121206
  if (shouldRefresh) {
120951
121207
  await ctx.providers.cache.refreshGameOrigins();
120952
121208
  }
@@ -120959,7 +121215,7 @@ var init_admin_controller = __esm(() => {
120959
121215
  };
120960
121216
  });
120961
121217
  });
120962
- var logger39;
121218
+ var logger40;
120963
121219
  var listFiles;
120964
121220
  var getFile;
120965
121221
  var putFile;
@@ -120971,7 +121227,7 @@ var init_bucket_controller = __esm(() => {
120971
121227
  init_src2();
120972
121228
  init_errors();
120973
121229
  init_utils11();
120974
- logger39 = log.scope("BucketController");
121230
+ logger40 = log.scope("BucketController");
120975
121231
  listFiles = requireDeveloper(async (ctx) => {
120976
121232
  const slug2 = ctx.params.slug;
120977
121233
  if (!slug2) {
@@ -120979,7 +121235,7 @@ var init_bucket_controller = __esm(() => {
120979
121235
  }
120980
121236
  const url = ctx.url;
120981
121237
  const prefix2 = url.searchParams.get("prefix") || undefined;
120982
- logger39.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
121238
+ logger40.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
120983
121239
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
120984
121240
  return { files };
120985
121241
  });
@@ -120989,7 +121245,7 @@ var init_bucket_controller = __esm(() => {
120989
121245
  if (!slug2 || !key) {
120990
121246
  throw ApiError.badRequest("Missing game slug or file key");
120991
121247
  }
120992
- logger39.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
121248
+ logger40.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
120993
121249
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
120994
121250
  return new Response(Buffer.from(object.body), {
120995
121251
  status: 200,
@@ -121008,7 +121264,7 @@ var init_bucket_controller = __esm(() => {
121008
121264
  const arrayBuffer = await ctx.request.arrayBuffer();
121009
121265
  const body2 = new Uint8Array(arrayBuffer);
121010
121266
  const contentType = ctx.request.headers.get("content-type") || undefined;
121011
- logger39.debug("Uploading file", {
121267
+ logger40.debug("Uploading file", {
121012
121268
  userId: ctx.user.id,
121013
121269
  slug: slug2,
121014
121270
  key,
@@ -121024,7 +121280,7 @@ var init_bucket_controller = __esm(() => {
121024
121280
  if (!slug2 || !key) {
121025
121281
  throw ApiError.badRequest("Missing game slug or file key");
121026
121282
  }
121027
- logger39.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
121283
+ logger40.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
121028
121284
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
121029
121285
  return { success: true, key };
121030
121286
  });
@@ -121036,12 +121292,12 @@ var init_bucket_controller = __esm(() => {
121036
121292
  } catch (error2) {
121037
121293
  if (error2 instanceof exports_external.ZodError) {
121038
121294
  const details = formatZodError(error2);
121039
- logger39.warn("Initiate upload validation failed", { details });
121295
+ logger40.warn("Initiate upload validation failed", { details });
121040
121296
  throw ApiError.unprocessableEntity("Validation failed", details);
121041
121297
  }
121042
121298
  throw ApiError.badRequest("Invalid JSON body");
121043
121299
  }
121044
- logger39.debug("Initiating multipart upload", {
121300
+ logger40.debug("Initiating multipart upload", {
121045
121301
  userId: ctx.user.id,
121046
121302
  gameId: body2.gameId,
121047
121303
  fileName: body2.fileName
@@ -121056,10 +121312,10 @@ async function listComponents(ctx) {
121056
121312
  if (!isNaN(parsed) && isFinite(parsed)) {
121057
121313
  level = Math.floor(Math.max(0, parsed));
121058
121314
  }
121059
- logger40.debug("Listing components", { level });
121315
+ logger41.debug("Listing components", { level });
121060
121316
  return ctx.services.character.listAvailableComponents(level);
121061
121317
  }
121062
- var logger40;
121318
+ var logger41;
121063
121319
  var get;
121064
121320
  var getByUserId;
121065
121321
  var create;
@@ -121073,9 +121329,9 @@ var init_character_controller = __esm(() => {
121073
121329
  init_src2();
121074
121330
  init_errors();
121075
121331
  init_utils11();
121076
- logger40 = log.scope("CharacterController");
121332
+ logger41 = log.scope("CharacterController");
121077
121333
  get = requireNonAnonymous(async (ctx) => {
121078
- logger40.debug("Getting character", { userId: ctx.user.id });
121334
+ logger41.debug("Getting character", { userId: ctx.user.id });
121079
121335
  return ctx.services.character.getByUser(ctx.user);
121080
121336
  });
121081
121337
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -121083,7 +121339,7 @@ var init_character_controller = __esm(() => {
121083
121339
  if (!userId) {
121084
121340
  throw ApiError.badRequest("User ID is required in the URL path");
121085
121341
  }
121086
- logger40.debug("Getting character by user ID", { requestedUserId: userId });
121342
+ logger41.debug("Getting character by user ID", { requestedUserId: userId });
121087
121343
  return ctx.services.character.getByUserId(userId);
121088
121344
  });
121089
121345
  create = requireNonAnonymous(async (ctx) => {
@@ -121094,12 +121350,12 @@ var init_character_controller = __esm(() => {
121094
121350
  } catch (error2) {
121095
121351
  if (error2 instanceof exports_external.ZodError) {
121096
121352
  const details = formatZodError(error2);
121097
- logger40.warn("Create character validation failed", { details });
121353
+ logger41.warn("Create character validation failed", { details });
121098
121354
  throw ApiError.unprocessableEntity("Invalid request body", details);
121099
121355
  }
121100
121356
  throw ApiError.badRequest("Invalid JSON body");
121101
121357
  }
121102
- logger40.debug("Creating character", {
121358
+ logger41.debug("Creating character", {
121103
121359
  userId: ctx.user.id,
121104
121360
  bodyComponentId: body2.bodyComponentId,
121105
121361
  hairstyleComponentId: body2.hairstyleComponentId
@@ -121114,12 +121370,12 @@ var init_character_controller = __esm(() => {
121114
121370
  } catch (error2) {
121115
121371
  if (error2 instanceof exports_external.ZodError) {
121116
121372
  const details = formatZodError(error2);
121117
- logger40.warn("Update character validation failed", { details });
121373
+ logger41.warn("Update character validation failed", { details });
121118
121374
  throw ApiError.unprocessableEntity("Invalid request body", details);
121119
121375
  }
121120
121376
  throw ApiError.badRequest("Invalid JSON body");
121121
121377
  }
121122
- logger40.debug("Updating character", {
121378
+ logger41.debug("Updating character", {
121123
121379
  userId: ctx.user.id,
121124
121380
  bodyComponentId: body2.bodyComponentId,
121125
121381
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -121135,12 +121391,12 @@ var init_character_controller = __esm(() => {
121135
121391
  } catch (error2) {
121136
121392
  if (error2 instanceof exports_external.ZodError) {
121137
121393
  const details = formatZodError(error2);
121138
- logger40.warn("Equip accessory validation failed", { details });
121394
+ logger41.warn("Equip accessory validation failed", { details });
121139
121395
  throw ApiError.unprocessableEntity("Invalid request body", details);
121140
121396
  }
121141
121397
  throw ApiError.badRequest("Invalid JSON body");
121142
121398
  }
121143
- logger40.debug("Equipping accessory", {
121399
+ logger41.debug("Equipping accessory", {
121144
121400
  userId: ctx.user.id,
121145
121401
  slot: body2.slot,
121146
121402
  accessoryComponentId: body2.accessoryComponentId
@@ -121152,7 +121408,7 @@ var init_character_controller = __esm(() => {
121152
121408
  if (!slot) {
121153
121409
  throw ApiError.badRequest("Slot is required in the URL path");
121154
121410
  }
121155
- logger40.debug("Removing accessory", { userId: ctx.user.id, slot });
121411
+ logger41.debug("Removing accessory", { userId: ctx.user.id, slot });
121156
121412
  await ctx.services.character.removeAccessory(slot, ctx.user);
121157
121413
  return { success: true };
121158
121414
  });
@@ -121166,7 +121422,7 @@ var init_character_controller = __esm(() => {
121166
121422
  removeAccessory
121167
121423
  };
121168
121424
  });
121169
- var logger41;
121425
+ var logger42;
121170
121426
  var list;
121171
121427
  var getById;
121172
121428
  var create2;
@@ -121180,9 +121436,9 @@ var init_currency_controller = __esm(() => {
121180
121436
  init_src4();
121181
121437
  init_errors();
121182
121438
  init_utils11();
121183
- logger41 = log.scope("CurrencyController");
121439
+ logger42 = log.scope("CurrencyController");
121184
121440
  list = requireNonAnonymous(async (ctx) => {
121185
- logger41.debug("Listing currencies", { userId: ctx.user.id });
121441
+ logger42.debug("Listing currencies", { userId: ctx.user.id });
121186
121442
  return ctx.services.currency.list();
121187
121443
  });
121188
121444
  getById = requireNonAnonymous(async (ctx) => {
@@ -121193,7 +121449,7 @@ var init_currency_controller = __esm(() => {
121193
121449
  if (!isValidUUID(currencyId)) {
121194
121450
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
121195
121451
  }
121196
- logger41.debug("Getting currency", { userId: ctx.user.id, currencyId });
121452
+ logger42.debug("Getting currency", { userId: ctx.user.id, currencyId });
121197
121453
  return ctx.services.currency.getById(currencyId);
121198
121454
  });
121199
121455
  create2 = requireAdmin(async (ctx) => {
@@ -121204,12 +121460,12 @@ var init_currency_controller = __esm(() => {
121204
121460
  } catch (error2) {
121205
121461
  if (error2 instanceof exports_external.ZodError) {
121206
121462
  const details = formatZodError(error2);
121207
- logger41.warn("Create currency validation failed", { details });
121463
+ logger42.warn("Create currency validation failed", { details });
121208
121464
  throw ApiError.unprocessableEntity("Validation failed", details);
121209
121465
  }
121210
121466
  throw ApiError.badRequest("Invalid JSON body");
121211
121467
  }
121212
- logger41.debug("Creating currency", {
121468
+ logger42.debug("Creating currency", {
121213
121469
  userId: ctx.user.id,
121214
121470
  symbol: body2.symbol,
121215
121471
  itemId: body2.itemId,
@@ -121232,12 +121488,12 @@ var init_currency_controller = __esm(() => {
121232
121488
  } catch (error2) {
121233
121489
  if (error2 instanceof exports_external.ZodError) {
121234
121490
  const details = formatZodError(error2);
121235
- logger41.warn("Update currency validation failed", { details });
121491
+ logger42.warn("Update currency validation failed", { details });
121236
121492
  throw ApiError.unprocessableEntity("Validation failed", details);
121237
121493
  }
121238
121494
  throw ApiError.badRequest("Invalid JSON body");
121239
121495
  }
121240
- logger41.debug("Updating currency", {
121496
+ logger42.debug("Updating currency", {
121241
121497
  userId: ctx.user.id,
121242
121498
  currencyId,
121243
121499
  symbol: body2.symbol,
@@ -121254,7 +121510,7 @@ var init_currency_controller = __esm(() => {
121254
121510
  if (!isValidUUID(currencyId)) {
121255
121511
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
121256
121512
  }
121257
- logger41.debug("Deleting currency", { userId: ctx.user.id, currencyId });
121513
+ logger42.debug("Deleting currency", { userId: ctx.user.id, currencyId });
121258
121514
  await ctx.services.currency.delete(currencyId);
121259
121515
  });
121260
121516
  currencyController = {
@@ -121265,7 +121521,7 @@ var init_currency_controller = __esm(() => {
121265
121521
  remove
121266
121522
  };
121267
121523
  });
121268
- var logger42;
121524
+ var logger43;
121269
121525
  var reset;
121270
121526
  var init_database_controller = __esm(() => {
121271
121527
  init_esm();
@@ -121273,7 +121529,7 @@ var init_database_controller = __esm(() => {
121273
121529
  init_src2();
121274
121530
  init_errors();
121275
121531
  init_utils11();
121276
- logger42 = log.scope("DatabaseController");
121532
+ logger43 = log.scope("DatabaseController");
121277
121533
  reset = requireDeveloper(async (ctx) => {
121278
121534
  const slug2 = ctx.params.slug;
121279
121535
  if (!slug2) {
@@ -121286,11 +121542,11 @@ var init_database_controller = __esm(() => {
121286
121542
  } catch (error2) {
121287
121543
  if (error2 instanceof exports_external.ZodError) {
121288
121544
  const details = formatZodError(error2);
121289
- logger42.warn("Database reset validation failed", { details });
121545
+ logger43.warn("Database reset validation failed", { details });
121290
121546
  throw ApiError.unprocessableEntity("Validation failed", details);
121291
121547
  }
121292
121548
  }
121293
- logger42.debug("Resetting database", {
121549
+ logger43.debug("Resetting database", {
121294
121550
  userId: ctx.user.id,
121295
121551
  slug: slug2,
121296
121552
  hasSchema: Boolean(body2.schema)
@@ -121306,7 +121562,7 @@ async function createJob(ctx) {
121306
121562
  let body2;
121307
121563
  try {
121308
121564
  const json4 = await ctx.request.json();
121309
- logger43.debug("Deploy request body", {
121565
+ logger44.debug("Deploy request body", {
121310
121566
  keys: Object.keys(json4 || {}),
121311
121567
  hasUploadToken: Boolean(json4?.uploadToken),
121312
121568
  hasCode: Boolean(json4?.code),
@@ -121316,7 +121572,7 @@ async function createJob(ctx) {
121316
121572
  } catch (error2) {
121317
121573
  if (error2 instanceof exports_external.ZodError) {
121318
121574
  const details = formatZodError(error2);
121319
- logger43.warn("Deploy validation failed", { details });
121575
+ logger44.warn("Deploy validation failed", { details });
121320
121576
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
121321
121577
  }
121322
121578
  throw ApiError.badRequest("Invalid JSON body");
@@ -121337,7 +121593,7 @@ async function getJob(ctx) {
121337
121593
  }
121338
121594
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
121339
121595
  }
121340
- var logger43;
121596
+ var logger44;
121341
121597
  var deploy;
121342
121598
  var init_deploy_controller = __esm(() => {
121343
121599
  init_esm();
@@ -121345,26 +121601,26 @@ var init_deploy_controller = __esm(() => {
121345
121601
  init_src2();
121346
121602
  init_errors();
121347
121603
  init_utils11();
121348
- logger43 = log.scope("DeployController");
121604
+ logger44 = log.scope("DeployController");
121349
121605
  deploy = {
121350
121606
  createJob: requireDeveloper(createJob),
121351
121607
  getJob: requireDeveloper(getJob)
121352
121608
  };
121353
121609
  });
121354
- var logger44;
121610
+ var logger45;
121355
121611
  var apply;
121356
121612
  var getStatus;
121357
121613
  var developer;
121358
121614
  var init_developer_controller = __esm(() => {
121359
121615
  init_src2();
121360
121616
  init_utils11();
121361
- logger44 = log.scope("DeveloperController");
121617
+ logger45 = log.scope("DeveloperController");
121362
121618
  apply = requireNonAnonymous(async (ctx) => {
121363
- logger44.debug("Applying for developer status", { userId: ctx.user.id });
121619
+ logger45.debug("Applying for developer status", { userId: ctx.user.id });
121364
121620
  await ctx.services.developer.apply(ctx.user);
121365
121621
  });
121366
121622
  getStatus = requireNonAnonymous(async (ctx) => {
121367
- logger44.debug("Getting developer status", { userId: ctx.user.id });
121623
+ logger45.debug("Getting developer status", { userId: ctx.user.id });
121368
121624
  const status = await ctx.services.developer.getStatus(ctx.user.id);
121369
121625
  return { status };
121370
121626
  });
@@ -121373,7 +121629,7 @@ var init_developer_controller = __esm(() => {
121373
121629
  getStatus
121374
121630
  };
121375
121631
  });
121376
- var logger45;
121632
+ var logger46;
121377
121633
  var add;
121378
121634
  var list2;
121379
121635
  var getStatus2;
@@ -121386,7 +121642,7 @@ var init_domain_controller = __esm(() => {
121386
121642
  init_config2();
121387
121643
  init_errors();
121388
121644
  init_utils11();
121389
- logger45 = log.scope("DomainController");
121645
+ logger46 = log.scope("DomainController");
121390
121646
  add = requireDeveloper(async (ctx) => {
121391
121647
  const slug2 = ctx.params.slug;
121392
121648
  if (!slug2) {
@@ -121399,13 +121655,13 @@ var init_domain_controller = __esm(() => {
121399
121655
  } catch (error2) {
121400
121656
  if (error2 instanceof exports_external.ZodError) {
121401
121657
  const details = formatZodError(error2);
121402
- logger45.warn("Add domain validation failed", { details });
121658
+ logger46.warn("Add domain validation failed", { details });
121403
121659
  throw ApiError.unprocessableEntity("Validation failed", details);
121404
121660
  }
121405
121661
  throw ApiError.badRequest("Invalid JSON body");
121406
121662
  }
121407
121663
  const environment = getPlatformEnvironment(ctx.config);
121408
- logger45.debug("Adding domain", {
121664
+ logger46.debug("Adding domain", {
121409
121665
  userId: ctx.user.id,
121410
121666
  slug: slug2,
121411
121667
  hostname: body2.hostname,
@@ -121419,7 +121675,7 @@ var init_domain_controller = __esm(() => {
121419
121675
  throw ApiError.badRequest("Missing game slug");
121420
121676
  }
121421
121677
  const environment = getPlatformEnvironment(ctx.config);
121422
- logger45.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
121678
+ logger46.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
121423
121679
  const domains22 = await ctx.services.domain.list(slug2, environment, ctx.user);
121424
121680
  return { domains: domains22 };
121425
121681
  });
@@ -121434,7 +121690,7 @@ var init_domain_controller = __esm(() => {
121434
121690
  }
121435
121691
  const refresh = ctx.url.searchParams.get("refresh") === "true";
121436
121692
  const environment = getPlatformEnvironment(ctx.config);
121437
- logger45.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
121693
+ logger46.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
121438
121694
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
121439
121695
  });
121440
121696
  remove2 = requireDeveloper(async (ctx) => {
@@ -121447,7 +121703,7 @@ var init_domain_controller = __esm(() => {
121447
121703
  throw ApiError.badRequest("Missing hostname");
121448
121704
  }
121449
121705
  const environment = getPlatformEnvironment(ctx.config);
121450
- logger45.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
121706
+ logger46.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
121451
121707
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
121452
121708
  });
121453
121709
  domains2 = {
@@ -121457,7 +121713,7 @@ var init_domain_controller = __esm(() => {
121457
121713
  remove: remove2
121458
121714
  };
121459
121715
  });
121460
- var logger46;
121716
+ var logger47;
121461
121717
  var list3;
121462
121718
  var listManageable;
121463
121719
  var getSubjects;
@@ -121474,17 +121730,17 @@ var init_game_controller = __esm(() => {
121474
121730
  init_src4();
121475
121731
  init_errors();
121476
121732
  init_utils11();
121477
- logger46 = log.scope("GameController");
121733
+ logger47 = log.scope("GameController");
121478
121734
  list3 = requireNonAnonymous(async (ctx) => {
121479
- logger46.debug("Listing games", { userId: ctx.user.id });
121735
+ logger47.debug("Listing games", { userId: ctx.user.id });
121480
121736
  return ctx.services.game.list(ctx.user);
121481
121737
  });
121482
121738
  listManageable = requireNonAnonymous(async (ctx) => {
121483
- logger46.debug("Listing manageable games", { userId: ctx.user.id });
121739
+ logger47.debug("Listing manageable games", { userId: ctx.user.id });
121484
121740
  return ctx.services.game.listManageable(ctx.user);
121485
121741
  });
121486
121742
  getSubjects = requireNonAnonymous(async (ctx) => {
121487
- logger46.debug("Getting game subjects", { userId: ctx.user.id });
121743
+ logger47.debug("Getting game subjects", { userId: ctx.user.id });
121488
121744
  return ctx.services.game.getSubjects();
121489
121745
  });
121490
121746
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -121495,7 +121751,7 @@ var init_game_controller = __esm(() => {
121495
121751
  if (!isValidUUID(gameId)) {
121496
121752
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121497
121753
  }
121498
- logger46.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
121754
+ logger47.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
121499
121755
  return ctx.services.game.getById(gameId, ctx.user);
121500
121756
  });
121501
121757
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -121503,7 +121759,7 @@ var init_game_controller = __esm(() => {
121503
121759
  if (!slug2) {
121504
121760
  throw ApiError.badRequest("Missing game slug");
121505
121761
  }
121506
- logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
121762
+ logger47.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
121507
121763
  return ctx.services.game.getBySlug(slug2, ctx.user);
121508
121764
  });
121509
121765
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -121514,7 +121770,7 @@ var init_game_controller = __esm(() => {
121514
121770
  if (!isValidUUID(gameId)) {
121515
121771
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121516
121772
  }
121517
- logger46.debug("Getting game manifest by ID", {
121773
+ logger47.debug("Getting game manifest by ID", {
121518
121774
  userId: ctx.user.id,
121519
121775
  gameId,
121520
121776
  launchId: ctx.launchId
@@ -121533,12 +121789,12 @@ var init_game_controller = __esm(() => {
121533
121789
  } catch (error2) {
121534
121790
  if (error2 instanceof exports_external.ZodError) {
121535
121791
  const details = formatZodError(error2);
121536
- logger46.warn("Upsert game validation failed", { details });
121792
+ logger47.warn("Upsert game validation failed", { details });
121537
121793
  throw ApiError.unprocessableEntity("Validation failed", details);
121538
121794
  }
121539
121795
  throw ApiError.badRequest("Invalid JSON body");
121540
121796
  }
121541
- logger46.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
121797
+ logger47.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
121542
121798
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
121543
121799
  });
121544
121800
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -121549,7 +121805,7 @@ var init_game_controller = __esm(() => {
121549
121805
  if (!isValidUUID(gameId)) {
121550
121806
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121551
121807
  }
121552
- logger46.debug("Deleting game", { userId: ctx.user.id, gameId });
121808
+ logger47.debug("Deleting game", { userId: ctx.user.id, gameId });
121553
121809
  await ctx.services.game.delete(gameId, ctx.user);
121554
121810
  });
121555
121811
  games2 = {
@@ -121563,7 +121819,7 @@ var init_game_controller = __esm(() => {
121563
121819
  remove: remove3
121564
121820
  };
121565
121821
  });
121566
- var logger47;
121822
+ var logger48;
121567
121823
  var list4;
121568
121824
  var addItem;
121569
121825
  var removeItem;
@@ -121574,9 +121830,9 @@ var init_inventory_controller = __esm(() => {
121574
121830
  init_src2();
121575
121831
  init_errors();
121576
121832
  init_utils11();
121577
- logger47 = log.scope("InventoryController");
121833
+ logger48 = log.scope("InventoryController");
121578
121834
  list4 = requireNonAnonymous(async (ctx) => {
121579
- logger47.debug("Listing inventory", { userId: ctx.user.id });
121835
+ logger48.debug("Listing inventory", { userId: ctx.user.id });
121580
121836
  return ctx.services.inventory.list(ctx.user);
121581
121837
  });
121582
121838
  addItem = requireNonAnonymous(async (ctx) => {
@@ -121587,12 +121843,12 @@ var init_inventory_controller = __esm(() => {
121587
121843
  } catch (error2) {
121588
121844
  if (error2 instanceof exports_external.ZodError) {
121589
121845
  const details = formatZodError(error2);
121590
- logger47.warn("Add inventory item validation failed", { details });
121846
+ logger48.warn("Add inventory item validation failed", { details });
121591
121847
  throw ApiError.unprocessableEntity("Invalid request body", details);
121592
121848
  }
121593
121849
  throw ApiError.badRequest("Invalid JSON body");
121594
121850
  }
121595
- logger47.debug("Adding item", {
121851
+ logger48.debug("Adding item", {
121596
121852
  userId: ctx.user.id,
121597
121853
  itemId: body2.itemId,
121598
121854
  qty: body2.qty
@@ -121607,12 +121863,12 @@ var init_inventory_controller = __esm(() => {
121607
121863
  } catch (error2) {
121608
121864
  if (error2 instanceof exports_external.ZodError) {
121609
121865
  const details = formatZodError(error2);
121610
- logger47.warn("Remove inventory item validation failed", { details });
121866
+ logger48.warn("Remove inventory item validation failed", { details });
121611
121867
  throw ApiError.unprocessableEntity("Invalid request body", details);
121612
121868
  }
121613
121869
  throw ApiError.badRequest("Invalid JSON body");
121614
121870
  }
121615
- logger47.debug("Removing item", {
121871
+ logger48.debug("Removing item", {
121616
121872
  userId: ctx.user.id,
121617
121873
  itemId: body2.itemId,
121618
121874
  qty: body2.qty
@@ -121625,7 +121881,7 @@ var init_inventory_controller = __esm(() => {
121625
121881
  removeItem
121626
121882
  };
121627
121883
  });
121628
- var logger48;
121884
+ var logger49;
121629
121885
  var list5;
121630
121886
  var getById3;
121631
121887
  var resolve2;
@@ -121644,10 +121900,10 @@ var init_item_controller = __esm(() => {
121644
121900
  init_src4();
121645
121901
  init_errors();
121646
121902
  init_utils11();
121647
- logger48 = log.scope("ItemController");
121903
+ logger49 = log.scope("ItemController");
121648
121904
  list5 = requireNonAnonymous(async (ctx) => {
121649
121905
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
121650
- logger48.debug("Listing items", { userId: ctx.user.id, gameId });
121906
+ logger49.debug("Listing items", { userId: ctx.user.id, gameId });
121651
121907
  return ctx.services.item.list(gameId);
121652
121908
  });
121653
121909
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -121658,7 +121914,7 @@ var init_item_controller = __esm(() => {
121658
121914
  if (!isValidUUID(itemId)) {
121659
121915
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121660
121916
  }
121661
- logger48.debug("Getting item", { userId: ctx.user.id, itemId });
121917
+ logger49.debug("Getting item", { userId: ctx.user.id, itemId });
121662
121918
  return ctx.services.item.getById(itemId);
121663
121919
  });
121664
121920
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -121670,7 +121926,7 @@ var init_item_controller = __esm(() => {
121670
121926
  if (gameId && !isValidUUID(gameId)) {
121671
121927
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121672
121928
  }
121673
- logger48.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
121929
+ logger49.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
121674
121930
  return ctx.services.item.resolveBySlug(slug2, gameId);
121675
121931
  });
121676
121932
  create3 = requireRole(["admin"], async (ctx) => {
@@ -121681,12 +121937,12 @@ var init_item_controller = __esm(() => {
121681
121937
  } catch (error2) {
121682
121938
  if (error2 instanceof exports_external.ZodError) {
121683
121939
  const details = formatZodError(error2);
121684
- logger48.warn("Create item validation failed", { details });
121940
+ logger49.warn("Create item validation failed", { details });
121685
121941
  throw ApiError.unprocessableEntity("Validation failed", details);
121686
121942
  }
121687
121943
  throw ApiError.badRequest("Invalid JSON body");
121688
121944
  }
121689
- logger48.debug("Creating item", {
121945
+ logger49.debug("Creating item", {
121690
121946
  userId: ctx.user.id,
121691
121947
  slug: body2.slug,
121692
121948
  displayName: body2.displayName
@@ -121708,7 +121964,7 @@ var init_item_controller = __esm(() => {
121708
121964
  } catch (error2) {
121709
121965
  if (error2 instanceof exports_external.ZodError) {
121710
121966
  const details = formatZodError(error2);
121711
- logger48.warn("Update item validation failed", { details });
121967
+ logger49.warn("Update item validation failed", { details });
121712
121968
  throw ApiError.unprocessableEntity("Validation failed", details);
121713
121969
  }
121714
121970
  throw ApiError.badRequest("Invalid JSON body");
@@ -121716,7 +121972,7 @@ var init_item_controller = __esm(() => {
121716
121972
  if (Object.keys(body2).length === 0) {
121717
121973
  throw ApiError.badRequest("No update data provided");
121718
121974
  }
121719
- logger48.debug("Updating item", {
121975
+ logger49.debug("Updating item", {
121720
121976
  userId: ctx.user.id,
121721
121977
  itemId,
121722
121978
  slug: body2.slug,
@@ -121733,7 +121989,7 @@ var init_item_controller = __esm(() => {
121733
121989
  if (!isValidUUID(itemId)) {
121734
121990
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121735
121991
  }
121736
- logger48.debug("Deleting item", { userId: ctx.user.id, itemId });
121992
+ logger49.debug("Deleting item", { userId: ctx.user.id, itemId });
121737
121993
  await ctx.services.item.delete(itemId);
121738
121994
  });
121739
121995
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -121744,7 +122000,7 @@ var init_item_controller = __esm(() => {
121744
122000
  if (!isValidUUID(gameId)) {
121745
122001
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121746
122002
  }
121747
- logger48.debug("Listing game items", { userId: ctx.user.id, gameId });
122003
+ logger49.debug("Listing game items", { userId: ctx.user.id, gameId });
121748
122004
  return ctx.services.item.listByGame(gameId);
121749
122005
  });
121750
122006
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -121762,12 +122018,12 @@ var init_item_controller = __esm(() => {
121762
122018
  } catch (error2) {
121763
122019
  if (error2 instanceof exports_external.ZodError) {
121764
122020
  const details = formatZodError(error2);
121765
- logger48.warn("Create game item validation failed", { details });
122021
+ logger49.warn("Create game item validation failed", { details });
121766
122022
  throw ApiError.unprocessableEntity("Validation failed", details);
121767
122023
  }
121768
122024
  throw ApiError.badRequest("Invalid JSON body");
121769
122025
  }
121770
- logger48.debug("Creating game item", {
122026
+ logger49.debug("Creating game item", {
121771
122027
  userId: ctx.user.id,
121772
122028
  gameId,
121773
122029
  slug: body2.slug,
@@ -121794,7 +122050,7 @@ var init_item_controller = __esm(() => {
121794
122050
  } catch (error2) {
121795
122051
  if (error2 instanceof exports_external.ZodError) {
121796
122052
  const details = formatZodError(error2);
121797
- logger48.warn("Update game item validation failed", { details });
122053
+ logger49.warn("Update game item validation failed", { details });
121798
122054
  throw ApiError.unprocessableEntity("Validation failed", details);
121799
122055
  }
121800
122056
  throw ApiError.badRequest("Invalid JSON body");
@@ -121802,7 +122058,7 @@ var init_item_controller = __esm(() => {
121802
122058
  if (Object.keys(body2).length === 0) {
121803
122059
  throw ApiError.badRequest("No update data provided");
121804
122060
  }
121805
- logger48.debug("Updating game item", {
122061
+ logger49.debug("Updating game item", {
121806
122062
  userId: ctx.user.id,
121807
122063
  gameId,
121808
122064
  itemId,
@@ -121824,7 +122080,7 @@ var init_item_controller = __esm(() => {
121824
122080
  if (!isValidUUID(itemId)) {
121825
122081
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121826
122082
  }
121827
- logger48.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
122083
+ logger49.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
121828
122084
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
121829
122085
  });
121830
122086
  items2 = {
@@ -121840,7 +122096,7 @@ var init_item_controller = __esm(() => {
121840
122096
  deleteForGame
121841
122097
  };
121842
122098
  });
121843
- var logger49;
122099
+ var logger50;
121844
122100
  var listKeys;
121845
122101
  var getStats;
121846
122102
  var seed;
@@ -121855,7 +122111,7 @@ var init_kv_controller = __esm(() => {
121855
122111
  init_src2();
121856
122112
  init_errors();
121857
122113
  init_utils11();
121858
- logger49 = log.scope("KVController");
122114
+ logger50 = log.scope("KVController");
121859
122115
  listKeys = requireDeveloper(async (ctx) => {
121860
122116
  const slug2 = ctx.params.slug;
121861
122117
  if (!slug2) {
@@ -121863,7 +122119,7 @@ var init_kv_controller = __esm(() => {
121863
122119
  }
121864
122120
  const url = ctx.url;
121865
122121
  const prefix2 = url.searchParams.get("prefix") || undefined;
121866
- logger49.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122122
+ logger50.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
121867
122123
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
121868
122124
  return { keys };
121869
122125
  });
@@ -121872,7 +122128,7 @@ var init_kv_controller = __esm(() => {
121872
122128
  if (!slug2) {
121873
122129
  throw ApiError.badRequest("Missing game slug");
121874
122130
  }
121875
- logger49.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
122131
+ logger50.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
121876
122132
  return ctx.services.kv.getStats(slug2, ctx.user);
121877
122133
  });
121878
122134
  seed = requireDeveloper(async (ctx) => {
@@ -121887,12 +122143,12 @@ var init_kv_controller = __esm(() => {
121887
122143
  } catch (error2) {
121888
122144
  if (error2 instanceof exports_external.ZodError) {
121889
122145
  const details = formatZodError(error2);
121890
- logger49.warn("Seed validation failed", { details });
122146
+ logger50.warn("Seed validation failed", { details });
121891
122147
  throw ApiError.unprocessableEntity("Validation failed", details);
121892
122148
  }
121893
122149
  throw ApiError.badRequest("Invalid JSON body");
121894
122150
  }
121895
- logger49.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
122151
+ logger50.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
121896
122152
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
121897
122153
  return { success: true, count: body2.entries.length };
121898
122154
  });
@@ -121902,7 +122158,7 @@ var init_kv_controller = __esm(() => {
121902
122158
  if (!slug2 || !key) {
121903
122159
  throw ApiError.badRequest("Missing game slug or key");
121904
122160
  }
121905
- logger49.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
122161
+ logger50.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
121906
122162
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
121907
122163
  return { key, value };
121908
122164
  });
@@ -121916,7 +122172,7 @@ var init_kv_controller = __esm(() => {
121916
122172
  if (!value) {
121917
122173
  throw ApiError.badRequest("Missing value in request body");
121918
122174
  }
121919
- logger49.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
122175
+ logger50.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
121920
122176
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
121921
122177
  return { success: true, key };
121922
122178
  });
@@ -121926,7 +122182,7 @@ var init_kv_controller = __esm(() => {
121926
122182
  if (!slug2 || !key) {
121927
122183
  throw ApiError.badRequest("Missing game slug or key");
121928
122184
  }
121929
- logger49.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
122185
+ logger50.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
121930
122186
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
121931
122187
  return { success: true, key };
121932
122188
  });
@@ -121936,7 +122192,7 @@ var init_kv_controller = __esm(() => {
121936
122192
  if (!slug2 || !key) {
121937
122193
  throw ApiError.badRequest("Missing game slug or key");
121938
122194
  }
121939
- logger49.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
122195
+ logger50.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
121940
122196
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
121941
122197
  return { key, metadata: metadata2 };
121942
122198
  });
@@ -121945,12 +122201,12 @@ var init_kv_controller = __esm(() => {
121945
122201
  if (!slug2) {
121946
122202
  throw ApiError.badRequest("Missing game slug");
121947
122203
  }
121948
- logger49.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
122204
+ logger50.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
121949
122205
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
121950
122206
  return { success: true, deleted };
121951
122207
  });
121952
122208
  });
121953
- var logger50;
122209
+ var logger51;
121954
122210
  var submitScore;
121955
122211
  var getGlobalLeaderboard;
121956
122212
  var getLeaderboard;
@@ -121964,7 +122220,7 @@ var init_leaderboard_controller = __esm(() => {
121964
122220
  init_src2();
121965
122221
  init_errors();
121966
122222
  init_utils11();
121967
- logger50 = log.scope("LeaderboardController");
122223
+ logger51 = log.scope("LeaderboardController");
121968
122224
  submitScore = requireAuth(async (ctx) => {
121969
122225
  const gameId = ctx.params.gameId;
121970
122226
  if (!gameId) {
@@ -121977,12 +122233,12 @@ var init_leaderboard_controller = __esm(() => {
121977
122233
  } catch (error2) {
121978
122234
  if (error2 instanceof exports_external.ZodError) {
121979
122235
  const details = formatZodError(error2);
121980
- logger50.warn("Submit score validation failed", { details });
122236
+ logger51.warn("Submit score validation failed", { details });
121981
122237
  throw ApiError.unprocessableEntity("Validation failed", details);
121982
122238
  }
121983
122239
  throw ApiError.badRequest("Invalid JSON body");
121984
122240
  }
121985
- logger50.debug("Submitting score", {
122241
+ logger51.debug("Submitting score", {
121986
122242
  userId: ctx.user.id,
121987
122243
  gameId,
121988
122244
  score: body2.score
@@ -122005,12 +122261,12 @@ var init_leaderboard_controller = __esm(() => {
122005
122261
  } catch (error2) {
122006
122262
  if (error2 instanceof exports_external.ZodError) {
122007
122263
  const details = formatZodError(error2);
122008
- logger50.warn("Get global leaderboard query validation failed", { details });
122264
+ logger51.warn("Get global leaderboard query validation failed", { details });
122009
122265
  throw ApiError.badRequest("Invalid query parameters", details);
122010
122266
  }
122011
122267
  throw ApiError.badRequest("Invalid query parameters");
122012
122268
  }
122013
- logger50.debug("Getting global leaderboard", {
122269
+ logger51.debug("Getting global leaderboard", {
122014
122270
  userId: ctx.user.id,
122015
122271
  gameId,
122016
122272
  ...query
@@ -122033,12 +122289,12 @@ var init_leaderboard_controller = __esm(() => {
122033
122289
  } catch (error2) {
122034
122290
  if (error2 instanceof exports_external.ZodError) {
122035
122291
  const details = formatZodError(error2);
122036
- logger50.warn("Get leaderboard query validation failed", { details });
122292
+ logger51.warn("Get leaderboard query validation failed", { details });
122037
122293
  throw ApiError.badRequest("Invalid query parameters", details);
122038
122294
  }
122039
122295
  throw ApiError.badRequest("Invalid query parameters");
122040
122296
  }
122041
- logger50.debug("Getting leaderboard", {
122297
+ logger51.debug("Getting leaderboard", {
122042
122298
  userId: ctx.user.id,
122043
122299
  gameId,
122044
122300
  ...query
@@ -122050,7 +122306,7 @@ var init_leaderboard_controller = __esm(() => {
122050
122306
  if (!gameId || !userId) {
122051
122307
  throw ApiError.badRequest("Game ID and User ID are required");
122052
122308
  }
122053
- logger50.debug("Getting user rank", {
122309
+ logger51.debug("Getting user rank", {
122054
122310
  requesterId: ctx.user.id,
122055
122311
  gameId,
122056
122312
  targetUserId: userId
@@ -122065,7 +122321,7 @@ var init_leaderboard_controller = __esm(() => {
122065
122321
  const url = ctx.url;
122066
122322
  const limit = Math.min(Number(url.searchParams.get("limit") || "50"), 100);
122067
122323
  const gameId = url.searchParams.get("gameId") || undefined;
122068
- logger50.debug("Getting user all scores", {
122324
+ logger51.debug("Getting user all scores", {
122069
122325
  requesterId: ctx.user.id,
122070
122326
  targetUserId: userId,
122071
122327
  gameId,
@@ -122080,7 +122336,7 @@ var init_leaderboard_controller = __esm(() => {
122080
122336
  }
122081
122337
  const url = ctx.url;
122082
122338
  const limit = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
122083
- logger50.debug("Getting user scores", {
122339
+ logger51.debug("Getting user scores", {
122084
122340
  requesterId: ctx.user.id,
122085
122341
  gameId,
122086
122342
  targetUserId: userId,
@@ -122098,7 +122354,7 @@ var init_leaderboard_controller = __esm(() => {
122098
122354
  };
122099
122355
  });
122100
122356
  async function listConfigs(ctx) {
122101
- logger51.debug("Listing level configs");
122357
+ logger52.debug("Listing level configs");
122102
122358
  return ctx.services.level.listConfigs();
122103
122359
  }
122104
122360
  async function getConfig(ctx) {
@@ -122110,10 +122366,10 @@ async function getConfig(ctx) {
122110
122366
  if (isNaN(level) || level < 1) {
122111
122367
  throw ApiError.badRequest("Level must be a positive integer");
122112
122368
  }
122113
- logger51.debug("Getting level config", { level });
122369
+ logger52.debug("Getting level config", { level });
122114
122370
  return ctx.services.level.getConfig(level);
122115
122371
  }
122116
- var logger51;
122372
+ var logger52;
122117
122373
  var getByUser;
122118
122374
  var getProgress;
122119
122375
  var levels;
@@ -122121,13 +122377,13 @@ var init_level_controller = __esm(() => {
122121
122377
  init_src2();
122122
122378
  init_errors();
122123
122379
  init_utils11();
122124
- logger51 = log.scope("LevelController");
122380
+ logger52 = log.scope("LevelController");
122125
122381
  getByUser = requireNonAnonymous(async (ctx) => {
122126
- logger51.debug("Getting user level", { userId: ctx.user.id });
122382
+ logger52.debug("Getting user level", { userId: ctx.user.id });
122127
122383
  return ctx.services.level.getByUser(ctx.user);
122128
122384
  });
122129
122385
  getProgress = requireNonAnonymous(async (ctx) => {
122130
- logger51.debug("Getting level progress", { userId: ctx.user.id });
122386
+ logger52.debug("Getting level progress", { userId: ctx.user.id });
122131
122387
  return ctx.services.level.getProgress(ctx.user);
122132
122388
  });
122133
122389
  levels = {
@@ -122137,14 +122393,14 @@ var init_level_controller = __esm(() => {
122137
122393
  getProgress
122138
122394
  };
122139
122395
  });
122140
- var logger52;
122396
+ var logger53;
122141
122397
  var generateToken;
122142
122398
  var logs;
122143
122399
  var init_logs_controller = __esm(() => {
122144
122400
  init_src2();
122145
122401
  init_errors();
122146
122402
  init_utils11();
122147
- logger52 = log.scope("LogsController");
122403
+ logger53 = log.scope("LogsController");
122148
122404
  generateToken = requireDeveloper(async (ctx) => {
122149
122405
  const slug2 = ctx.params.slug;
122150
122406
  if (!slug2) {
@@ -122163,7 +122419,7 @@ var init_logs_controller = __esm(() => {
122163
122419
  }
122164
122420
  throw ApiError.badRequest("Invalid JSON body");
122165
122421
  }
122166
- logger52.debug("Generating log stream token", {
122422
+ logger53.debug("Generating log stream token", {
122167
122423
  userId: ctx.user.id,
122168
122424
  slug: slug2,
122169
122425
  environment: body2.environment
@@ -122174,22 +122430,22 @@ var init_logs_controller = __esm(() => {
122174
122430
  generateToken
122175
122431
  };
122176
122432
  });
122177
- var logger53;
122433
+ var logger54;
122178
122434
  var getStatus3;
122179
122435
  var lti;
122180
122436
  var init_lti_controller = __esm(() => {
122181
122437
  init_src2();
122182
122438
  init_utils11();
122183
- logger53 = log.scope("LtiController");
122439
+ logger54 = log.scope("LtiController");
122184
122440
  getStatus3 = requireNonAnonymous(async (ctx) => {
122185
- logger53.debug("Getting status", { userId: ctx.user.id });
122441
+ logger54.debug("Getting status", { userId: ctx.user.id });
122186
122442
  return ctx.services.lti.getStatus(ctx.user);
122187
122443
  });
122188
122444
  lti = {
122189
122445
  getStatus: getStatus3
122190
122446
  };
122191
122447
  });
122192
- var logger54;
122448
+ var logger55;
122193
122449
  var getByIdentifier;
122194
122450
  var getElements;
122195
122451
  var getObjects;
@@ -122203,13 +122459,13 @@ var init_map_controller = __esm(() => {
122203
122459
  init_src4();
122204
122460
  init_errors();
122205
122461
  init_utils11();
122206
- logger54 = log.scope("MapController");
122462
+ logger55 = log.scope("MapController");
122207
122463
  getByIdentifier = requireNonAnonymous(async (ctx) => {
122208
122464
  const identifier = ctx.params.identifier;
122209
122465
  if (!identifier) {
122210
122466
  throw ApiError.badRequest("Missing map identifier");
122211
122467
  }
122212
- logger54.debug("Getting map", { userId: ctx.user.id, identifier });
122468
+ logger55.debug("Getting map", { userId: ctx.user.id, identifier });
122213
122469
  return ctx.services.map.getByIdentifier(identifier);
122214
122470
  });
122215
122471
  getElements = requireNonAnonymous(async (ctx) => {
@@ -122220,7 +122476,7 @@ var init_map_controller = __esm(() => {
122220
122476
  if (!isValidUUID(mapId)) {
122221
122477
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
122222
122478
  }
122223
- logger54.debug("Getting map elements", { userId: ctx.user.id, mapId });
122479
+ logger55.debug("Getting map elements", { userId: ctx.user.id, mapId });
122224
122480
  return ctx.services.map.getElements(mapId);
122225
122481
  });
122226
122482
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -122231,7 +122487,7 @@ var init_map_controller = __esm(() => {
122231
122487
  if (!isValidUUID(mapId)) {
122232
122488
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
122233
122489
  }
122234
- logger54.debug("Getting map objects", { userId: ctx.user.id, mapId });
122490
+ logger55.debug("Getting map objects", { userId: ctx.user.id, mapId });
122235
122491
  return ctx.services.map.getObjects(mapId, ctx.user.id);
122236
122492
  });
122237
122493
  createObject = requireNonAnonymous(async (ctx) => {
@@ -122253,12 +122509,12 @@ var init_map_controller = __esm(() => {
122253
122509
  } catch (error2) {
122254
122510
  if (error2 instanceof exports_external.ZodError) {
122255
122511
  const details = formatZodError(error2);
122256
- logger54.warn("Create map object validation failed", { details });
122512
+ logger55.warn("Create map object validation failed", { details });
122257
122513
  throw ApiError.unprocessableEntity("Validation failed", details);
122258
122514
  }
122259
122515
  throw ApiError.badRequest("Invalid JSON body");
122260
122516
  }
122261
- logger54.debug("Creating map object", {
122517
+ logger55.debug("Creating map object", {
122262
122518
  userId: ctx.user.id,
122263
122519
  mapId,
122264
122520
  itemId: body2.itemId,
@@ -122282,7 +122538,7 @@ var init_map_controller = __esm(() => {
122282
122538
  if (!isValidUUID(objectId)) {
122283
122539
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
122284
122540
  }
122285
- logger54.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
122541
+ logger55.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
122286
122542
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
122287
122543
  });
122288
122544
  maps2 = {
@@ -122293,7 +122549,7 @@ var init_map_controller = __esm(() => {
122293
122549
  deleteObject
122294
122550
  };
122295
122551
  });
122296
- var logger55;
122552
+ var logger56;
122297
122553
  var list6;
122298
122554
  var updateStatus;
122299
122555
  var getStats2;
@@ -122306,7 +122562,7 @@ var init_notification_controller = __esm(() => {
122306
122562
  init_src2();
122307
122563
  init_errors();
122308
122564
  init_utils11();
122309
- logger55 = log.scope("NotificationController");
122565
+ logger56 = log.scope("NotificationController");
122310
122566
  list6 = requireNonAnonymous(async (ctx) => {
122311
122567
  const query = {
122312
122568
  status: ctx.url.searchParams.get("status") || undefined,
@@ -122317,10 +122573,10 @@ var init_notification_controller = __esm(() => {
122317
122573
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
122318
122574
  if (!result.success) {
122319
122575
  const details = formatZodError(result.error);
122320
- logger55.warn("List notifications query validation failed", { details });
122576
+ logger56.warn("List notifications query validation failed", { details });
122321
122577
  throw ApiError.badRequest("Invalid query parameters", details);
122322
122578
  }
122323
- logger55.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
122579
+ logger56.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
122324
122580
  return ctx.services.notification.list(ctx.user, result.data);
122325
122581
  });
122326
122582
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -122335,12 +122591,12 @@ var init_notification_controller = __esm(() => {
122335
122591
  } catch (error2) {
122336
122592
  if (error2 instanceof exports_external.ZodError) {
122337
122593
  const details = formatZodError(error2);
122338
- logger55.warn("Update notification status validation failed", { details });
122594
+ logger56.warn("Update notification status validation failed", { details });
122339
122595
  throw ApiError.unprocessableEntity("Invalid request body", details);
122340
122596
  }
122341
122597
  throw ApiError.badRequest("Invalid JSON body");
122342
122598
  }
122343
- logger55.debug("Updating status", {
122599
+ logger56.debug("Updating status", {
122344
122600
  userId: ctx.user.id,
122345
122601
  notificationId,
122346
122602
  status: body2.status
@@ -122350,7 +122606,7 @@ var init_notification_controller = __esm(() => {
122350
122606
  getStats2 = requireNonAnonymous(async (ctx) => {
122351
122607
  const startDate = ctx.url.searchParams.get("startDate");
122352
122608
  const endDate = ctx.url.searchParams.get("endDate");
122353
- logger55.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
122609
+ logger56.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
122354
122610
  return ctx.services.notification.getStats(ctx.user, {
122355
122611
  startDate: startDate ? new Date(startDate) : undefined,
122356
122612
  endDate: endDate ? new Date(endDate) : undefined
@@ -122364,12 +122620,12 @@ var init_notification_controller = __esm(() => {
122364
122620
  } catch (error2) {
122365
122621
  if (error2 instanceof exports_external.ZodError) {
122366
122622
  const details = formatZodError(error2);
122367
- logger55.warn("Create notification validation failed", { details });
122623
+ logger56.warn("Create notification validation failed", { details });
122368
122624
  throw ApiError.unprocessableEntity("Invalid request body", details);
122369
122625
  }
122370
122626
  throw ApiError.badRequest("Invalid JSON body");
122371
122627
  }
122372
- logger55.debug("Creating notification", {
122628
+ logger56.debug("Creating notification", {
122373
122629
  userId: ctx.user.id,
122374
122630
  targetUserId: body2.userId,
122375
122631
  type: body2.type
@@ -122387,12 +122643,12 @@ var init_notification_controller = __esm(() => {
122387
122643
  });
122388
122644
  });
122389
122645
  deliver = requireNonAnonymous(async (ctx) => {
122390
- logger55.debug("Delivering notifications", { userId: ctx.user.id });
122646
+ logger56.debug("Delivering notifications", { userId: ctx.user.id });
122391
122647
  try {
122392
122648
  await ctx.services.notification.deliverPending(ctx.user.id);
122393
122649
  return { success: true };
122394
122650
  } catch (error2) {
122395
- logger55.error("Failed to deliver notifications", { error: error2 });
122651
+ logger56.error("Failed to deliver notifications", { error: error2 });
122396
122652
  throw ApiError.internal("Failed to deliver notifications");
122397
122653
  }
122398
122654
  });
@@ -122404,16 +122660,16 @@ var init_notification_controller = __esm(() => {
122404
122660
  deliver
122405
122661
  };
122406
122662
  });
122407
- var logger56;
122663
+ var logger57;
122408
122664
  var generateToken2;
122409
122665
  var realtime;
122410
122666
  var init_realtime_controller = __esm(() => {
122411
122667
  init_src2();
122412
122668
  init_utils11();
122413
- logger56 = log.scope("RealtimeController");
122669
+ logger57 = log.scope("RealtimeController");
122414
122670
  generateToken2 = requireNonAnonymous(async (ctx) => {
122415
122671
  const gameIdOrSlug = ctx.params.gameId;
122416
- logger56.debug("Generating token", {
122672
+ logger57.debug("Generating token", {
122417
122673
  userId: ctx.user.id,
122418
122674
  gameId: gameIdOrSlug || "global",
122419
122675
  launchId: ctx.launchId
@@ -122424,7 +122680,7 @@ var init_realtime_controller = __esm(() => {
122424
122680
  generateToken: generateToken2
122425
122681
  };
122426
122682
  });
122427
- var logger57;
122683
+ var logger58;
122428
122684
  var listKeys2;
122429
122685
  var setSecrets;
122430
122686
  var deleteSecret;
@@ -122435,13 +122691,13 @@ var init_secrets_controller = __esm(() => {
122435
122691
  init_src2();
122436
122692
  init_errors();
122437
122693
  init_utils11();
122438
- logger57 = log.scope("SecretsController");
122694
+ logger58 = log.scope("SecretsController");
122439
122695
  listKeys2 = requireDeveloper(async (ctx) => {
122440
122696
  const slug2 = ctx.params.slug;
122441
122697
  if (!slug2) {
122442
122698
  throw ApiError.badRequest("Missing game slug");
122443
122699
  }
122444
- logger57.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
122700
+ logger58.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
122445
122701
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
122446
122702
  return { keys };
122447
122703
  });
@@ -122457,12 +122713,12 @@ var init_secrets_controller = __esm(() => {
122457
122713
  } catch (error2) {
122458
122714
  if (error2 instanceof exports_external.ZodError) {
122459
122715
  const details = formatZodError(error2);
122460
- logger57.warn("Set secrets validation failed", { details });
122716
+ logger58.warn("Set secrets validation failed", { details });
122461
122717
  throw ApiError.unprocessableEntity("Validation failed", details);
122462
122718
  }
122463
122719
  throw ApiError.badRequest("Invalid JSON body");
122464
122720
  }
122465
- logger57.debug("Setting secrets", {
122721
+ logger58.debug("Setting secrets", {
122466
122722
  userId: ctx.user.id,
122467
122723
  slug: slug2,
122468
122724
  keyCount: Object.keys(body2).length
@@ -122479,7 +122735,7 @@ var init_secrets_controller = __esm(() => {
122479
122735
  if (!key) {
122480
122736
  throw ApiError.badRequest("Missing secret key");
122481
122737
  }
122482
- logger57.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
122738
+ logger58.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
122483
122739
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
122484
122740
  return { success: true };
122485
122741
  });
@@ -122489,7 +122745,7 @@ var init_secrets_controller = __esm(() => {
122489
122745
  deleteSecret
122490
122746
  };
122491
122747
  });
122492
- var logger58;
122748
+ var logger59;
122493
122749
  var seed2;
122494
122750
  var init_seed_controller = __esm(() => {
122495
122751
  init_esm();
@@ -122497,7 +122753,7 @@ var init_seed_controller = __esm(() => {
122497
122753
  init_src2();
122498
122754
  init_errors();
122499
122755
  init_utils11();
122500
- logger58 = log.scope("SeedController");
122756
+ logger59 = log.scope("SeedController");
122501
122757
  seed2 = requireDeveloper(async (ctx) => {
122502
122758
  const slug2 = ctx.params.slug;
122503
122759
  if (!slug2) {
@@ -122510,12 +122766,12 @@ var init_seed_controller = __esm(() => {
122510
122766
  } catch (error2) {
122511
122767
  if (error2 instanceof exports_external.ZodError) {
122512
122768
  const details = formatZodError(error2);
122513
- logger58.warn("Seed database validation failed", { details });
122769
+ logger59.warn("Seed database validation failed", { details });
122514
122770
  throw ApiError.unprocessableEntity("Validation failed", details);
122515
122771
  }
122516
122772
  throw ApiError.badRequest("Invalid JSON body");
122517
122773
  }
122518
- logger58.debug("Seeding database", {
122774
+ logger59.debug("Seeding database", {
122519
122775
  userId: ctx.user.id,
122520
122776
  slug: slug2,
122521
122777
  codeLength: body2.code.length,
@@ -122524,7 +122780,7 @@ var init_seed_controller = __esm(() => {
122524
122780
  return ctx.services.seed.seed(slug2, body2.code, ctx.user, body2.secrets);
122525
122781
  });
122526
122782
  });
122527
- var logger59;
122783
+ var logger60;
122528
122784
  var start2;
122529
122785
  var end;
122530
122786
  var mintToken;
@@ -122533,13 +122789,13 @@ var init_session_controller = __esm(() => {
122533
122789
  init_src2();
122534
122790
  init_errors();
122535
122791
  init_utils11();
122536
- logger59 = log.scope("SessionController");
122792
+ logger60 = log.scope("SessionController");
122537
122793
  start2 = requireAuth(async (ctx) => {
122538
122794
  const gameIdOrSlug = ctx.params.gameId;
122539
122795
  if (!gameIdOrSlug) {
122540
122796
  throw ApiError.badRequest("Missing game ID or slug");
122541
122797
  }
122542
- logger59.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122798
+ logger60.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122543
122799
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
122544
122800
  });
122545
122801
  end = requireAuth(async (ctx) => {
@@ -122551,7 +122807,7 @@ var init_session_controller = __esm(() => {
122551
122807
  if (!sessionId) {
122552
122808
  throw ApiError.badRequest("Missing session ID");
122553
122809
  }
122554
- logger59.debug("Ending session", {
122810
+ logger60.debug("Ending session", {
122555
122811
  userId: ctx.user.id,
122556
122812
  gameIdOrSlug,
122557
122813
  sessionId,
@@ -122564,7 +122820,7 @@ var init_session_controller = __esm(() => {
122564
122820
  if (!gameIdOrSlug) {
122565
122821
  throw ApiError.badRequest("Missing game ID or slug");
122566
122822
  }
122567
- logger59.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122823
+ logger60.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122568
122824
  return ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
122569
122825
  });
122570
122826
  sessions2 = {
@@ -122573,22 +122829,22 @@ var init_session_controller = __esm(() => {
122573
122829
  mintToken
122574
122830
  };
122575
122831
  });
122576
- var logger60;
122832
+ var logger61;
122577
122833
  var getShopView;
122578
122834
  var shop;
122579
122835
  var init_shop_controller = __esm(() => {
122580
122836
  init_src2();
122581
122837
  init_utils11();
122582
- logger60 = log.scope("ShopController");
122838
+ logger61 = log.scope("ShopController");
122583
122839
  getShopView = requireNonAnonymous(async (ctx) => {
122584
- logger60.debug("Getting shop view", { userId: ctx.user.id });
122840
+ logger61.debug("Getting shop view", { userId: ctx.user.id });
122585
122841
  return ctx.services.shop.getShopView(ctx.user);
122586
122842
  });
122587
122843
  shop = {
122588
122844
  getShopView
122589
122845
  };
122590
122846
  });
122591
- var logger61;
122847
+ var logger62;
122592
122848
  var list7;
122593
122849
  var getById4;
122594
122850
  var create5;
@@ -122607,9 +122863,9 @@ var init_shop_listing_controller = __esm(() => {
122607
122863
  init_src4();
122608
122864
  init_errors();
122609
122865
  init_utils11();
122610
- logger61 = log.scope("ShopListingController");
122866
+ logger62 = log.scope("ShopListingController");
122611
122867
  list7 = requireAdmin(async (ctx) => {
122612
- logger61.debug("Listing shop listings", { userId: ctx.user.id });
122868
+ logger62.debug("Listing shop listings", { userId: ctx.user.id });
122613
122869
  return ctx.services.shopListing.list();
122614
122870
  });
122615
122871
  getById4 = requireAdmin(async (ctx) => {
@@ -122620,7 +122876,7 @@ var init_shop_listing_controller = __esm(() => {
122620
122876
  if (!isValidUUID(listingId)) {
122621
122877
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
122622
122878
  }
122623
- logger61.debug("Getting listing", { userId: ctx.user.id, listingId });
122879
+ logger62.debug("Getting listing", { userId: ctx.user.id, listingId });
122624
122880
  return ctx.services.shopListing.getById(listingId);
122625
122881
  });
122626
122882
  create5 = requireAdmin(async (ctx) => {
@@ -122631,12 +122887,12 @@ var init_shop_listing_controller = __esm(() => {
122631
122887
  } catch (error2) {
122632
122888
  if (error2 instanceof exports_external.ZodError) {
122633
122889
  const details = formatZodError(error2);
122634
- logger61.warn("Create shop listing validation failed", { details });
122890
+ logger62.warn("Create shop listing validation failed", { details });
122635
122891
  throw ApiError.unprocessableEntity("Validation failed", details);
122636
122892
  }
122637
122893
  throw ApiError.badRequest("Invalid JSON body");
122638
122894
  }
122639
- logger61.debug("Creating listing", {
122895
+ logger62.debug("Creating listing", {
122640
122896
  userId: ctx.user.id,
122641
122897
  itemId: body2.itemId,
122642
122898
  currencyId: body2.currencyId,
@@ -122659,12 +122915,12 @@ var init_shop_listing_controller = __esm(() => {
122659
122915
  } catch (error2) {
122660
122916
  if (error2 instanceof exports_external.ZodError) {
122661
122917
  const details = formatZodError(error2);
122662
- logger61.warn("Update shop listing validation failed", { details });
122918
+ logger62.warn("Update shop listing validation failed", { details });
122663
122919
  throw ApiError.unprocessableEntity("Validation failed", details);
122664
122920
  }
122665
122921
  throw ApiError.badRequest("Invalid JSON body");
122666
122922
  }
122667
- logger61.debug("Updating listing", {
122923
+ logger62.debug("Updating listing", {
122668
122924
  userId: ctx.user.id,
122669
122925
  listingId,
122670
122926
  price: body2.price,
@@ -122681,7 +122937,7 @@ var init_shop_listing_controller = __esm(() => {
122681
122937
  if (!isValidUUID(listingId)) {
122682
122938
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
122683
122939
  }
122684
- logger61.debug("Deleting listing", { userId: ctx.user.id, listingId });
122940
+ logger62.debug("Deleting listing", { userId: ctx.user.id, listingId });
122685
122941
  await ctx.services.shopListing.delete(listingId);
122686
122942
  });
122687
122943
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -122692,7 +122948,7 @@ var init_shop_listing_controller = __esm(() => {
122692
122948
  if (!isValidUUID(gameId)) {
122693
122949
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
122694
122950
  }
122695
- logger61.debug("Listing game listings", { userId: ctx.user.id, gameId });
122951
+ logger62.debug("Listing game listings", { userId: ctx.user.id, gameId });
122696
122952
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
122697
122953
  });
122698
122954
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -122707,7 +122963,7 @@ var init_shop_listing_controller = __esm(() => {
122707
122963
  if (!isValidUUID(itemId)) {
122708
122964
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
122709
122965
  }
122710
- logger61.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
122966
+ logger62.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
122711
122967
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
122712
122968
  });
122713
122969
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -122729,12 +122985,12 @@ var init_shop_listing_controller = __esm(() => {
122729
122985
  } catch (error2) {
122730
122986
  if (error2 instanceof exports_external.ZodError) {
122731
122987
  const details = formatZodError(error2);
122732
- logger61.warn("Create game item listing validation failed", { details });
122988
+ logger62.warn("Create game item listing validation failed", { details });
122733
122989
  throw ApiError.unprocessableEntity("Validation failed", details);
122734
122990
  }
122735
122991
  throw ApiError.badRequest("Invalid JSON body");
122736
122992
  }
122737
- logger61.debug("Creating game item listing", {
122993
+ logger62.debug("Creating game item listing", {
122738
122994
  userId: ctx.user.id,
122739
122995
  gameId,
122740
122996
  itemId,
@@ -122762,12 +123018,12 @@ var init_shop_listing_controller = __esm(() => {
122762
123018
  } catch (error2) {
122763
123019
  if (error2 instanceof exports_external.ZodError) {
122764
123020
  const details = formatZodError(error2);
122765
- logger61.warn("Update game item listing validation failed", { details });
123021
+ logger62.warn("Update game item listing validation failed", { details });
122766
123022
  throw ApiError.unprocessableEntity("Validation failed", details);
122767
123023
  }
122768
123024
  throw ApiError.badRequest("Invalid JSON body");
122769
123025
  }
122770
- logger61.debug("Updating game item listing", {
123026
+ logger62.debug("Updating game item listing", {
122771
123027
  userId: ctx.user.id,
122772
123028
  gameId,
122773
123029
  itemId,
@@ -122789,7 +123045,7 @@ var init_shop_listing_controller = __esm(() => {
122789
123045
  if (!isValidUUID(itemId)) {
122790
123046
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
122791
123047
  }
122792
- logger61.debug("Deleting game item listing", {
123048
+ logger62.debug("Deleting game item listing", {
122793
123049
  userId: ctx.user.id,
122794
123050
  gameId,
122795
123051
  itemId
@@ -122814,20 +123070,20 @@ async function getBySlug2(ctx) {
122814
123070
  if (!slug2) {
122815
123071
  throw ApiError.badRequest("Template slug is required");
122816
123072
  }
122817
- logger62.debug("Getting sprite by slug", { slug: slug2 });
123073
+ logger63.debug("Getting sprite by slug", { slug: slug2 });
122818
123074
  return ctx.services.sprite.getBySlug(slug2);
122819
123075
  }
122820
- var logger62;
123076
+ var logger63;
122821
123077
  var sprites;
122822
123078
  var init_sprite_controller = __esm(() => {
122823
123079
  init_src2();
122824
123080
  init_errors();
122825
- logger62 = log.scope("SpriteController");
123081
+ logger63 = log.scope("SpriteController");
122826
123082
  sprites = {
122827
123083
  getBySlug: getBySlug2
122828
123084
  };
122829
123085
  });
122830
- var logger63;
123086
+ var logger64;
122831
123087
  var getTodayXp;
122832
123088
  var getTotalXp;
122833
123089
  var updateTodayXp;
@@ -122842,6 +123098,7 @@ var getConfig2;
122842
123098
  var deleteIntegrations;
122843
123099
  var endActivity;
122844
123100
  var heartbeat;
123101
+ var advanceCourse;
122845
123102
  var getStudentXp;
122846
123103
  var getRoster;
122847
123104
  var getStudentOverview;
@@ -122861,15 +123118,15 @@ var init_timeback_controller = __esm(() => {
122861
123118
  init_src4();
122862
123119
  init_errors();
122863
123120
  init_utils11();
122864
- logger63 = log.scope("TimebackController");
123121
+ logger64 = log.scope("TimebackController");
122865
123122
  getTodayXp = requireNonAnonymous(async (ctx) => {
122866
123123
  const date4 = ctx.url.searchParams.get("date") || undefined;
122867
123124
  const tz = ctx.url.searchParams.get("tz") || undefined;
122868
- logger63.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
123125
+ logger64.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
122869
123126
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
122870
123127
  });
122871
123128
  getTotalXp = requireNonAnonymous(async (ctx) => {
122872
- logger63.debug("Getting total XP", { userId: ctx.user.id });
123129
+ logger64.debug("Getting total XP", { userId: ctx.user.id });
122873
123130
  return ctx.services.timeback.getTotalXp(ctx.user.id);
122874
123131
  });
122875
123132
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -122880,18 +123137,18 @@ var init_timeback_controller = __esm(() => {
122880
123137
  } catch (error2) {
122881
123138
  if (error2 instanceof exports_external.ZodError) {
122882
123139
  const details = formatZodError(error2);
122883
- logger63.warn("Update today XP validation failed", { details });
123140
+ logger64.warn("Update today XP validation failed", { details });
122884
123141
  throw ApiError.unprocessableEntity("Validation failed", details);
122885
123142
  }
122886
123143
  throw ApiError.badRequest("Invalid JSON body");
122887
123144
  }
122888
- logger63.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
123145
+ logger64.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
122889
123146
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
122890
123147
  });
122891
123148
  getXpHistory = requireNonAnonymous(async (ctx) => {
122892
123149
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
122893
123150
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
122894
- logger63.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
123151
+ logger64.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
122895
123152
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
122896
123153
  });
122897
123154
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -122902,18 +123159,18 @@ var init_timeback_controller = __esm(() => {
122902
123159
  } catch (error2) {
122903
123160
  if (error2 instanceof exports_external.ZodError) {
122904
123161
  const details = formatZodError(error2);
122905
- logger63.warn("Populate student validation failed", { details });
123162
+ logger64.warn("Populate student validation failed", { details });
122906
123163
  throw ApiError.unprocessableEntity("Validation failed", details);
122907
123164
  }
122908
123165
  }
122909
- logger63.debug("Populating student", {
123166
+ logger64.debug("Populating student", {
122910
123167
  userId: ctx.user.id,
122911
123168
  hasProvidedNames: Boolean(providedNames)
122912
123169
  });
122913
123170
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
122914
123171
  });
122915
123172
  getUser = requireNonAnonymous(async (ctx) => {
122916
- logger63.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
123173
+ logger64.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
122917
123174
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
122918
123175
  });
122919
123176
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -122921,7 +123178,7 @@ var init_timeback_controller = __esm(() => {
122921
123178
  if (!timebackId) {
122922
123179
  throw ApiError.badRequest("Missing timebackId parameter");
122923
123180
  }
122924
- logger63.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
123181
+ logger64.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
122925
123182
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
122926
123183
  });
122927
123184
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -122932,12 +123189,12 @@ var init_timeback_controller = __esm(() => {
122932
123189
  } catch (error2) {
122933
123190
  if (error2 instanceof exports_external.ZodError) {
122934
123191
  const details = formatZodError(error2);
122935
- logger63.warn("Setup integration validation failed", { details });
123192
+ logger64.warn("Setup integration validation failed", { details });
122936
123193
  throw ApiError.unprocessableEntity("Validation failed", details);
122937
123194
  }
122938
123195
  throw ApiError.badRequest("Invalid JSON body");
122939
123196
  }
122940
- logger63.debug("Setting up integration", {
123197
+ logger64.debug("Setting up integration", {
122941
123198
  userId: ctx.user.id,
122942
123199
  gameId: body2.gameId
122943
123200
  });
@@ -122951,7 +123208,7 @@ var init_timeback_controller = __esm(() => {
122951
123208
  if (!isValidUUID(gameId)) {
122952
123209
  throw ApiError.unprocessableEntity("Invalid gameId format");
122953
123210
  }
122954
- logger63.debug("Getting integrations", { userId: ctx.user.id, gameId });
123211
+ logger64.debug("Getting integrations", { userId: ctx.user.id, gameId });
122955
123212
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
122956
123213
  });
122957
123214
  verifyIntegration = requireDeveloper(async (ctx) => {
@@ -122962,7 +123219,7 @@ var init_timeback_controller = __esm(() => {
122962
123219
  if (!isValidUUID(gameId)) {
122963
123220
  throw ApiError.unprocessableEntity("Invalid gameId format");
122964
123221
  }
122965
- logger63.debug("Verifying integration", { userId: ctx.user.id, gameId });
123222
+ logger64.debug("Verifying integration", { userId: ctx.user.id, gameId });
122966
123223
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
122967
123224
  });
122968
123225
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -122973,7 +123230,7 @@ var init_timeback_controller = __esm(() => {
122973
123230
  if (!isValidUUID(gameId)) {
122974
123231
  throw ApiError.unprocessableEntity("Invalid gameId format");
122975
123232
  }
122976
- logger63.debug("Getting config", { userId: ctx.user.id, gameId });
123233
+ logger64.debug("Getting config", { userId: ctx.user.id, gameId });
122977
123234
  return ctx.services.timeback.getConfig(gameId, ctx.user);
122978
123235
  });
122979
123236
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -122984,7 +123241,7 @@ var init_timeback_controller = __esm(() => {
122984
123241
  if (!isValidUUID(gameId)) {
122985
123242
  throw ApiError.unprocessableEntity("Invalid gameId format");
122986
123243
  }
122987
- logger63.debug("Deleting integrations", { userId: ctx.user.id, gameId });
123244
+ logger64.debug("Deleting integrations", { userId: ctx.user.id, gameId });
122988
123245
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
122989
123246
  });
122990
123247
  endActivity = requireDeveloper(async (ctx) => {
@@ -122995,7 +123252,7 @@ var init_timeback_controller = __esm(() => {
122995
123252
  } catch (error2) {
122996
123253
  if (error2 instanceof exports_external.ZodError) {
122997
123254
  const details = formatZodError(error2);
122998
- logger63.warn("End activity validation failed", { details });
123255
+ logger64.warn("End activity validation failed", { details });
122999
123256
  throw ApiError.unprocessableEntity("Validation failed", details);
123000
123257
  }
123001
123258
  throw ApiError.badRequest("Invalid JSON body");
@@ -123013,7 +123270,7 @@ var init_timeback_controller = __esm(() => {
123013
123270
  masteredUnits,
123014
123271
  extensions
123015
123272
  } = body2;
123016
- logger63.debug("Ending activity", { userId: ctx.user.id, gameId });
123273
+ logger64.debug("Ending activity", { userId: ctx.user.id, gameId });
123017
123274
  return ctx.services.timeback.endActivity({
123018
123275
  gameId,
123019
123276
  studentId,
@@ -123037,7 +123294,7 @@ var init_timeback_controller = __esm(() => {
123037
123294
  } catch (error2) {
123038
123295
  if (error2 instanceof exports_external.ZodError) {
123039
123296
  const details = formatZodError(error2);
123040
- logger63.warn("Heartbeat validation failed", { details });
123297
+ logger64.warn("Heartbeat validation failed", { details });
123041
123298
  throw ApiError.unprocessableEntity("Validation failed", details);
123042
123299
  }
123043
123300
  throw ApiError.badRequest("Invalid JSON body");
@@ -123053,7 +123310,7 @@ var init_timeback_controller = __esm(() => {
123053
123310
  windowSequence,
123054
123311
  isFinal
123055
123312
  } = body2;
123056
- logger63.debug("Recording heartbeat", {
123313
+ logger64.debug("Recording heartbeat", {
123057
123314
  userId: ctx.user.id,
123058
123315
  gameId,
123059
123316
  runId,
@@ -123076,6 +123333,19 @@ var init_timeback_controller = __esm(() => {
123076
123333
  user: ctx.user
123077
123334
  });
123078
123335
  });
123336
+ advanceCourse = requireDeveloper(async (ctx) => {
123337
+ const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
123338
+ logger64.debug("Advancing student manually", {
123339
+ userId: ctx.user.id,
123340
+ gameId: body2.gameId,
123341
+ studentId: body2.studentId,
123342
+ subject: body2.subject
123343
+ });
123344
+ return ctx.services.timeback.advanceCourse({
123345
+ ...body2,
123346
+ user: ctx.user
123347
+ });
123348
+ });
123079
123349
  getStudentXp = requireDeveloper(async (ctx) => {
123080
123350
  const timebackId = ctx.params.timebackId;
123081
123351
  if (!timebackId) {
@@ -123106,7 +123376,7 @@ var init_timeback_controller = __esm(() => {
123106
123376
  perCourse: includeOptions.includes("percourse"),
123107
123377
  today: includeOptions.includes("today")
123108
123378
  };
123109
- logger63.debug("Getting student XP", {
123379
+ logger64.debug("Getting student XP", {
123110
123380
  requesterId: ctx.user.id,
123111
123381
  timebackId,
123112
123382
  gameId,
@@ -123127,7 +123397,7 @@ var init_timeback_controller = __esm(() => {
123127
123397
  if (!gameId || !courseId) {
123128
123398
  throw ApiError.badRequest("Missing gameId or courseId parameter");
123129
123399
  }
123130
- logger63.debug("Getting course roster", {
123400
+ logger64.debug("Getting course roster", {
123131
123401
  requesterId: ctx.user.id,
123132
123402
  gameId,
123133
123403
  courseId
@@ -123141,7 +123411,7 @@ var init_timeback_controller = __esm(() => {
123141
123411
  if (!timebackId || !gameId) {
123142
123412
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
123143
123413
  }
123144
- logger63.debug("Getting student overview", {
123414
+ logger64.debug("Getting student overview", {
123145
123415
  requesterId: ctx.user.id,
123146
123416
  timebackId,
123147
123417
  gameId,
@@ -123160,7 +123430,7 @@ var init_timeback_controller = __esm(() => {
123160
123430
  if (!timebackId || !courseId || !gameId) {
123161
123431
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
123162
123432
  }
123163
- logger63.debug("Getting student activity", {
123433
+ logger64.debug("Getting student activity", {
123164
123434
  requesterId: ctx.user.id,
123165
123435
  timebackId,
123166
123436
  courseId,
@@ -123178,7 +123448,7 @@ var init_timeback_controller = __esm(() => {
123178
123448
  });
123179
123449
  grantXp = requireDeveloper(async (ctx) => {
123180
123450
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
123181
- logger63.debug("Granting manual XP", {
123451
+ logger64.debug("Granting manual XP", {
123182
123452
  requesterId: ctx.user.id,
123183
123453
  gameId: body2.gameId,
123184
123454
  courseId: body2.courseId,
@@ -123190,7 +123460,7 @@ var init_timeback_controller = __esm(() => {
123190
123460
  });
123191
123461
  adjustTime = requireDeveloper(async (ctx) => {
123192
123462
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
123193
- logger63.debug("Adjusting time spent", {
123463
+ logger64.debug("Adjusting time spent", {
123194
123464
  requesterId: ctx.user.id,
123195
123465
  gameId: body2.gameId,
123196
123466
  courseId: body2.courseId,
@@ -123202,7 +123472,7 @@ var init_timeback_controller = __esm(() => {
123202
123472
  });
123203
123473
  adjustMastery = requireDeveloper(async (ctx) => {
123204
123474
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
123205
- logger63.debug("Adjusting mastered units", {
123475
+ logger64.debug("Adjusting mastered units", {
123206
123476
  requesterId: ctx.user.id,
123207
123477
  gameId: body2.gameId,
123208
123478
  courseId: body2.courseId,
@@ -123214,7 +123484,7 @@ var init_timeback_controller = __esm(() => {
123214
123484
  });
123215
123485
  toggleCompletion = requireGameManagementAccess(async (ctx) => {
123216
123486
  const body2 = await parseRequestBody(ctx.request, ToggleCourseCompletionRequestSchema);
123217
- logger63.debug("Toggling course completion", {
123487
+ logger64.debug("Toggling course completion", {
123218
123488
  requesterId: ctx.user.id,
123219
123489
  gameId: body2.gameId,
123220
123490
  courseId: body2.courseId,
@@ -123230,7 +123500,7 @@ var init_timeback_controller = __esm(() => {
123230
123500
  if (!gameId || !courseId) {
123231
123501
  throw ApiError.badRequest("Missing gameId or courseId parameter");
123232
123502
  }
123233
- logger63.debug("Searching students for enrollment", {
123503
+ logger64.debug("Searching students for enrollment", {
123234
123504
  requesterId: ctx.user.id,
123235
123505
  gameId,
123236
123506
  courseId,
@@ -123240,7 +123510,7 @@ var init_timeback_controller = __esm(() => {
123240
123510
  });
123241
123511
  enrollStudent = requireGameManagementAccess(async (ctx) => {
123242
123512
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
123243
- logger63.debug("Enrolling student", {
123513
+ logger64.debug("Enrolling student", {
123244
123514
  requesterId: ctx.user.id,
123245
123515
  gameId: body2.gameId,
123246
123516
  courseId: body2.courseId,
@@ -123250,7 +123520,7 @@ var init_timeback_controller = __esm(() => {
123250
123520
  });
123251
123521
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
123252
123522
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
123253
- logger63.debug("Unenrolling student", {
123523
+ logger64.debug("Unenrolling student", {
123254
123524
  requesterId: ctx.user.id,
123255
123525
  gameId: body2.gameId,
123256
123526
  courseId: body2.courseId,
@@ -123273,6 +123543,7 @@ var init_timeback_controller = __esm(() => {
123273
123543
  deleteIntegrations,
123274
123544
  endActivity,
123275
123545
  heartbeat,
123546
+ advanceCourse,
123276
123547
  getStudentXp,
123277
123548
  getRoster,
123278
123549
  getStudentOverview,
@@ -123286,7 +123557,7 @@ var init_timeback_controller = __esm(() => {
123286
123557
  unenrollStudent
123287
123558
  };
123288
123559
  });
123289
- var logger64;
123560
+ var logger65;
123290
123561
  var initiate;
123291
123562
  var init_upload_controller = __esm(() => {
123292
123563
  init_esm();
@@ -123294,7 +123565,7 @@ var init_upload_controller = __esm(() => {
123294
123565
  init_src2();
123295
123566
  init_errors();
123296
123567
  init_utils11();
123297
- logger64 = log.scope("UploadController");
123568
+ logger65 = log.scope("UploadController");
123298
123569
  initiate = requireDeveloper(async (ctx) => {
123299
123570
  let body2;
123300
123571
  try {
@@ -123303,16 +123574,16 @@ var init_upload_controller = __esm(() => {
123303
123574
  } catch (error2) {
123304
123575
  if (error2 instanceof exports_external.ZodError) {
123305
123576
  const details = formatZodError(error2);
123306
- logger64.warn("Initiate upload validation failed", { details });
123577
+ logger65.warn("Initiate upload validation failed", { details });
123307
123578
  throw ApiError.unprocessableEntity("Validation failed", details);
123308
123579
  }
123309
123580
  throw ApiError.badRequest("Invalid JSON body");
123310
123581
  }
123311
- logger64.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
123582
+ logger65.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
123312
123583
  return ctx.services.upload.initiate(body2, ctx.user);
123313
123584
  });
123314
123585
  });
123315
- var logger65;
123586
+ var logger66;
123316
123587
  var getMe;
123317
123588
  var getDemoProfile;
123318
123589
  var updateDemoProfile;
@@ -123321,18 +123592,18 @@ var init_user_controller = __esm(() => {
123321
123592
  init_schemas_index();
123322
123593
  init_src2();
123323
123594
  init_utils11();
123324
- logger65 = log.scope("UserController");
123595
+ logger66 = log.scope("UserController");
123325
123596
  getMe = requireNonAnonymous(async (ctx) => {
123326
- logger65.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
123597
+ logger66.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
123327
123598
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
123328
123599
  });
123329
123600
  getDemoProfile = requireAnonymous(async (ctx) => {
123330
- logger65.debug("Getting demo profile", { userId: ctx.user.id });
123601
+ logger66.debug("Getting demo profile", { userId: ctx.user.id });
123331
123602
  return ctx.services.user.getDemoProfile(ctx.user.id);
123332
123603
  });
123333
123604
  updateDemoProfile = requireAnonymous(async (ctx) => {
123334
123605
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
123335
- logger65.debug("Updating demo profile", {
123606
+ logger66.debug("Updating demo profile", {
123336
123607
  userId: ctx.user.id,
123337
123608
  displayName: body2.displayName
123338
123609
  });
@@ -123344,13 +123615,13 @@ var init_user_controller = __esm(() => {
123344
123615
  updateDemoProfile
123345
123616
  };
123346
123617
  });
123347
- var logger66;
123618
+ var logger67;
123348
123619
  var init_verify_controller = __esm(() => {
123349
123620
  init_schemas_index();
123350
123621
  init_src2();
123351
123622
  init_errors();
123352
123623
  init_utils11();
123353
- logger66 = log.scope("VerifyController");
123624
+ logger67 = log.scope("VerifyController");
123354
123625
  });
123355
123626
  var init_controllers = __esm(() => {
123356
123627
  init_achievement_controller();
@@ -123647,7 +123918,7 @@ var init_uploads = __esm(() => {
123647
123918
  gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
123648
123919
  gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
123649
123920
  });
123650
- var logger67;
123921
+ var logger68;
123651
123922
  var gameDeployRouter;
123652
123923
  var init_deploy = __esm(() => {
123653
123924
  init_drizzle_orm();
@@ -123658,7 +123929,7 @@ var init_deploy = __esm(() => {
123658
123929
  init_src2();
123659
123930
  init_api();
123660
123931
  init_uploads();
123661
- logger67 = log.scope("SandboxDeploy");
123932
+ logger68 = log.scope("SandboxDeploy");
123662
123933
  gameDeployRouter = new Hono2;
123663
123934
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
123664
123935
  const user = c2.get("user");
@@ -123775,7 +124046,7 @@ var init_deploy = __esm(() => {
123775
124046
  completedAt: now2
123776
124047
  };
123777
124048
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
123778
- logger67.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
124049
+ logger68.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
123779
124050
  return c2.json({
123780
124051
  id: insertedJob.id,
123781
124052
  status: "succeeded",
@@ -124281,6 +124552,7 @@ var init_timeback6 = __esm(() => {
124281
124552
  timebackRouter.delete("/integrations/:gameId", handle2(timeback2.deleteIntegrations, { status: 204 }));
124282
124553
  timebackRouter.post("/end-activity", handle2(timeback2.endActivity));
124283
124554
  timebackRouter.post("/heartbeat", handle2(timeback2.heartbeat));
124555
+ timebackRouter.post("/advance-course", handle2(timeback2.advanceCourse));
124284
124556
  timebackRouter.get("/user", async (c2) => {
124285
124557
  const user = c2.get("user");
124286
124558
  const gameId = c2.get("gameId");
@@ -124366,7 +124638,7 @@ function verifyMockToken(idToken) {
124366
124638
  throw new Error("Invalid LTI token format");
124367
124639
  }
124368
124640
  }
124369
- var logger68;
124641
+ var logger69;
124370
124642
  var ltiRouter;
124371
124643
  var init_lti = __esm(() => {
124372
124644
  init_drizzle_orm();
@@ -124377,7 +124649,7 @@ var init_lti = __esm(() => {
124377
124649
  init_src2();
124378
124650
  init_constants();
124379
124651
  init_api();
124380
- logger68 = log.scope("SandboxLti");
124652
+ logger69 = log.scope("SandboxLti");
124381
124653
  ltiRouter = new Hono2;
124382
124654
  ltiRouter.post("/launch", async (c2) => {
124383
124655
  const db2 = c2.get("db");
@@ -124395,7 +124667,7 @@ var init_lti = __esm(() => {
124395
124667
  claims = verifyMockToken(idToken);
124396
124668
  } catch (error2) {
124397
124669
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
124398
- logger68.error("LTI token verification failed", { error: errorMessage });
124670
+ logger69.error("LTI token verification failed", { error: errorMessage });
124399
124671
  return c2.json({
124400
124672
  error: "invalid_token",
124401
124673
  message: errorMessage
@@ -124403,7 +124675,7 @@ var init_lti = __esm(() => {
124403
124675
  }
124404
124676
  const validationError = validateLtiClaims(claims);
124405
124677
  if (validationError) {
124406
- logger68.warn("LTI claims validation failed", {
124678
+ logger69.warn("LTI claims validation failed", {
124407
124679
  error: validationError,
124408
124680
  sub: claims.sub
124409
124681
  });
@@ -124423,7 +124695,7 @@ var init_lti = __esm(() => {
124423
124695
  createdAt: new Date,
124424
124696
  updatedAt: new Date
124425
124697
  });
124426
- logger68.info("LTI launch successful", { userId: user.id });
124698
+ logger69.info("LTI launch successful", { userId: user.id });
124427
124699
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
124428
124700
  const currentHost = new URL(c2.req.url).hostname;
124429
124701
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -124431,7 +124703,7 @@ var init_lti = __esm(() => {
124431
124703
  return c2.redirect(redirectPath);
124432
124704
  } catch (error2) {
124433
124705
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
124434
- logger68.error("Unexpected error during LTI launch", { error: errorMessage });
124706
+ logger69.error("Unexpected error during LTI launch", { error: errorMessage });
124435
124707
  return c2.json({
124436
124708
  error: "unexpected_error",
124437
124709
  message: "An unexpected error occurred during LTI launch"
@@ -125022,10 +125294,10 @@ var ACHIEVEMENT_DEFINITIONS2 = [
125022
125294
  var TypeScriptPackages2 = {
125023
125295
  tsc: "tsc",
125024
125296
  nativePreview: "@typescript/native-preview",
125025
- nativePreviewPinned: "@typescript/native-preview@7.0.0-dev.20260221.1"
125297
+ nativePreviewBeta: "@typescript/native-preview@beta"
125026
125298
  };
125027
125299
  var TYPESCRIPT_RUNNER2 = {
125028
- package: TypeScriptPackages2.nativePreviewPinned,
125300
+ package: TypeScriptPackages2.nativePreviewBeta,
125029
125301
  bin: "tsgo"
125030
125302
  };
125031
125303
  // ../constants/src/overworld.ts
@@ -125334,10 +125606,10 @@ var init_typescript2 = __esm7(() => {
125334
125606
  TypeScriptPackages3 = {
125335
125607
  tsc: "tsc",
125336
125608
  nativePreview: "@typescript/native-preview",
125337
- nativePreviewPinned: "@typescript/native-preview@7.0.0-dev.20260221.1"
125609
+ nativePreviewBeta: "@typescript/native-preview@beta"
125338
125610
  };
125339
125611
  TYPESCRIPT_RUNNER3 = {
125340
- package: TypeScriptPackages3.nativePreviewPinned,
125612
+ package: TypeScriptPackages3.nativePreviewBeta,
125341
125613
  bin: "tsgo"
125342
125614
  };
125343
125615
  });
@@ -125419,7 +125691,8 @@ var init_timeback7 = __esm7(() => {
125419
125691
  TIMEBACK_ROUTES2 = {
125420
125692
  END_ACTIVITY: "/integrations/timeback/end-activity",
125421
125693
  GET_XP: "/integrations/timeback/xp",
125422
- HEARTBEAT: "/integrations/timeback/heartbeat"
125694
+ HEARTBEAT: "/integrations/timeback/heartbeat",
125695
+ ADVANCE_COURSE: "/integrations/timeback/advance-course"
125423
125696
  };
125424
125697
  TIMEBACK_COURSE_DEFAULTS2 = {
125425
125698
  gradingScheme: "STANDARD",
@@ -126096,7 +126369,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
126096
126369
  // package.json
126097
126370
  var package_default2 = {
126098
126371
  name: "@playcademy/vite-plugin",
126099
- version: "0.2.26-beta.3",
126372
+ version: "0.2.26-beta.5",
126100
126373
  type: "module",
126101
126374
  exports: {
126102
126375
  ".": {