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

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.
Files changed (2) hide show
  1. package/dist/index.js +655 -409
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -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.19",
25373
25374
  description: "Local development server for Playcademy game development",
25374
25375
  type: "module",
25375
25376
  exports: {
@@ -52738,7 +52739,8 @@ var init_constants3 = __esm(() => {
52738
52739
  TIMEBACK: {
52739
52740
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
52740
52741
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
52741
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`
52742
+ HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
52743
+ ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`
52742
52744
  }
52743
52745
  };
52744
52746
  });
@@ -55131,6 +55133,7 @@ class TimebackAdminService {
55131
55133
  await client.edubridge.enrollments.enroll(data.studentId, data.courseId, {
55132
55134
  role: "student"
55133
55135
  });
55136
+ client.invalidateEnrollments(data.studentId);
55134
55137
  return { status: "ok" };
55135
55138
  }
55136
55139
  async unenrollStudent(data, user) {
@@ -55143,6 +55146,7 @@ class TimebackAdminService {
55143
55146
  throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
55144
55147
  }
55145
55148
  await client.edubridge.enrollments.unenroll(data.studentId, data.courseId);
55149
+ client.invalidateEnrollments(data.studentId);
55146
55150
  return { status: "ok" };
55147
55151
  }
55148
55152
  async getCompletionStatus(client, courseId, studentId) {
@@ -55198,7 +55202,103 @@ var init_timeback_admin_service = __esm(() => {
55198
55202
  init_timeback_util();
55199
55203
  logger16 = log.scope("TimebackAdminService");
55200
55204
  });
55205
+ async function promoteCompletedCourse({
55206
+ db: db2,
55207
+ client,
55208
+ currentIntegration,
55209
+ studentId,
55210
+ enrollments: prefetchedEnrollments
55211
+ }) {
55212
+ const subjectIntegrations = await db2.query.gameTimebackIntegrations.findMany({
55213
+ where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject))
55214
+ });
55215
+ const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
55216
+ if (!nextIntegration) {
55217
+ logger17.debug("Skipping promotion because no next course is configured", {
55218
+ gameId: currentIntegration.gameId,
55219
+ studentId,
55220
+ grade: currentIntegration.grade,
55221
+ subject: currentIntegration.subject,
55222
+ currentCourseId: currentIntegration.courseId
55223
+ });
55224
+ return {
55225
+ status: "no-next-course",
55226
+ currentCourseId: currentIntegration.courseId
55227
+ };
55228
+ }
55229
+ const enrollments = prefetchedEnrollments ?? await client.edubridge.enrollments.listByUser(studentId);
55230
+ const currentEnrollment = enrollments.find((enrollment) => enrollment.course.id === currentIntegration.courseId);
55231
+ const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
55232
+ if (!currentEnrollment) {
55233
+ if (nextEnrollment) {
55234
+ logger17.debug("Skipping promotion because student is already on the next course", {
55235
+ gameId: currentIntegration.gameId,
55236
+ studentId,
55237
+ grade: currentIntegration.grade,
55238
+ subject: currentIntegration.subject,
55239
+ currentCourseId: currentIntegration.courseId,
55240
+ nextCourseId: nextIntegration.courseId
55241
+ });
55242
+ return {
55243
+ status: "already-promoted",
55244
+ currentCourseId: currentIntegration.courseId,
55245
+ nextCourseId: nextIntegration.courseId
55246
+ };
55247
+ }
55248
+ logger17.debug("Skipping promotion because student is not enrolled in the current course", {
55249
+ gameId: currentIntegration.gameId,
55250
+ studentId,
55251
+ grade: currentIntegration.grade,
55252
+ subject: currentIntegration.subject,
55253
+ currentCourseId: currentIntegration.courseId,
55254
+ nextCourseId: nextIntegration.courseId
55255
+ });
55256
+ return {
55257
+ status: "not-enrolled",
55258
+ currentCourseId: currentIntegration.courseId,
55259
+ nextCourseId: nextIntegration.courseId
55260
+ };
55261
+ }
55262
+ const schoolId = currentEnrollment.school.id;
55263
+ let mutatedEnrollments = false;
55264
+ try {
55265
+ if (!nextEnrollment) {
55266
+ await client.edubridge.enrollments.enroll(studentId, nextIntegration.courseId, {
55267
+ role: "student",
55268
+ ...schoolId ? { schoolId } : {}
55269
+ });
55270
+ mutatedEnrollments = true;
55271
+ }
55272
+ await client.edubridge.enrollments.unenroll(studentId, currentIntegration.courseId, schoolId ? { schoolId } : {});
55273
+ mutatedEnrollments = true;
55274
+ } finally {
55275
+ if (mutatedEnrollments) {
55276
+ client.invalidateEnrollments(studentId);
55277
+ }
55278
+ }
55279
+ logger17.info("Promoted student to next course", {
55280
+ gameId: currentIntegration.gameId,
55281
+ studentId,
55282
+ subject: currentIntegration.subject,
55283
+ fromGrade: currentIntegration.grade,
55284
+ toGrade: nextIntegration.grade,
55285
+ currentCourseId: currentIntegration.courseId,
55286
+ nextCourseId: nextIntegration.courseId
55287
+ });
55288
+ return {
55289
+ status: "promoted",
55290
+ currentCourseId: currentIntegration.courseId,
55291
+ nextCourseId: nextIntegration.courseId
55292
+ };
55293
+ }
55201
55294
  var logger17;
55295
+ var init_timeback_promotion_util = __esm(() => {
55296
+ init_drizzle_orm();
55297
+ init_tables_index();
55298
+ init_src2();
55299
+ logger17 = log.scope("TimebackPromotion");
55300
+ });
55301
+ var logger18;
55202
55302
  var TimebackService;
55203
55303
  var init_timeback_service = __esm(() => {
55204
55304
  init_drizzle_orm();
@@ -55208,8 +55308,9 @@ var init_timeback_service = __esm(() => {
55208
55308
  init_types4();
55209
55309
  init_src4();
55210
55310
  init_errors();
55311
+ init_timeback_promotion_util();
55211
55312
  init_timeback_util();
55212
- logger17 = log.scope("TimebackService");
55313
+ logger18 = log.scope("TimebackService");
55213
55314
  TimebackService = class TimebackService2 {
55214
55315
  static HEARTBEAT_DEDUPE_TTL_MS = 300000;
55215
55316
  static processedHeartbeatWindows = new Map;
@@ -55255,7 +55356,7 @@ var init_timeback_service = __esm(() => {
55255
55356
  }
55256
55357
  requireClient() {
55257
55358
  if (!this.deps.timeback) {
55258
- logger17.error("Timeback client not available in context");
55359
+ logger18.error("Timeback client not available in context");
55259
55360
  throw new ValidationError("Timeback integration not available in this environment");
55260
55361
  }
55261
55362
  return this.deps.timeback;
@@ -55308,7 +55409,7 @@ var init_timeback_service = __esm(() => {
55308
55409
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
55309
55410
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
55310
55411
  if (!result) {
55311
- logger17.error("Daily XP upsert returned no rows", { userId, date: targetDate });
55412
+ logger18.error("Daily XP upsert returned no rows", { userId, date: targetDate });
55312
55413
  throw new InternalError("Failed to update daily XP record");
55313
55414
  }
55314
55415
  return { xp: result.xp, date: result.date.toISOString() };
@@ -55339,7 +55440,7 @@ var init_timeback_service = __esm(() => {
55339
55440
  columns: { id: true, timebackId: true }
55340
55441
  });
55341
55442
  if (dbUser?.timebackId) {
55342
- logger17.info("Student already onboarded", { userId: user.id });
55443
+ logger18.info("Student already onboarded", { userId: user.id });
55343
55444
  return { status: "already_populated" };
55344
55445
  }
55345
55446
  let timebackId;
@@ -55348,7 +55449,7 @@ var init_timeback_service = __esm(() => {
55348
55449
  const existingUser = await client.oneroster.users.findByEmail(user.email);
55349
55450
  timebackId = existingUser.sourcedId;
55350
55451
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
55351
- logger17.info("Found existing student in OneRoster", {
55452
+ logger18.info("Found existing student in OneRoster", {
55352
55453
  userId: user.id,
55353
55454
  timebackId
55354
55455
  });
@@ -55377,7 +55478,7 @@ var init_timeback_service = __esm(() => {
55377
55478
  }
55378
55479
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
55379
55480
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
55380
- logger17.info("Created student in OneRoster", { userId: user.id, timebackId });
55481
+ logger18.info("Created student in OneRoster", { userId: user.id, timebackId });
55381
55482
  }
55382
55483
  const assessments = await this.fetchAssessments(timebackId);
55383
55484
  await db2.transaction(async (tx) => {
@@ -55411,7 +55512,7 @@ var init_timeback_service = __esm(() => {
55411
55512
  }
55412
55513
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
55413
55514
  if (!updated) {
55414
- logger17.error("User Timeback ID update returned no rows", {
55515
+ logger18.error("User Timeback ID update returned no rows", {
55415
55516
  userId: user.id,
55416
55517
  timebackId
55417
55518
  });
@@ -55435,13 +55536,13 @@ var init_timeback_service = __esm(() => {
55435
55536
  }
55436
55537
  offset += limit;
55437
55538
  }
55438
- logger17.debug("Fetched assessments", {
55539
+ logger18.debug("Fetched assessments", {
55439
55540
  studentSourcedId,
55440
55541
  totalCount: allAssessments.length
55441
55542
  });
55442
55543
  return allAssessments;
55443
55544
  } catch (error) {
55444
- logger17.warn("Failed to fetch assessments", { studentSourcedId, error });
55545
+ logger18.warn("Failed to fetch assessments", { studentSourcedId, error });
55445
55546
  return [];
55446
55547
  }
55447
55548
  }
@@ -55554,7 +55655,7 @@ var init_timeback_service = __esm(() => {
55554
55655
  masterableUnits: derivedMasterableUnits
55555
55656
  } = courseConfig;
55556
55657
  if (!isTimebackSubject(subjectInput)) {
55557
- logger17.warn("Invalid Timeback subject in course config", {
55658
+ logger18.warn("Invalid Timeback subject in course config", {
55558
55659
  subject: subjectInput,
55559
55660
  courseCode,
55560
55661
  title
@@ -55562,7 +55663,7 @@ var init_timeback_service = __esm(() => {
55562
55663
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
55563
55664
  }
55564
55665
  if (!isTimebackGrade(grade)) {
55565
- logger17.warn("Invalid Timeback grade in course config", {
55666
+ logger18.warn("Invalid Timeback grade in course config", {
55566
55667
  grade,
55567
55668
  courseCode,
55568
55669
  title
@@ -55574,7 +55675,7 @@ var init_timeback_service = __esm(() => {
55574
55675
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
55575
55676
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
55576
55677
  if (typeof totalXp !== "number") {
55577
- logger17.warn("Course missing totalXp in Timeback config", {
55678
+ logger18.warn("Course missing totalXp in Timeback config", {
55578
55679
  courseCode,
55579
55680
  title
55580
55681
  });
@@ -55775,7 +55876,7 @@ var init_timeback_service = __esm(() => {
55775
55876
  ...runId ? { runId } : {}
55776
55877
  });
55777
55878
  }
55778
- logger17.info("Recorded activity completion", {
55879
+ logger18.info("Recorded activity completion", {
55779
55880
  gameId,
55780
55881
  courseId: integration.courseId,
55781
55882
  studentId,
@@ -55792,6 +55893,72 @@ var init_timeback_service = __esm(() => {
55792
55893
  inProgress: result.inProgress
55793
55894
  };
55794
55895
  }
55896
+ async advanceCourse({
55897
+ gameId,
55898
+ studentId,
55899
+ subject,
55900
+ user
55901
+ }) {
55902
+ const client = this.requireClient();
55903
+ const db2 = this.deps.db;
55904
+ await this.deps.validateDeveloperAccess(user, gameId);
55905
+ const integrations = await db2.query.gameTimebackIntegrations.findMany({
55906
+ where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject)) : eq(gameTimebackIntegrations.gameId, gameId)
55907
+ });
55908
+ if (integrations.length === 0) {
55909
+ throw new NotFoundError(subject ? `Timeback integrations for game (subject ${subject})` : "Timeback integrations for game");
55910
+ }
55911
+ const enrollments = await client.edubridge.enrollments.listByUser(studentId);
55912
+ const enrolledCourseIds = new Set(enrollments.map((e) => e.course.id));
55913
+ const enrolledIntegrations = integrations.filter((integration) => enrolledCourseIds.has(integration.courseId));
55914
+ if (enrolledIntegrations.length === 0) {
55915
+ throw new NotFoundError(subject ? `Active enrollment for game ladder (subject ${subject})` : "Active enrollment for game ladder");
55916
+ }
55917
+ const subjectsInPlay = new Set(enrolledIntegrations.map((i2) => i2.subject));
55918
+ if (subjectsInPlay.size > 1) {
55919
+ throw new ValidationError(`Ambiguous Timeback advance: student is enrolled in ${subjectsInPlay.size} parallel ladders (${[...subjectsInPlay].join(", ")}); pass { subject } to disambiguate`);
55920
+ }
55921
+ const currentIntegration = enrolledIntegrations.toSorted((left, right) => left.grade - right.grade)[0];
55922
+ const masteryStatus = await client.getMasteryStatus(currentIntegration.courseId, studentId);
55923
+ if (!masteryStatus) {
55924
+ 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().`);
55925
+ }
55926
+ if (!masteryStatus.isComplete) {
55927
+ const promotion2 = {
55928
+ status: "not-mastered",
55929
+ currentCourseId: currentIntegration.courseId,
55930
+ masteredUnits: masteryStatus.masteredUnits,
55931
+ masterableUnits: masteryStatus.masterableUnits
55932
+ };
55933
+ logger18.debug("Skipping course advancement because mastery is incomplete", {
55934
+ gameId,
55935
+ studentId,
55936
+ subject: currentIntegration.subject,
55937
+ grade: currentIntegration.grade,
55938
+ currentCourseId: currentIntegration.courseId,
55939
+ masteredUnits: masteryStatus.masteredUnits,
55940
+ masterableUnits: masteryStatus.masterableUnits
55941
+ });
55942
+ return { status: "ok", promotion: promotion2 };
55943
+ }
55944
+ const promotion = await promoteCompletedCourse({
55945
+ db: db2,
55946
+ client,
55947
+ currentIntegration,
55948
+ studentId,
55949
+ enrollments
55950
+ });
55951
+ logger18.info("Manually advanced student", {
55952
+ gameId,
55953
+ studentId,
55954
+ subject: currentIntegration.subject,
55955
+ grade: currentIntegration.grade,
55956
+ promotionStatus: promotion.status,
55957
+ currentCourseId: promotion.currentCourseId,
55958
+ nextCourseId: promotion.nextCourseId
55959
+ });
55960
+ return { status: "ok", promotion };
55961
+ }
55795
55962
  async recordHeartbeat({
55796
55963
  gameId,
55797
55964
  studentId,
@@ -55814,7 +55981,7 @@ var init_timeback_service = __esm(() => {
55814
55981
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
55815
55982
  const effectiveResumeId = resumeId ?? runId;
55816
55983
  if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
55817
- logger17.debug("Skipping duplicate heartbeat window", {
55984
+ logger18.debug("Skipping duplicate heartbeat window", {
55818
55985
  gameId,
55819
55986
  studentId,
55820
55987
  runId,
@@ -55827,7 +55994,7 @@ var init_timeback_service = __esm(() => {
55827
55994
  await this.deps.validateDeveloperAccess(user, gameId);
55828
55995
  const inFlightHeartbeat = TimebackService2.getInFlightHeartbeatWindow(heartbeatWindowKey);
55829
55996
  if (inFlightHeartbeat) {
55830
- logger17.debug("Joining in-flight heartbeat window", {
55997
+ logger18.debug("Joining in-flight heartbeat window", {
55831
55998
  gameId,
55832
55999
  studentId,
55833
56000
  runId,
@@ -55864,7 +56031,7 @@ var init_timeback_service = __esm(() => {
55864
56031
  });
55865
56032
  }
55866
56033
  TimebackService2.markHeartbeatWindowProcessed(heartbeatWindowKey);
55867
- logger17.debug("Recorded heartbeat", {
56034
+ logger18.debug("Recorded heartbeat", {
55868
56035
  gameId,
55869
56036
  courseId: integration.courseId,
55870
56037
  studentId,
@@ -55899,7 +56066,7 @@ var init_timeback_service = __esm(() => {
55899
56066
  });
55900
56067
  courseIds = integrations.map((i2) => i2.courseId);
55901
56068
  if (courseIds.length === 0) {
55902
- logger17.debug("No integrations found for game, returning 0 XP", {
56069
+ logger18.debug("No integrations found for game, returning 0 XP", {
55903
56070
  timebackId,
55904
56071
  gameId: options.gameId,
55905
56072
  grade: options.grade,
@@ -55916,7 +56083,7 @@ var init_timeback_service = __esm(() => {
55916
56083
  courseIds: courseIds.length > 0 ? courseIds : undefined,
55917
56084
  include: options?.include
55918
56085
  });
55919
- logger17.debug("Retrieved student XP", {
56086
+ logger18.debug("Retrieved student XP", {
55920
56087
  timebackId,
55921
56088
  gameId: options?.gameId,
55922
56089
  grade: options?.grade,
@@ -55944,15 +56111,15 @@ class UploadService {
55944
56111
  const { fileName, gameId } = request;
55945
56112
  const bucketName = this.deps.uploadBucket;
55946
56113
  if (!bucketName) {
55947
- logger18.error("Upload bucket not configured in environment");
56114
+ logger19.error("Upload bucket not configured in environment");
55948
56115
  throw new ValidationError("Upload bucket not configured");
55949
56116
  }
55950
56117
  await this.deps.validateDeveloperAccess(user, gameId);
55951
56118
  const version2 = ulid();
55952
56119
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
55953
- logger18.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
56120
+ logger19.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
55954
56121
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
55955
- logger18.info("Presigned URL generated", {
56122
+ logger19.info("Presigned URL generated", {
55956
56123
  userId: user.id,
55957
56124
  gameId,
55958
56125
  version: version2
@@ -55965,12 +56132,12 @@ class UploadService {
55965
56132
  };
55966
56133
  }
55967
56134
  }
55968
- var logger18;
56135
+ var logger19;
55969
56136
  var init_upload_service = __esm(() => {
55970
56137
  init_node();
55971
56138
  init_src2();
55972
56139
  init_errors();
55973
- logger18 = log.scope("UploadService");
56140
+ logger19 = log.scope("UploadService");
55974
56141
  });
55975
56142
  function createPlatformServices(deps) {
55976
56143
  const {
@@ -56262,7 +56429,7 @@ class AchievementService {
56262
56429
  results.push(result);
56263
56430
  }
56264
56431
  }
56265
- logger19.debug("Listed current achievements", { userId: user.id, count: results.length });
56432
+ logger20.debug("Listed current achievements", { userId: user.id, count: results.length });
56266
56433
  return results;
56267
56434
  }
56268
56435
  async listHistory(user, limit) {
@@ -56280,14 +56447,14 @@ class AchievementService {
56280
56447
  createdAt: c.createdAt,
56281
56448
  scopeKey: c.scopeKey
56282
56449
  }));
56283
- logger19.debug("Listed achievement history", { userId: user.id, count: results.length });
56450
+ logger20.debug("Listed achievement history", { userId: user.id, count: results.length });
56284
56451
  return results;
56285
56452
  }
56286
56453
  async submitProgress(achievementId, user) {
56287
56454
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
56288
56455
  broadcast: false
56289
56456
  });
56290
- logger19.debug("Submitted progress", {
56457
+ logger20.debug("Submitted progress", {
56291
56458
  userId: user.id,
56292
56459
  achievementId,
56293
56460
  wasNewClaim
@@ -56319,7 +56486,7 @@ class AchievementService {
56319
56486
  rewardCredits
56320
56487
  }).returning();
56321
56488
  if (!newClaim) {
56322
- logger19.error("Achievement claim insert returned no rows", {
56489
+ logger20.error("Achievement claim insert returned no rows", {
56323
56490
  userId,
56324
56491
  achievementId,
56325
56492
  scopeKey
@@ -56328,7 +56495,7 @@ class AchievementService {
56328
56495
  }
56329
56496
  await this.deps.addCredits(userId, rewardCredits);
56330
56497
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
56331
- logger19.info("Awarded achievement", {
56498
+ logger20.info("Awarded achievement", {
56332
56499
  userId,
56333
56500
  achievementId,
56334
56501
  scopeKey,
@@ -56365,7 +56532,7 @@ class AchievementService {
56365
56532
  return { title, body: body2 };
56366
56533
  }
56367
56534
  }
56368
- var logger19;
56535
+ var logger20;
56369
56536
  var init_achievement_service = __esm(() => {
56370
56537
  init_drizzle_orm();
56371
56538
  init_tables_index();
@@ -56374,7 +56541,7 @@ var init_achievement_service = __esm(() => {
56374
56541
  init_errors();
56375
56542
  init_leaderboard_util();
56376
56543
  init_scope_util();
56377
- logger19 = log.scope("AchievementService");
56544
+ logger20 = log.scope("AchievementService");
56378
56545
  });
56379
56546
 
56380
56547
  class InventoryService {
@@ -56401,7 +56568,7 @@ class InventoryService {
56401
56568
  },
56402
56569
  updatedAt: inventoryItems.updatedAt
56403
56570
  }).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 });
56571
+ logger21.debug("Listed inventory", { userId: user.id, count: inventory.length });
56405
56572
  return inventory;
56406
56573
  }
56407
56574
  async addItem(itemId, quantity, user) {
@@ -56418,7 +56585,7 @@ class InventoryService {
56418
56585
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
56419
56586
  return inserted?.quantity ?? 0;
56420
56587
  });
56421
- logger20.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
56588
+ logger21.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
56422
56589
  return { newTotal };
56423
56590
  }
56424
56591
  async removeItem(itemId, quantity, user) {
@@ -56429,7 +56596,7 @@ class InventoryService {
56429
56596
  }
56430
56597
  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
56598
  if (!currentItem) {
56432
- logger20.warn("Insufficient inventory for removal", {
56599
+ logger21.warn("Insufficient inventory for removal", {
56433
56600
  userId: user.id,
56434
56601
  itemId,
56435
56602
  requestedQuantity: quantity
@@ -56439,13 +56606,13 @@ class InventoryService {
56439
56606
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
56440
56607
  return updated?.quantity ?? 0;
56441
56608
  });
56442
- logger20.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
56609
+ logger21.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
56443
56610
  return { newTotal };
56444
56611
  }
56445
56612
  async addCredits(userId, amount) {
56446
56613
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
56447
56614
  if (!creditsItem) {
56448
- logger20.error("Primary currency not found", {
56615
+ logger21.error("Primary currency not found", {
56449
56616
  userId,
56450
56617
  amount
56451
56618
  });
@@ -56458,17 +56625,17 @@ class InventoryService {
56458
56625
  updatedAt: new Date
56459
56626
  }
56460
56627
  });
56461
- logger20.debug("Added credits", { userId, amount });
56628
+ logger21.debug("Added credits", { userId, amount });
56462
56629
  }
56463
56630
  }
56464
- var logger20;
56631
+ var logger21;
56465
56632
  var init_inventory_service = __esm(() => {
56466
56633
  init_drizzle_orm();
56467
56634
  init_src();
56468
56635
  init_tables_index();
56469
56636
  init_src2();
56470
56637
  init_errors();
56471
- logger20 = log.scope("InventoryService");
56638
+ logger21 = log.scope("InventoryService");
56472
56639
  });
56473
56640
  var NotificationType;
56474
56641
  var NotificationStatus;
@@ -56522,7 +56689,7 @@ class LeaderboardService {
56522
56689
  sessionId
56523
56690
  }).returning();
56524
56691
  if (!newScore) {
56525
- logger21.error("Score insert returned no rows", { userId, gameId, score: input.score });
56692
+ logger22.error("Score insert returned no rows", { userId, gameId, score: input.score });
56526
56693
  throw new InternalError("Failed to insert score");
56527
56694
  }
56528
56695
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -56550,7 +56717,7 @@ class LeaderboardService {
56550
56717
  movedUpWithinTop3
56551
56718
  });
56552
56719
  }
56553
- logger21.info("Score submitted", {
56720
+ logger22.info("Score submitted", {
56554
56721
  gameId,
56555
56722
  userId,
56556
56723
  isAnonymousUser,
@@ -56627,7 +56794,7 @@ class LeaderboardService {
56627
56794
  });
56628
56795
  }
56629
56796
  } catch (error) {
56630
- logger21.warn("Failed to publish notification", { error });
56797
+ logger22.warn("Failed to publish notification", { error });
56631
56798
  }
56632
56799
  }
56633
56800
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -56746,7 +56913,7 @@ class LeaderboardService {
56746
56913
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
56747
56914
  }
56748
56915
  }
56749
- var logger21;
56916
+ var logger22;
56750
56917
  var init_leaderboard_service = __esm(() => {
56751
56918
  init_drizzle_orm();
56752
56919
  init_src();
@@ -56756,7 +56923,7 @@ var init_leaderboard_service = __esm(() => {
56756
56923
  init_notification();
56757
56924
  init_errors();
56758
56925
  init_leaderboard_util();
56759
- logger21 = log.scope("LeaderboardService");
56926
+ logger22 = log.scope("LeaderboardService");
56760
56927
  });
56761
56928
 
56762
56929
  class LevelService {
@@ -56771,9 +56938,9 @@ class LevelService {
56771
56938
  for (const config2 of configs) {
56772
56939
  levelConfigCache.set(config2.level, config2);
56773
56940
  }
56774
- logger22.info("Cache pre-warmed", { count: configs.length });
56941
+ logger23.info("Cache pre-warmed", { count: configs.length });
56775
56942
  } catch (error) {
56776
- logger22.error("Cache pre-warm failed", { error });
56943
+ logger23.error("Cache pre-warm failed", { error });
56777
56944
  }
56778
56945
  }
56779
56946
  async getConfig(level) {
@@ -56807,7 +56974,7 @@ class LevelService {
56807
56974
  totalXP
56808
56975
  }).returning();
56809
56976
  if (!newUserLevel) {
56810
- logger22.error("User level insert returned no rows", { userId: user.id });
56977
+ logger23.error("User level insert returned no rows", { userId: user.id });
56811
56978
  throw new InternalError("Failed to create user level cache record");
56812
56979
  }
56813
56980
  userLevel = newUserLevel;
@@ -56822,7 +56989,7 @@ class LevelService {
56822
56989
  userLevel = updatedUserLevel;
56823
56990
  }
56824
56991
  }
56825
- logger22.debug("Retrieved user level", {
56992
+ logger23.debug("Retrieved user level", {
56826
56993
  userId: user.id,
56827
56994
  totalXP,
56828
56995
  currentLevel,
@@ -56833,7 +57000,7 @@ class LevelService {
56833
57000
  async getProgress(user) {
56834
57001
  const userLevel = await this.getByUser(user);
56835
57002
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
56836
- logger22.debug("Retrieved progress", { userId: user.id });
57003
+ logger23.debug("Retrieved progress", { userId: user.id });
56837
57004
  return {
56838
57005
  level: userLevel.currentLevel,
56839
57006
  currentXp: userLevel.currentXp,
@@ -56867,7 +57034,7 @@ class LevelService {
56867
57034
  if (leveledUp && previousUserLevel) {
56868
57035
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
56869
57036
  }
56870
- logger22.info("Synced from Timeback", {
57037
+ logger23.info("Synced from Timeback", {
56871
57038
  userId,
56872
57039
  totalXP,
56873
57040
  currentLevel,
@@ -56908,7 +57075,7 @@ class LevelService {
56908
57075
  }
56909
57076
  if (totalCredits > 0) {
56910
57077
  await this.deps.addCredits(userId, totalCredits);
56911
- logger22.info("Awarded level-up credits", {
57078
+ logger23.info("Awarded level-up credits", {
56912
57079
  userId,
56913
57080
  fromLevel,
56914
57081
  toLevel,
@@ -56946,7 +57113,7 @@ class LevelService {
56946
57113
  };
56947
57114
  }
56948
57115
  }
56949
- var logger22;
57116
+ var logger23;
56950
57117
  var levelConfigCache = null;
56951
57118
  var init_level_service = __esm(() => {
56952
57119
  init_drizzle_orm();
@@ -56954,7 +57121,7 @@ var init_level_service = __esm(() => {
56954
57121
  init_tables_index();
56955
57122
  init_src2();
56956
57123
  init_errors();
56957
- logger22 = log.scope("LevelService");
57124
+ logger23 = log.scope("LevelService");
56958
57125
  });
56959
57126
  var init_events = () => {};
56960
57127
  function convertWebSocketUrlToHttp(wsUrl) {
@@ -56971,13 +57138,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
56971
57138
  });
56972
57139
  if (!res.ok) {
56973
57140
  const text3 = await res.text().catch(() => "");
56974
- logger23.warn("Failed to publish to user", {
57141
+ logger24.warn("Failed to publish to user", {
56975
57142
  status: res.status,
56976
57143
  body: text3
56977
57144
  });
56978
57145
  }
56979
57146
  } catch (error) {
56980
- logger23.error("Publish to user error", { error });
57147
+ logger24.error("Publish to user error", { error });
56981
57148
  }
56982
57149
  }
56983
57150
 
@@ -56999,7 +57166,7 @@ class NotificationService {
56999
57166
  conditions2.push(eq(notifications.type, type));
57000
57167
  }
57001
57168
  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 });
57169
+ logger24.debug("Listed notifications", { userId: user.id, count: results.length });
57003
57170
  return results;
57004
57171
  }
57005
57172
  async updateStatus(notificationId, status, method) {
@@ -57018,7 +57185,7 @@ class NotificationService {
57018
57185
  if (!updated) {
57019
57186
  throw new NotFoundError("Notification", notificationId);
57020
57187
  }
57021
- logger23.debug("Updated status", { notificationId, status });
57188
+ logger24.debug("Updated status", { notificationId, status });
57022
57189
  return updated;
57023
57190
  }
57024
57191
  async getStats(user, options) {
@@ -57044,7 +57211,7 @@ class NotificationService {
57044
57211
  const clicked = statsMap.clicked || 0;
57045
57212
  const dismissed = statsMap.dismissed || 0;
57046
57213
  const expired = statsMap.expired || 0;
57047
- logger23.debug("Retrieved stats", { userId: user.id, total });
57214
+ logger24.debug("Retrieved stats", { userId: user.id, total });
57048
57215
  return {
57049
57216
  total,
57050
57217
  delivered,
@@ -57093,7 +57260,7 @@ class NotificationService {
57093
57260
  options: { data, clickUrl, metadata: metadata2 }
57094
57261
  });
57095
57262
  }
57096
- logger23.debug("Created notification", {
57263
+ logger24.debug("Created notification", {
57097
57264
  userId,
57098
57265
  type,
57099
57266
  id: notificationId,
@@ -57101,7 +57268,7 @@ class NotificationService {
57101
57268
  });
57102
57269
  return notificationId;
57103
57270
  } catch (error) {
57104
- logger23.error("Failed to create notification", { userId, type, error });
57271
+ logger24.error("Failed to create notification", { userId, type, error });
57105
57272
  return null;
57106
57273
  }
57107
57274
  }
@@ -57115,7 +57282,7 @@ class NotificationService {
57115
57282
  }) {
57116
57283
  const realtimeConfig = this.deps.realtime;
57117
57284
  if (!realtimeConfig) {
57118
- logger23.warn("No realtime config for publish");
57285
+ logger24.warn("No realtime config for publish");
57119
57286
  return;
57120
57287
  }
57121
57288
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -57157,13 +57324,13 @@ class NotificationService {
57157
57324
  metadata: data.metadata || {}
57158
57325
  }).returning();
57159
57326
  if (!notification) {
57160
- logger23.error("Notification insert returned no rows", {
57327
+ logger24.error("Notification insert returned no rows", {
57161
57328
  userId: data.userId,
57162
57329
  type: data.type
57163
57330
  });
57164
57331
  throw new InternalError("Failed to create notification");
57165
57332
  }
57166
- logger23.info("Inserted notification", {
57333
+ logger24.info("Inserted notification", {
57167
57334
  notificationId: notification.id,
57168
57335
  userId: notification.userId,
57169
57336
  type: notification.type
@@ -57173,7 +57340,7 @@ class NotificationService {
57173
57340
  async deliverPending(userId) {
57174
57341
  const realtimeConfig = this.deps.realtime;
57175
57342
  if (!realtimeConfig) {
57176
- logger23.warn("No realtime config for delivery");
57343
+ logger24.warn("No realtime config for delivery");
57177
57344
  return;
57178
57345
  }
57179
57346
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -57200,13 +57367,13 @@ class NotificationService {
57200
57367
  metadata: notification.metadata,
57201
57368
  clickUrl: notification.clickUrl
57202
57369
  });
57203
- logger23.info("Delivered notification", {
57370
+ logger24.info("Delivered notification", {
57204
57371
  notificationId: notification.id,
57205
57372
  userId,
57206
57373
  type: notification.type
57207
57374
  });
57208
57375
  } catch (error) {
57209
- logger23.warn("Failed to deliver", {
57376
+ logger24.warn("Failed to deliver", {
57210
57377
  notificationId: notification.id,
57211
57378
  error
57212
57379
  });
@@ -57214,7 +57381,7 @@ class NotificationService {
57214
57381
  }
57215
57382
  }
57216
57383
  }
57217
- var logger23;
57384
+ var logger24;
57218
57385
  var init_notification_service = __esm(() => {
57219
57386
  init_drizzle_orm();
57220
57387
  init_src();
@@ -57223,7 +57390,7 @@ var init_notification_service = __esm(() => {
57223
57390
  init_events();
57224
57391
  init_notification();
57225
57392
  init_errors();
57226
- logger23 = log.scope("NotificationService");
57393
+ logger24 = log.scope("NotificationService");
57227
57394
  });
57228
57395
  function createPlayerServices(deps) {
57229
57396
  const { db: db2, realtime } = deps;
@@ -57342,7 +57509,7 @@ class CharacterService {
57342
57509
  createdAt: characterComponents.createdAt,
57343
57510
  updatedAt: characterComponents.updatedAt
57344
57511
  }).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", {
57512
+ logger25.debug("Listed available components", {
57346
57513
  level,
57347
57514
  count: components.length
57348
57515
  });
@@ -57360,7 +57527,7 @@ class CharacterService {
57360
57527
  }
57361
57528
  }
57362
57529
  });
57363
- logger24.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
57530
+ logger25.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
57364
57531
  return pc3 ?? null;
57365
57532
  }
57366
57533
  async getByUserId(userId) {
@@ -57375,7 +57542,7 @@ class CharacterService {
57375
57542
  }
57376
57543
  }
57377
57544
  });
57378
- logger24.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
57545
+ logger25.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
57379
57546
  return pc3 ?? null;
57380
57547
  }
57381
57548
  async create(input, user) {
@@ -57390,13 +57557,13 @@ class CharacterService {
57390
57557
  }
57391
57558
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
57392
57559
  if (!characterRow) {
57393
- logger24.error("Character insert returned no rows", { userId: user.id });
57560
+ logger25.error("Character insert returned no rows", { userId: user.id });
57394
57561
  throw new InternalError("Failed to create character in database");
57395
57562
  }
57396
57563
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
57397
57564
  return characterRow;
57398
57565
  });
57399
- logger24.info("Created character", { userId: user.id, characterId: result.id });
57566
+ logger25.info("Created character", { userId: user.id, characterId: result.id });
57400
57567
  return result;
57401
57568
  }
57402
57569
  async update(input, user) {
@@ -57408,7 +57575,7 @@ class CharacterService {
57408
57575
  if (!row) {
57409
57576
  throw new NotFoundError("Player character");
57410
57577
  }
57411
- logger24.info("Updated character", {
57578
+ logger25.info("Updated character", {
57412
57579
  userId: user.id,
57413
57580
  characterId: row.id,
57414
57581
  updatedFields: Object.keys(input)
@@ -57430,7 +57597,7 @@ class CharacterService {
57430
57597
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
57431
57598
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
57432
57599
  if (!validation.isValid) {
57433
- logger24.warn("Accessory validation failed", {
57600
+ logger25.warn("Accessory validation failed", {
57434
57601
  userId: user.id,
57435
57602
  slot,
57436
57603
  accessoryComponentId,
@@ -57446,14 +57613,14 @@ class CharacterService {
57446
57613
  slot
57447
57614
  }).returning();
57448
57615
  if (!result) {
57449
- logger24.error("Accessory insert returned no rows", {
57616
+ logger25.error("Accessory insert returned no rows", {
57450
57617
  userId: user.id,
57451
57618
  slot,
57452
57619
  accessoryComponentId
57453
57620
  });
57454
57621
  throw new InternalError("Failed to equip accessory");
57455
57622
  }
57456
- logger24.info("Equipped accessory", {
57623
+ logger25.info("Equipped accessory", {
57457
57624
  userId: user.id,
57458
57625
  slot,
57459
57626
  accessoryComponentId
@@ -57474,7 +57641,7 @@ class CharacterService {
57474
57641
  const playerLevel = userLevel?.currentLevel ?? 1;
57475
57642
  const validation = validateAccessoryRemoval(slot, playerLevel);
57476
57643
  if (!validation.isValid) {
57477
- logger24.warn("Accessory removal validation failed", {
57644
+ logger25.warn("Accessory removal validation failed", {
57478
57645
  userId: user.id,
57479
57646
  slot,
57480
57647
  playerLevel,
@@ -57483,17 +57650,17 @@ class CharacterService {
57483
57650
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
57484
57651
  }
57485
57652
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
57486
- logger24.info("Removed accessory", { userId: user.id, slot });
57653
+ logger25.info("Removed accessory", { userId: user.id, slot });
57487
57654
  }
57488
57655
  }
57489
- var logger24;
57656
+ var logger25;
57490
57657
  var init_character_service = __esm(() => {
57491
57658
  init_drizzle_orm();
57492
57659
  init_tables_index();
57493
57660
  init_src2();
57494
57661
  init_errors();
57495
57662
  init_accessory_util();
57496
- logger24 = log.scope("CharacterService");
57663
+ logger25 = log.scope("CharacterService");
57497
57664
  });
57498
57665
 
57499
57666
  class CurrencyService {
@@ -57504,7 +57671,7 @@ class CurrencyService {
57504
57671
  async list() {
57505
57672
  const db2 = this.deps.db;
57506
57673
  const allCurrencies = await db2.query.currencies.findMany();
57507
- logger25.debug("Listed currencies", { count: allCurrencies.length });
57674
+ logger26.debug("Listed currencies", { count: allCurrencies.length });
57508
57675
  return allCurrencies;
57509
57676
  }
57510
57677
  async getById(currencyId) {
@@ -57515,7 +57682,7 @@ class CurrencyService {
57515
57682
  if (!currency) {
57516
57683
  throw new NotFoundError("Currency", currencyId);
57517
57684
  }
57518
- logger25.debug("Retrieved currency", { currencyId });
57685
+ logger26.debug("Retrieved currency", { currencyId });
57519
57686
  return currency;
57520
57687
  }
57521
57688
  async create(data) {
@@ -57523,13 +57690,13 @@ class CurrencyService {
57523
57690
  try {
57524
57691
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
57525
57692
  if (!newCurrency) {
57526
- logger25.error("Currency insert returned no rows", {
57693
+ logger26.error("Currency insert returned no rows", {
57527
57694
  itemId: data.itemId,
57528
57695
  symbol: data.symbol
57529
57696
  });
57530
57697
  throw new InternalError("Failed to create currency");
57531
57698
  }
57532
- logger25.info("Created currency", {
57699
+ logger26.info("Created currency", {
57533
57700
  currencyId: newCurrency.id,
57534
57701
  itemId: newCurrency.itemId,
57535
57702
  symbol: newCurrency.symbol,
@@ -57558,7 +57725,7 @@ class CurrencyService {
57558
57725
  if (!updatedCurrency) {
57559
57726
  throw new NotFoundError("Currency", currencyId);
57560
57727
  }
57561
- logger25.info("Updated currency", {
57728
+ logger26.info("Updated currency", {
57562
57729
  currencyId: updatedCurrency.id,
57563
57730
  updatedFields: Object.keys(data)
57564
57731
  });
@@ -57584,16 +57751,16 @@ class CurrencyService {
57584
57751
  if (result.length === 0) {
57585
57752
  throw new NotFoundError("Currency", currencyId);
57586
57753
  }
57587
- logger25.info("Deleted currency", { currencyId });
57754
+ logger26.info("Deleted currency", { currencyId });
57588
57755
  }
57589
57756
  }
57590
- var logger25;
57757
+ var logger26;
57591
57758
  var init_currency_service = __esm(() => {
57592
57759
  init_drizzle_orm();
57593
57760
  init_tables_index();
57594
57761
  init_src2();
57595
57762
  init_errors();
57596
- logger25 = log.scope("CurrencyService");
57763
+ logger26 = log.scope("CurrencyService");
57597
57764
  });
57598
57765
 
57599
57766
  class LogsService {
@@ -57611,11 +57778,11 @@ class LogsService {
57611
57778
  if (!game) {
57612
57779
  throw new NotFoundError("Game", slug2);
57613
57780
  }
57614
- logger26.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
57781
+ logger27.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
57615
57782
  } else {
57616
57783
  const isApprovedDev = user.developerStatus === "approved";
57617
57784
  if (!isApprovedDev) {
57618
- logger26.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
57785
+ logger27.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
57619
57786
  throw new AccessDeniedError("Must be an approved developer");
57620
57787
  }
57621
57788
  const game = await db2.query.games.findFirst({
@@ -57623,7 +57790,7 @@ class LogsService {
57623
57790
  columns: { id: true }
57624
57791
  });
57625
57792
  if (!game) {
57626
- logger26.warn("Developer attempted access to unowned game logs", {
57793
+ logger27.warn("Developer attempted access to unowned game logs", {
57627
57794
  userId: user.id,
57628
57795
  slug: slug2
57629
57796
  });
@@ -57633,7 +57800,7 @@ class LogsService {
57633
57800
  const isProduction3 = environment === "production";
57634
57801
  const workerId = getDeploymentId(slug2, isProduction3);
57635
57802
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
57636
- logger26.debug("Generated log stream token", {
57803
+ logger27.debug("Generated log stream token", {
57637
57804
  userId: user.id,
57638
57805
  slug: slug2,
57639
57806
  workerId
@@ -57641,14 +57808,14 @@ class LogsService {
57641
57808
  return { token, workerId };
57642
57809
  }
57643
57810
  }
57644
- var logger26;
57811
+ var logger27;
57645
57812
  var init_logs_service = __esm(() => {
57646
57813
  init_drizzle_orm();
57647
57814
  init_tables_index();
57648
57815
  init_src2();
57649
57816
  init_errors();
57650
57817
  init_deployment_util();
57651
- logger26 = log.scope("LogsService");
57818
+ logger27 = log.scope("LogsService");
57652
57819
  });
57653
57820
 
57654
57821
  class LtiService {
@@ -57699,7 +57866,7 @@ class MapService {
57699
57866
  if (!mapDetails) {
57700
57867
  throw new NotFoundError("Map", identifier);
57701
57868
  }
57702
- logger27.debug("Retrieved map", { identifier });
57869
+ logger28.debug("Retrieved map", { identifier });
57703
57870
  return mapDetails;
57704
57871
  }
57705
57872
  async getElements(mapId) {
@@ -57715,7 +57882,7 @@ class MapService {
57715
57882
  }
57716
57883
  }
57717
57884
  });
57718
- logger27.debug("Retrieved elements", { mapId, count: elements.length });
57885
+ logger28.debug("Retrieved elements", { mapId, count: elements.length });
57719
57886
  return elements;
57720
57887
  }
57721
57888
  async getObjects(mapId, userId) {
@@ -57736,7 +57903,7 @@ class MapService {
57736
57903
  }
57737
57904
  }
57738
57905
  });
57739
- logger27.debug("Retrieved objects", { mapId, userId, count: objects.length });
57906
+ logger28.debug("Retrieved objects", { mapId, userId, count: objects.length });
57740
57907
  return objects.map((object) => this.formatMapObjectWithItem(object));
57741
57908
  }
57742
57909
  async createObject(mapId, data, user) {
@@ -57763,7 +57930,7 @@ class MapService {
57763
57930
  throw new NotFoundError("Item", data.itemId);
57764
57931
  }
57765
57932
  if (!item.isPlaceable) {
57766
- logger27.warn("Attempted to place non-placeable item", {
57933
+ logger28.warn("Attempted to place non-placeable item", {
57767
57934
  userId: user.id,
57768
57935
  itemId: data.itemId,
57769
57936
  mapId
@@ -57777,7 +57944,7 @@ class MapService {
57777
57944
  };
57778
57945
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
57779
57946
  if (!createdObject) {
57780
- logger27.error("Map object insert returned no rows", {
57947
+ logger28.error("Map object insert returned no rows", {
57781
57948
  userId: user.id,
57782
57949
  mapId,
57783
57950
  itemId: data.itemId
@@ -57801,12 +57968,12 @@ class MapService {
57801
57968
  }
57802
57969
  });
57803
57970
  if (!objectWithItem) {
57804
- logger27.error("Map object query after insert returned no rows", {
57971
+ logger28.error("Map object query after insert returned no rows", {
57805
57972
  objectId: createdObject.id
57806
57973
  });
57807
57974
  throw new InternalError("Failed to retrieve created object");
57808
57975
  }
57809
- logger27.info("Created object", {
57976
+ logger28.info("Created object", {
57810
57977
  userId: user.id,
57811
57978
  mapId,
57812
57979
  objectId: createdObject.id,
@@ -57830,7 +57997,7 @@ class MapService {
57830
57997
  if (result.length === 0) {
57831
57998
  throw new NotFoundError("MapObject", objectId);
57832
57999
  }
57833
- logger27.info("Deleted object", {
58000
+ logger28.info("Deleted object", {
57834
58001
  userId: user.id,
57835
58002
  mapId,
57836
58003
  objectId
@@ -57859,13 +58026,13 @@ class MapService {
57859
58026
  };
57860
58027
  }
57861
58028
  }
57862
- var logger27;
58029
+ var logger28;
57863
58030
  var init_map_service = __esm(() => {
57864
58031
  init_drizzle_orm();
57865
58032
  init_tables_index();
57866
58033
  init_src2();
57867
58034
  init_errors();
57868
- logger27 = log.scope("MapService");
58035
+ logger28 = log.scope("MapService");
57869
58036
  });
57870
58037
 
57871
58038
  class RealtimeService {
@@ -57899,20 +58066,20 @@ class RealtimeService {
57899
58066
  }
57900
58067
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
57901
58068
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
57902
- logger28.info("Generated token", {
58069
+ logger29.info("Generated token", {
57903
58070
  userId: user.id,
57904
58071
  gameId: resolvedGameId || "global"
57905
58072
  });
57906
58073
  return { token };
57907
58074
  }
57908
58075
  }
57909
- var logger28;
58076
+ var logger29;
57910
58077
  var init_realtime_service = __esm(() => {
57911
58078
  init_drizzle_orm();
57912
58079
  init_tables_index();
57913
58080
  init_src2();
57914
58081
  init_errors();
57915
- logger28 = log.scope("RealtimeService");
58082
+ logger29 = log.scope("RealtimeService");
57916
58083
  });
57917
58084
 
57918
58085
  class SessionService {
@@ -57946,10 +58113,10 @@ class SessionService {
57946
58113
  };
57947
58114
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
57948
58115
  if (!newSession?.sessionId) {
57949
- logger29.error("Game session insert returned no rows", { userId, gameId });
58116
+ logger30.error("Game session insert returned no rows", { userId, gameId });
57950
58117
  throw new InternalError("Failed to create game session");
57951
58118
  }
57952
- logger29.info("Started new session", {
58119
+ logger30.info("Started new session", {
57953
58120
  sessionId: newSession.sessionId,
57954
58121
  gameId,
57955
58122
  userId
@@ -57970,23 +58137,23 @@ class SessionService {
57970
58137
  return { success: true, message: "Session already ended" };
57971
58138
  }
57972
58139
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
57973
- logger29.info("Ended session", { sessionId, gameId, userId });
58140
+ logger30.info("Ended session", { sessionId, gameId, userId });
57974
58141
  return { success: true };
57975
58142
  }
57976
58143
  async mintToken(gameIdOrSlug, userId) {
57977
58144
  const gameId = await this.resolveGameId(gameIdOrSlug);
57978
58145
  const result = await this.deps.mintGameToken(gameId, userId);
57979
- logger29.debug("Minted game token", { gameId, userId });
58146
+ logger30.debug("Minted game token", { gameId, userId });
57980
58147
  return result;
57981
58148
  }
57982
58149
  }
57983
- var logger29;
58150
+ var logger30;
57984
58151
  var init_session_service = __esm(() => {
57985
58152
  init_drizzle_orm();
57986
58153
  init_tables_index();
57987
58154
  init_src2();
57988
58155
  init_errors();
57989
- logger29 = log.scope("SessionService");
58156
+ logger30 = log.scope("SessionService");
57990
58157
  });
57991
58158
 
57992
58159
  class ShopService {
@@ -58031,7 +58198,7 @@ class ShopService {
58031
58198
  const shopItems = [];
58032
58199
  for (const listing of listingsWithRelations) {
58033
58200
  if (!listing.item || !listing.currency) {
58034
- logger30.warn("Listing missing item or currency, skipping", {
58201
+ logger31.warn("Listing missing item or currency, skipping", {
58035
58202
  listingId: listing.id
58036
58203
  });
58037
58204
  } else {
@@ -58048,7 +58215,7 @@ class ShopService {
58048
58215
  });
58049
58216
  }
58050
58217
  }
58051
- logger30.debug("Retrieved shop view", {
58218
+ logger31.debug("Retrieved shop view", {
58052
58219
  userId: user.id,
58053
58220
  itemCount: shopItems.length,
58054
58221
  currencyCount: shopCurrencies.length
@@ -58059,12 +58226,12 @@ class ShopService {
58059
58226
  };
58060
58227
  }
58061
58228
  }
58062
- var logger30;
58229
+ var logger31;
58063
58230
  var init_shop_service = __esm(() => {
58064
58231
  init_drizzle_orm();
58065
58232
  init_tables_index();
58066
58233
  init_src2();
58067
- logger30 = log.scope("ShopService");
58234
+ logger31 = log.scope("ShopService");
58068
58235
  });
58069
58236
 
58070
58237
  class SpriteService {
@@ -58080,17 +58247,17 @@ class SpriteService {
58080
58247
  if (!template) {
58081
58248
  throw new NotFoundError("SpriteTemplate", slug2);
58082
58249
  }
58083
- logger31.debug("Retrieved sprite", { slug: slug2 });
58250
+ logger32.debug("Retrieved sprite", { slug: slug2 });
58084
58251
  return template;
58085
58252
  }
58086
58253
  }
58087
- var logger31;
58254
+ var logger32;
58088
58255
  var init_sprite_service = __esm(() => {
58089
58256
  init_drizzle_orm();
58090
58257
  init_tables_index();
58091
58258
  init_src2();
58092
58259
  init_errors();
58093
- logger31 = log.scope("SpriteService");
58260
+ logger32 = log.scope("SpriteService");
58094
58261
  });
58095
58262
 
58096
58263
  class UserService {
@@ -58104,12 +58271,12 @@ class UserService {
58104
58271
  where: eq(users.id, user.id)
58105
58272
  });
58106
58273
  if (!userData) {
58107
- logger32.error("User not found", { userId: user.id });
58274
+ logger33.error("User not found", { userId: user.id });
58108
58275
  throw new NotFoundError("User", user.id);
58109
58276
  }
58110
58277
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
58111
58278
  if (gameId) {
58112
- logger32.debug("Fetched user profile (game context)", { userId: user.id, gameId });
58279
+ logger33.debug("Fetched user profile (game context)", { userId: user.id, gameId });
58113
58280
  return {
58114
58281
  id: userData.id,
58115
58282
  name: userData.name,
@@ -58122,7 +58289,7 @@ class UserService {
58122
58289
  const timebackAccount = await db2.query.accounts.findFirst({
58123
58290
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
58124
58291
  });
58125
- logger32.debug("Fetched user profile (platform context)", { userId: user.id });
58292
+ logger33.debug("Fetched user profile (platform context)", { userId: user.id });
58126
58293
  return {
58127
58294
  id: userData.id,
58128
58295
  name: userData.name,
@@ -58145,7 +58312,7 @@ class UserService {
58145
58312
  columns: { name: true }
58146
58313
  });
58147
58314
  if (!userData) {
58148
- logger32.error("Demo user not found", { userId });
58315
+ logger33.error("Demo user not found", { userId });
58149
58316
  throw new NotFoundError("User", userId);
58150
58317
  }
58151
58318
  return {
@@ -58159,10 +58326,10 @@ class UserService {
58159
58326
  updatedAt: new Date
58160
58327
  }).where(eq(users.id, userId)).returning({ name: users.name });
58161
58328
  if (!updatedUser) {
58162
- logger32.error("Demo user not found for profile update", { userId });
58329
+ logger33.error("Demo user not found for profile update", { userId });
58163
58330
  throw new NotFoundError("User", userId);
58164
58331
  }
58165
- logger32.debug("Updated demo profile", { userId, displayName });
58332
+ logger33.debug("Updated demo profile", { userId, displayName });
58166
58333
  return {
58167
58334
  displayName: updatedUser.name,
58168
58335
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -58175,7 +58342,7 @@ class UserService {
58175
58342
  ]);
58176
58343
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
58177
58344
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
58178
- logger32.debug("Fetched Timeback data", {
58345
+ logger33.debug("Fetched Timeback data", {
58179
58346
  timebackId,
58180
58347
  role,
58181
58348
  enrollmentCount: enrollments.length,
@@ -58184,9 +58351,9 @@ class UserService {
58184
58351
  return { id: timebackId, role, enrollments, organizations };
58185
58352
  }
58186
58353
  async fetchStudentProfile(timebackId) {
58187
- logger32.debug("Fetching student profile", { timebackId });
58354
+ logger33.debug("Fetching student profile", { timebackId });
58188
58355
  if (!this.deps.timeback) {
58189
- logger32.warn("Timeback client not available");
58356
+ logger33.warn("Timeback client not available");
58190
58357
  return { role: "student", organizations: [] };
58191
58358
  }
58192
58359
  try {
@@ -58214,14 +58381,14 @@ class UserService {
58214
58381
  }
58215
58382
  return { role, organizations: [...orgMap.values()] };
58216
58383
  } catch (error) {
58217
- logger32.warn("Failed to fetch student profile", { error, timebackId });
58384
+ logger33.warn("Failed to fetch student profile", { error, timebackId });
58218
58385
  return { role: "student", organizations: [] };
58219
58386
  }
58220
58387
  }
58221
58388
  async fetchEnrollments(timebackId) {
58222
- logger32.debug("Fetching enrollments", { timebackId });
58389
+ logger33.debug("Fetching enrollments", { timebackId });
58223
58390
  if (!this.deps.timeback) {
58224
- logger32.warn("Timeback client not available");
58391
+ logger33.warn("Timeback client not available");
58225
58392
  return [];
58226
58393
  }
58227
58394
  try {
@@ -58242,7 +58409,7 @@ class UserService {
58242
58409
  orgId: courseToSchool.get(i2.courseId)
58243
58410
  }));
58244
58411
  } catch (error) {
58245
- logger32.warn("Failed to fetch enrollments", { error, timebackId });
58412
+ logger33.warn("Failed to fetch enrollments", { error, timebackId });
58246
58413
  return [];
58247
58414
  }
58248
58415
  }
@@ -58257,14 +58424,14 @@ class UserService {
58257
58424
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
58258
58425
  }
58259
58426
  }
58260
- var logger32;
58427
+ var logger33;
58261
58428
  var init_user_service = __esm(() => {
58262
58429
  init_drizzle_orm();
58263
58430
  init_src();
58264
58431
  init_tables_index();
58265
58432
  init_src2();
58266
58433
  init_errors();
58267
- logger32 = log.scope("UserService");
58434
+ logger33 = log.scope("UserService");
58268
58435
  });
58269
58436
 
58270
58437
  class VerifyService {
@@ -58273,16 +58440,16 @@ class VerifyService {
58273
58440
  this.deps = deps;
58274
58441
  }
58275
58442
  async verifyGameToken(token) {
58276
- logger33.debug("Verifying game token");
58443
+ logger34.debug("Verifying game token");
58277
58444
  const payload = await this.deps.validateGameToken(token);
58278
58445
  if (!payload) {
58279
- logger33.warn("Invalid or expired game token presented");
58446
+ logger34.warn("Invalid or expired game token presented");
58280
58447
  throw new ValidationError("Invalid or expired token");
58281
58448
  }
58282
58449
  const gameId = payload.sub;
58283
58450
  const userId = payload.uid;
58284
58451
  if (typeof gameId !== "string" || typeof userId !== "string") {
58285
- logger33.warn("Game token missing required claims", {
58452
+ logger34.warn("Game token missing required claims", {
58286
58453
  hasGameId: typeof gameId === "string",
58287
58454
  hasUserId: typeof userId === "string"
58288
58455
  });
@@ -58293,7 +58460,7 @@ class VerifyService {
58293
58460
  where: eq(users.id, userId)
58294
58461
  });
58295
58462
  if (!userData) {
58296
- logger33.error("User not found for valid token", {
58463
+ logger34.error("User not found for valid token", {
58297
58464
  userId
58298
58465
  });
58299
58466
  throw new NotFoundError("User", userId);
@@ -58307,7 +58474,7 @@ class VerifyService {
58307
58474
  family_name: undefined,
58308
58475
  timeback_id: userData.timebackId || undefined
58309
58476
  };
58310
- logger33.info("Token verified", { gameId, userId });
58477
+ logger34.info("Token verified", { gameId, userId });
58311
58478
  return {
58312
58479
  claims: payload,
58313
58480
  gameId,
@@ -58315,13 +58482,13 @@ class VerifyService {
58315
58482
  };
58316
58483
  }
58317
58484
  }
58318
- var logger33;
58485
+ var logger34;
58319
58486
  var init_verify_service = __esm(() => {
58320
58487
  init_drizzle_orm();
58321
58488
  init_tables_index();
58322
58489
  init_src2();
58323
58490
  init_errors();
58324
- logger33 = log.scope("VerifyService");
58491
+ logger34 = log.scope("VerifyService");
58325
58492
  });
58326
58493
  function createStandaloneServices(deps) {
58327
58494
  const { db: db2, auth: auth2, timeback: timeback2 } = deps;
@@ -59901,6 +60068,9 @@ class TimebackCacheManager {
59901
60068
  setEnrollments(studentId, enrollments) {
59902
60069
  this.enrollmentCache.set(studentId, enrollments);
59903
60070
  }
60071
+ clearEnrollments(studentId) {
60072
+ return this.enrollmentCache.delete(studentId);
60073
+ }
59904
60074
  clearAll() {
59905
60075
  this.studentCache.clear();
59906
60076
  this.assessmentLineItemCache.clear();
@@ -59939,6 +60109,41 @@ class MasteryTracker {
59939
60109
  if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
59940
60110
  return;
59941
60111
  }
60112
+ const status = await this.calculateStatus({
60113
+ studentId,
60114
+ courseId,
60115
+ resourceId,
60116
+ additionalMasteredUnits: masteredUnits
60117
+ });
60118
+ if (!status) {
60119
+ return;
60120
+ }
60121
+ return {
60122
+ pctCompleteApp: status.pctCompleteApp,
60123
+ masteryAchieved: status.historicalMasteredUnits < status.masterableUnits && status.isComplete
60124
+ };
60125
+ }
60126
+ async getStatus(input) {
60127
+ const status = await this.calculateStatus({
60128
+ ...input,
60129
+ additionalMasteredUnits: 0
60130
+ });
60131
+ if (!status) {
60132
+ return;
60133
+ }
60134
+ return {
60135
+ masteredUnits: status.masteredUnits,
60136
+ masterableUnits: status.masterableUnits,
60137
+ pctCompleteApp: status.pctCompleteApp,
60138
+ isComplete: status.isComplete
60139
+ };
60140
+ }
60141
+ async calculateStatus({
60142
+ studentId,
60143
+ courseId,
60144
+ resourceId,
60145
+ additionalMasteredUnits
60146
+ }) {
59942
60147
  const masterableUnits = await this.resolveMasterableUnits(resourceId);
59943
60148
  if (!masterableUnits || masterableUnits <= 0) {
59944
60149
  log.warn("[MasteryTracker] No masterableUnits configured for course", {
@@ -59956,11 +60161,16 @@ class MasteryTracker {
59956
60161
  return;
59957
60162
  }
59958
60163
  const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
59959
- const totalMastered = historicalMasteredUnits + masteredUnits;
60164
+ const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
59960
60165
  const rawPct = totalMastered / masterableUnits * 100;
59961
60166
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
59962
- const masteryAchieved = historicalMasteredUnits < masterableUnits && totalMastered >= masterableUnits;
59963
- return { pctCompleteApp, masteryAchieved };
60167
+ return {
60168
+ masteredUnits: totalMastered,
60169
+ masterableUnits,
60170
+ pctCompleteApp,
60171
+ isComplete: totalMastered >= masterableUnits,
60172
+ historicalMasteredUnits
60173
+ };
59964
60174
  }
59965
60175
  async createCompletionEntry(studentId, courseId, classId, appName) {
59966
60176
  const ids = deriveSourcedIds2(courseId);
@@ -60556,6 +60766,7 @@ class TimebackClient {
60556
60766
  progressRecorder;
60557
60767
  sessionRecorder;
60558
60768
  adminEventRecorder;
60769
+ masteryTracker;
60559
60770
  constructor(config2) {
60560
60771
  this.baseUrl = TimebackClient.resolveBaseUrl(config2?.baseUrl);
60561
60772
  this.environment = process.env[ENV_VARS4.environment] === "staging" ? "staging" : "production";
@@ -60573,8 +60784,8 @@ class TimebackClient {
60573
60784
  this.edubridge = createEduBridgeNamespace(this);
60574
60785
  this.cacheManager = new TimebackCacheManager;
60575
60786
  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);
60787
+ this.masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
60788
+ this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, this.masteryTracker);
60578
60789
  this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
60579
60790
  this.adminEventRecorder = new AdminEventRecorder(this.studentResolver, this.oneroster, this.caliper, this.environment);
60580
60791
  if (this.credentials) {
@@ -60732,6 +60943,18 @@ class TimebackClient {
60732
60943
  this.cacheManager.setEnrollments(studentId, enrollments);
60733
60944
  return enrollments;
60734
60945
  }
60946
+ invalidateEnrollments(studentId) {
60947
+ this.cacheManager.clearEnrollments(studentId);
60948
+ }
60949
+ async getMasteryStatus(courseId, studentId) {
60950
+ await this._ensureAuthenticated();
60951
+ const ids = deriveSourcedIds2(courseId);
60952
+ return this.masteryTracker.getStatus({
60953
+ studentId,
60954
+ courseId,
60955
+ resourceId: ids.resource
60956
+ });
60957
+ }
60735
60958
  async getStudentXp(studentId, options) {
60736
60959
  await this._ensureAuthenticated();
60737
60960
  const enrollments = await this.edubridge.enrollments.listByUser(studentId);
@@ -63277,7 +63500,7 @@ var colorStatus = async (status) => {
63277
63500
  }
63278
63501
  return `${status}`;
63279
63502
  };
63280
- var logger34 = (fn = console.log) => {
63503
+ var logger35 = (fn = console.log) => {
63281
63504
  return async function logger2(c, next) {
63282
63505
  const { method, url } = c.req;
63283
63506
  const path2 = url.slice(url.indexOf("/", 8));
@@ -63454,7 +63677,7 @@ function createApp(db2, options) {
63454
63677
  const app = new Hono2;
63455
63678
  app.use("*", cors({ origin: "*", credentials: true }));
63456
63679
  if (options.verbose && !options.quiet) {
63457
- app.use("*", logger34());
63680
+ app.use("*", logger35());
63458
63681
  }
63459
63682
  app.use("/api/*", async (c, next) => {
63460
63683
  c.set("db", db2);
@@ -70156,12 +70379,12 @@ var init_session2 = __esm(() => {
70156
70379
  init_utils();
70157
70380
  init_dist5();
70158
70381
  PglitePreparedQuery = class PglitePreparedQuery2 extends PgPreparedQuery {
70159
- constructor(client, queryString, params, logger35, fields, name3, _isResponseInArrayMode, customResultMapper) {
70382
+ constructor(client, queryString, params, logger36, fields, name3, _isResponseInArrayMode, customResultMapper) {
70160
70383
  super({ sql: queryString, params });
70161
70384
  this.client = client;
70162
70385
  this.queryString = queryString;
70163
70386
  this.params = params;
70164
- this.logger = logger35;
70387
+ this.logger = logger36;
70165
70388
  this.fields = fields;
70166
70389
  this._isResponseInArrayMode = _isResponseInArrayMode;
70167
70390
  this.customResultMapper = customResultMapper;
@@ -70263,11 +70486,11 @@ var init_session2 = __esm(() => {
70263
70486
  });
70264
70487
  function construct(client, config2 = {}) {
70265
70488
  const dialect2 = new PgDialect({ casing: config2.casing });
70266
- let logger35;
70489
+ let logger36;
70267
70490
  if (config2.logger === true) {
70268
- logger35 = new DefaultLogger;
70491
+ logger36 = new DefaultLogger;
70269
70492
  } else if (config2.logger !== false) {
70270
- logger35 = config2.logger;
70493
+ logger36 = config2.logger;
70271
70494
  }
70272
70495
  let schema2;
70273
70496
  if (config2.schema) {
@@ -70278,7 +70501,7 @@ function construct(client, config2 = {}) {
70278
70501
  tableNamesMap: tablesConfig.tableNamesMap
70279
70502
  };
70280
70503
  }
70281
- const driver = new PgliteDriver(client, dialect2, { logger: logger35 });
70504
+ const driver = new PgliteDriver(client, dialect2, { logger: logger36 });
70282
70505
  const session2 = driver.createSession(schema2);
70283
70506
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
70284
70507
  db2.$client = client;
@@ -118923,8 +119146,8 @@ var init_currencies = __esm(() => {
118923
119146
  init_tables_index();
118924
119147
  init_constants();
118925
119148
  });
118926
- function setLogger(logger35) {
118927
- customLogger = logger35;
119149
+ function setLogger(logger36) {
119150
+ customLogger = logger36;
118928
119151
  }
118929
119152
  function getLogger() {
118930
119153
  if (customLogger) {
@@ -118937,10 +119160,10 @@ function getLogger() {
118937
119160
  };
118938
119161
  }
118939
119162
  var customLogger;
118940
- var logger35;
119163
+ var logger36;
118941
119164
  var init_adapter = __esm(() => {
118942
119165
  init_config();
118943
- logger35 = {
119166
+ logger36 = {
118944
119167
  info: (msg) => {
118945
119168
  if (customLogger || !config.embedded) {
118946
119169
  getLogger().info(msg);
@@ -119020,7 +119243,7 @@ async function seedCoreGames(db2) {
119020
119243
  try {
119021
119244
  await db2.insert(games).values(gameData).onConflictDoNothing();
119022
119245
  } catch (error2) {
119023
- logger35.error(`Error seeding core game '${gameData.slug}': ${error2}`);
119246
+ logger36.error(`Error seeding core game '${gameData.slug}': ${error2}`);
119024
119247
  }
119025
119248
  }
119026
119249
  }
@@ -119066,7 +119289,7 @@ async function seedCurrentProjectGame(db2, project) {
119066
119289
  }
119067
119290
  return newGame;
119068
119291
  } catch (error2) {
119069
- logger35.error(`❌ Error seeding project game: ${error2}`);
119292
+ logger36.error(`❌ Error seeding project game: ${error2}`);
119070
119293
  throw error2;
119071
119294
  }
119072
119295
  }
@@ -120427,6 +120650,7 @@ var TimebackSubjectSchema;
120427
120650
  var UpdateTimebackXpRequestSchema;
120428
120651
  var TimebackActivityDataSchema;
120429
120652
  var EndActivityRequestSchema;
120653
+ var AdvanceCourseRequestSchema;
120430
120654
  var HeartbeatRequestSchema;
120431
120655
  var PopulateStudentRequestSchema;
120432
120656
  var DerivedPlatformCourseConfigSchema;
@@ -120494,6 +120718,11 @@ var init_schemas11 = __esm(() => {
120494
120718
  masteredUnits: exports_external.number().nonnegative().optional(),
120495
120719
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
120496
120720
  });
120721
+ AdvanceCourseRequestSchema = exports_external.object({
120722
+ gameId: exports_external.string().uuid(),
120723
+ studentId: exports_external.string().min(1),
120724
+ subject: TimebackSubjectSchema.optional()
120725
+ });
120497
120726
  HeartbeatRequestSchema = exports_external.object({
120498
120727
  gameId: exports_external.string().uuid(),
120499
120728
  studentId: exports_external.string().min(1),
@@ -120763,7 +120992,7 @@ async function provisionLtiUser(db2, claims) {
120763
120992
  where: eq(users.id, existingAccount.userId)
120764
120993
  });
120765
120994
  if (user) {
120766
- logger36.info("Found user by LTI account", {
120995
+ logger37.info("Found user by LTI account", {
120767
120996
  userId: user.id,
120768
120997
  ltiTimebackId
120769
120998
  });
@@ -120792,13 +121021,13 @@ async function provisionLtiUser(db2, claims) {
120792
121021
  updatedAt: new Date
120793
121022
  }).returning({ id: accounts.id });
120794
121023
  if (!account) {
120795
- logger36.error("LTI account link insert returned no rows", {
121024
+ logger37.error("LTI account link insert returned no rows", {
120796
121025
  userId: existingUser.id,
120797
121026
  ltiTimebackId
120798
121027
  });
120799
121028
  throw new InternalError("Failed to link LTI account");
120800
121029
  }
120801
- logger36.info("Linked LTI account to existing user", {
121030
+ logger37.info("Linked LTI account to existing user", {
120802
121031
  userId: existingUser.id,
120803
121032
  ltiTimebackId
120804
121033
  });
@@ -120818,7 +121047,7 @@ async function provisionLtiUser(db2, claims) {
120818
121047
  updatedAt: new Date
120819
121048
  }).returning();
120820
121049
  if (!insertedUser) {
120821
- logger36.error("LTI user insert returned no rows", { email, ltiTimebackId });
121050
+ logger37.error("LTI user insert returned no rows", { email, ltiTimebackId });
120822
121051
  throw new InternalError("Failed to create user");
120823
121052
  }
120824
121053
  await tx.insert(accounts).values({
@@ -120833,7 +121062,7 @@ async function provisionLtiUser(db2, claims) {
120833
121062
  createdAt: new Date,
120834
121063
  updatedAt: new Date
120835
121064
  });
120836
- logger36.info("Provisioned new user from LTI", {
121065
+ logger37.info("Provisioned new user from LTI", {
120837
121066
  userId: insertedUser.id,
120838
121067
  ltiTimebackId
120839
121068
  });
@@ -120841,7 +121070,7 @@ async function provisionLtiUser(db2, claims) {
120841
121070
  });
120842
121071
  return createdUser;
120843
121072
  }
120844
- var logger36;
121073
+ var logger37;
120845
121074
  var init_lti_provisioning = __esm(() => {
120846
121075
  init_drizzle_orm();
120847
121076
  init_src();
@@ -120849,7 +121078,7 @@ var init_lti_provisioning = __esm(() => {
120849
121078
  init_src2();
120850
121079
  init_errors();
120851
121080
  init_lti_util();
120852
- logger36 = log.scope("LtiProvisioning");
121081
+ logger37 = log.scope("LtiProvisioning");
120853
121082
  });
120854
121083
  function formatZodError(error2) {
120855
121084
  const flat = error2.flatten();
@@ -120892,7 +121121,7 @@ var init_utils11 = __esm(() => {
120892
121121
  init_timeback_util();
120893
121122
  init_validation_util();
120894
121123
  });
120895
- var logger37;
121124
+ var logger38;
120896
121125
  var listCurrent;
120897
121126
  var listHistory;
120898
121127
  var postProgress;
@@ -120903,14 +121132,14 @@ var init_achievement_controller = __esm(() => {
120903
121132
  init_src2();
120904
121133
  init_errors();
120905
121134
  init_utils11();
120906
- logger37 = log.scope("AchievementController");
121135
+ logger38 = log.scope("AchievementController");
120907
121136
  listCurrent = requireNonAnonymous(async (ctx) => {
120908
- logger37.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
121137
+ logger38.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
120909
121138
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
120910
121139
  });
120911
121140
  listHistory = requireNonAnonymous(async (ctx) => {
120912
121141
  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 });
121142
+ logger38.debug("Listing achievement history", { userId: ctx.user.id, limit });
120914
121143
  return ctx.services.achievement.listHistory(ctx.user, limit);
120915
121144
  });
120916
121145
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -120921,12 +121150,12 @@ var init_achievement_controller = __esm(() => {
120921
121150
  } catch (error2) {
120922
121151
  if (error2 instanceof exports_external.ZodError) {
120923
121152
  const details = formatZodError(error2);
120924
- logger37.warn("Submit achievement progress validation failed", { details });
121153
+ logger38.warn("Submit achievement progress validation failed", { details });
120925
121154
  throw ApiError.unprocessableEntity("Invalid request body", details);
120926
121155
  }
120927
121156
  throw ApiError.badRequest("Invalid JSON body");
120928
121157
  }
120929
- logger37.debug("Submitting progress", {
121158
+ logger38.debug("Submitting progress", {
120930
121159
  userId: ctx.user.id,
120931
121160
  achievementId: body2.achievementId
120932
121161
  });
@@ -120938,15 +121167,15 @@ var init_achievement_controller = __esm(() => {
120938
121167
  postProgress
120939
121168
  };
120940
121169
  });
120941
- var logger38;
121170
+ var logger39;
120942
121171
  var getAllowedOrigins;
120943
121172
  var init_admin_controller = __esm(() => {
120944
121173
  init_src2();
120945
121174
  init_utils11();
120946
- logger38 = log.scope("AdminController");
121175
+ logger39 = log.scope("AdminController");
120947
121176
  getAllowedOrigins = requireAdmin(async (ctx) => {
120948
121177
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
120949
- logger38.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
121178
+ logger39.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
120950
121179
  if (shouldRefresh) {
120951
121180
  await ctx.providers.cache.refreshGameOrigins();
120952
121181
  }
@@ -120959,7 +121188,7 @@ var init_admin_controller = __esm(() => {
120959
121188
  };
120960
121189
  });
120961
121190
  });
120962
- var logger39;
121191
+ var logger40;
120963
121192
  var listFiles;
120964
121193
  var getFile;
120965
121194
  var putFile;
@@ -120971,7 +121200,7 @@ var init_bucket_controller = __esm(() => {
120971
121200
  init_src2();
120972
121201
  init_errors();
120973
121202
  init_utils11();
120974
- logger39 = log.scope("BucketController");
121203
+ logger40 = log.scope("BucketController");
120975
121204
  listFiles = requireDeveloper(async (ctx) => {
120976
121205
  const slug2 = ctx.params.slug;
120977
121206
  if (!slug2) {
@@ -120979,7 +121208,7 @@ var init_bucket_controller = __esm(() => {
120979
121208
  }
120980
121209
  const url = ctx.url;
120981
121210
  const prefix2 = url.searchParams.get("prefix") || undefined;
120982
- logger39.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
121211
+ logger40.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
120983
121212
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
120984
121213
  return { files };
120985
121214
  });
@@ -120989,7 +121218,7 @@ var init_bucket_controller = __esm(() => {
120989
121218
  if (!slug2 || !key) {
120990
121219
  throw ApiError.badRequest("Missing game slug or file key");
120991
121220
  }
120992
- logger39.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
121221
+ logger40.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
120993
121222
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
120994
121223
  return new Response(Buffer.from(object.body), {
120995
121224
  status: 200,
@@ -121008,7 +121237,7 @@ var init_bucket_controller = __esm(() => {
121008
121237
  const arrayBuffer = await ctx.request.arrayBuffer();
121009
121238
  const body2 = new Uint8Array(arrayBuffer);
121010
121239
  const contentType = ctx.request.headers.get("content-type") || undefined;
121011
- logger39.debug("Uploading file", {
121240
+ logger40.debug("Uploading file", {
121012
121241
  userId: ctx.user.id,
121013
121242
  slug: slug2,
121014
121243
  key,
@@ -121024,7 +121253,7 @@ var init_bucket_controller = __esm(() => {
121024
121253
  if (!slug2 || !key) {
121025
121254
  throw ApiError.badRequest("Missing game slug or file key");
121026
121255
  }
121027
- logger39.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
121256
+ logger40.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
121028
121257
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
121029
121258
  return { success: true, key };
121030
121259
  });
@@ -121036,12 +121265,12 @@ var init_bucket_controller = __esm(() => {
121036
121265
  } catch (error2) {
121037
121266
  if (error2 instanceof exports_external.ZodError) {
121038
121267
  const details = formatZodError(error2);
121039
- logger39.warn("Initiate upload validation failed", { details });
121268
+ logger40.warn("Initiate upload validation failed", { details });
121040
121269
  throw ApiError.unprocessableEntity("Validation failed", details);
121041
121270
  }
121042
121271
  throw ApiError.badRequest("Invalid JSON body");
121043
121272
  }
121044
- logger39.debug("Initiating multipart upload", {
121273
+ logger40.debug("Initiating multipart upload", {
121045
121274
  userId: ctx.user.id,
121046
121275
  gameId: body2.gameId,
121047
121276
  fileName: body2.fileName
@@ -121056,10 +121285,10 @@ async function listComponents(ctx) {
121056
121285
  if (!isNaN(parsed) && isFinite(parsed)) {
121057
121286
  level = Math.floor(Math.max(0, parsed));
121058
121287
  }
121059
- logger40.debug("Listing components", { level });
121288
+ logger41.debug("Listing components", { level });
121060
121289
  return ctx.services.character.listAvailableComponents(level);
121061
121290
  }
121062
- var logger40;
121291
+ var logger41;
121063
121292
  var get;
121064
121293
  var getByUserId;
121065
121294
  var create;
@@ -121073,9 +121302,9 @@ var init_character_controller = __esm(() => {
121073
121302
  init_src2();
121074
121303
  init_errors();
121075
121304
  init_utils11();
121076
- logger40 = log.scope("CharacterController");
121305
+ logger41 = log.scope("CharacterController");
121077
121306
  get = requireNonAnonymous(async (ctx) => {
121078
- logger40.debug("Getting character", { userId: ctx.user.id });
121307
+ logger41.debug("Getting character", { userId: ctx.user.id });
121079
121308
  return ctx.services.character.getByUser(ctx.user);
121080
121309
  });
121081
121310
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -121083,7 +121312,7 @@ var init_character_controller = __esm(() => {
121083
121312
  if (!userId) {
121084
121313
  throw ApiError.badRequest("User ID is required in the URL path");
121085
121314
  }
121086
- logger40.debug("Getting character by user ID", { requestedUserId: userId });
121315
+ logger41.debug("Getting character by user ID", { requestedUserId: userId });
121087
121316
  return ctx.services.character.getByUserId(userId);
121088
121317
  });
121089
121318
  create = requireNonAnonymous(async (ctx) => {
@@ -121094,12 +121323,12 @@ var init_character_controller = __esm(() => {
121094
121323
  } catch (error2) {
121095
121324
  if (error2 instanceof exports_external.ZodError) {
121096
121325
  const details = formatZodError(error2);
121097
- logger40.warn("Create character validation failed", { details });
121326
+ logger41.warn("Create character validation failed", { details });
121098
121327
  throw ApiError.unprocessableEntity("Invalid request body", details);
121099
121328
  }
121100
121329
  throw ApiError.badRequest("Invalid JSON body");
121101
121330
  }
121102
- logger40.debug("Creating character", {
121331
+ logger41.debug("Creating character", {
121103
121332
  userId: ctx.user.id,
121104
121333
  bodyComponentId: body2.bodyComponentId,
121105
121334
  hairstyleComponentId: body2.hairstyleComponentId
@@ -121114,12 +121343,12 @@ var init_character_controller = __esm(() => {
121114
121343
  } catch (error2) {
121115
121344
  if (error2 instanceof exports_external.ZodError) {
121116
121345
  const details = formatZodError(error2);
121117
- logger40.warn("Update character validation failed", { details });
121346
+ logger41.warn("Update character validation failed", { details });
121118
121347
  throw ApiError.unprocessableEntity("Invalid request body", details);
121119
121348
  }
121120
121349
  throw ApiError.badRequest("Invalid JSON body");
121121
121350
  }
121122
- logger40.debug("Updating character", {
121351
+ logger41.debug("Updating character", {
121123
121352
  userId: ctx.user.id,
121124
121353
  bodyComponentId: body2.bodyComponentId,
121125
121354
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -121135,12 +121364,12 @@ var init_character_controller = __esm(() => {
121135
121364
  } catch (error2) {
121136
121365
  if (error2 instanceof exports_external.ZodError) {
121137
121366
  const details = formatZodError(error2);
121138
- logger40.warn("Equip accessory validation failed", { details });
121367
+ logger41.warn("Equip accessory validation failed", { details });
121139
121368
  throw ApiError.unprocessableEntity("Invalid request body", details);
121140
121369
  }
121141
121370
  throw ApiError.badRequest("Invalid JSON body");
121142
121371
  }
121143
- logger40.debug("Equipping accessory", {
121372
+ logger41.debug("Equipping accessory", {
121144
121373
  userId: ctx.user.id,
121145
121374
  slot: body2.slot,
121146
121375
  accessoryComponentId: body2.accessoryComponentId
@@ -121152,7 +121381,7 @@ var init_character_controller = __esm(() => {
121152
121381
  if (!slot) {
121153
121382
  throw ApiError.badRequest("Slot is required in the URL path");
121154
121383
  }
121155
- logger40.debug("Removing accessory", { userId: ctx.user.id, slot });
121384
+ logger41.debug("Removing accessory", { userId: ctx.user.id, slot });
121156
121385
  await ctx.services.character.removeAccessory(slot, ctx.user);
121157
121386
  return { success: true };
121158
121387
  });
@@ -121166,7 +121395,7 @@ var init_character_controller = __esm(() => {
121166
121395
  removeAccessory
121167
121396
  };
121168
121397
  });
121169
- var logger41;
121398
+ var logger42;
121170
121399
  var list;
121171
121400
  var getById;
121172
121401
  var create2;
@@ -121180,9 +121409,9 @@ var init_currency_controller = __esm(() => {
121180
121409
  init_src4();
121181
121410
  init_errors();
121182
121411
  init_utils11();
121183
- logger41 = log.scope("CurrencyController");
121412
+ logger42 = log.scope("CurrencyController");
121184
121413
  list = requireNonAnonymous(async (ctx) => {
121185
- logger41.debug("Listing currencies", { userId: ctx.user.id });
121414
+ logger42.debug("Listing currencies", { userId: ctx.user.id });
121186
121415
  return ctx.services.currency.list();
121187
121416
  });
121188
121417
  getById = requireNonAnonymous(async (ctx) => {
@@ -121193,7 +121422,7 @@ var init_currency_controller = __esm(() => {
121193
121422
  if (!isValidUUID(currencyId)) {
121194
121423
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
121195
121424
  }
121196
- logger41.debug("Getting currency", { userId: ctx.user.id, currencyId });
121425
+ logger42.debug("Getting currency", { userId: ctx.user.id, currencyId });
121197
121426
  return ctx.services.currency.getById(currencyId);
121198
121427
  });
121199
121428
  create2 = requireAdmin(async (ctx) => {
@@ -121204,12 +121433,12 @@ var init_currency_controller = __esm(() => {
121204
121433
  } catch (error2) {
121205
121434
  if (error2 instanceof exports_external.ZodError) {
121206
121435
  const details = formatZodError(error2);
121207
- logger41.warn("Create currency validation failed", { details });
121436
+ logger42.warn("Create currency validation failed", { details });
121208
121437
  throw ApiError.unprocessableEntity("Validation failed", details);
121209
121438
  }
121210
121439
  throw ApiError.badRequest("Invalid JSON body");
121211
121440
  }
121212
- logger41.debug("Creating currency", {
121441
+ logger42.debug("Creating currency", {
121213
121442
  userId: ctx.user.id,
121214
121443
  symbol: body2.symbol,
121215
121444
  itemId: body2.itemId,
@@ -121232,12 +121461,12 @@ var init_currency_controller = __esm(() => {
121232
121461
  } catch (error2) {
121233
121462
  if (error2 instanceof exports_external.ZodError) {
121234
121463
  const details = formatZodError(error2);
121235
- logger41.warn("Update currency validation failed", { details });
121464
+ logger42.warn("Update currency validation failed", { details });
121236
121465
  throw ApiError.unprocessableEntity("Validation failed", details);
121237
121466
  }
121238
121467
  throw ApiError.badRequest("Invalid JSON body");
121239
121468
  }
121240
- logger41.debug("Updating currency", {
121469
+ logger42.debug("Updating currency", {
121241
121470
  userId: ctx.user.id,
121242
121471
  currencyId,
121243
121472
  symbol: body2.symbol,
@@ -121254,7 +121483,7 @@ var init_currency_controller = __esm(() => {
121254
121483
  if (!isValidUUID(currencyId)) {
121255
121484
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
121256
121485
  }
121257
- logger41.debug("Deleting currency", { userId: ctx.user.id, currencyId });
121486
+ logger42.debug("Deleting currency", { userId: ctx.user.id, currencyId });
121258
121487
  await ctx.services.currency.delete(currencyId);
121259
121488
  });
121260
121489
  currencyController = {
@@ -121265,7 +121494,7 @@ var init_currency_controller = __esm(() => {
121265
121494
  remove
121266
121495
  };
121267
121496
  });
121268
- var logger42;
121497
+ var logger43;
121269
121498
  var reset;
121270
121499
  var init_database_controller = __esm(() => {
121271
121500
  init_esm();
@@ -121273,7 +121502,7 @@ var init_database_controller = __esm(() => {
121273
121502
  init_src2();
121274
121503
  init_errors();
121275
121504
  init_utils11();
121276
- logger42 = log.scope("DatabaseController");
121505
+ logger43 = log.scope("DatabaseController");
121277
121506
  reset = requireDeveloper(async (ctx) => {
121278
121507
  const slug2 = ctx.params.slug;
121279
121508
  if (!slug2) {
@@ -121286,11 +121515,11 @@ var init_database_controller = __esm(() => {
121286
121515
  } catch (error2) {
121287
121516
  if (error2 instanceof exports_external.ZodError) {
121288
121517
  const details = formatZodError(error2);
121289
- logger42.warn("Database reset validation failed", { details });
121518
+ logger43.warn("Database reset validation failed", { details });
121290
121519
  throw ApiError.unprocessableEntity("Validation failed", details);
121291
121520
  }
121292
121521
  }
121293
- logger42.debug("Resetting database", {
121522
+ logger43.debug("Resetting database", {
121294
121523
  userId: ctx.user.id,
121295
121524
  slug: slug2,
121296
121525
  hasSchema: Boolean(body2.schema)
@@ -121306,7 +121535,7 @@ async function createJob(ctx) {
121306
121535
  let body2;
121307
121536
  try {
121308
121537
  const json4 = await ctx.request.json();
121309
- logger43.debug("Deploy request body", {
121538
+ logger44.debug("Deploy request body", {
121310
121539
  keys: Object.keys(json4 || {}),
121311
121540
  hasUploadToken: Boolean(json4?.uploadToken),
121312
121541
  hasCode: Boolean(json4?.code),
@@ -121316,7 +121545,7 @@ async function createJob(ctx) {
121316
121545
  } catch (error2) {
121317
121546
  if (error2 instanceof exports_external.ZodError) {
121318
121547
  const details = formatZodError(error2);
121319
- logger43.warn("Deploy validation failed", { details });
121548
+ logger44.warn("Deploy validation failed", { details });
121320
121549
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
121321
121550
  }
121322
121551
  throw ApiError.badRequest("Invalid JSON body");
@@ -121337,7 +121566,7 @@ async function getJob(ctx) {
121337
121566
  }
121338
121567
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
121339
121568
  }
121340
- var logger43;
121569
+ var logger44;
121341
121570
  var deploy;
121342
121571
  var init_deploy_controller = __esm(() => {
121343
121572
  init_esm();
@@ -121345,26 +121574,26 @@ var init_deploy_controller = __esm(() => {
121345
121574
  init_src2();
121346
121575
  init_errors();
121347
121576
  init_utils11();
121348
- logger43 = log.scope("DeployController");
121577
+ logger44 = log.scope("DeployController");
121349
121578
  deploy = {
121350
121579
  createJob: requireDeveloper(createJob),
121351
121580
  getJob: requireDeveloper(getJob)
121352
121581
  };
121353
121582
  });
121354
- var logger44;
121583
+ var logger45;
121355
121584
  var apply;
121356
121585
  var getStatus;
121357
121586
  var developer;
121358
121587
  var init_developer_controller = __esm(() => {
121359
121588
  init_src2();
121360
121589
  init_utils11();
121361
- logger44 = log.scope("DeveloperController");
121590
+ logger45 = log.scope("DeveloperController");
121362
121591
  apply = requireNonAnonymous(async (ctx) => {
121363
- logger44.debug("Applying for developer status", { userId: ctx.user.id });
121592
+ logger45.debug("Applying for developer status", { userId: ctx.user.id });
121364
121593
  await ctx.services.developer.apply(ctx.user);
121365
121594
  });
121366
121595
  getStatus = requireNonAnonymous(async (ctx) => {
121367
- logger44.debug("Getting developer status", { userId: ctx.user.id });
121596
+ logger45.debug("Getting developer status", { userId: ctx.user.id });
121368
121597
  const status = await ctx.services.developer.getStatus(ctx.user.id);
121369
121598
  return { status };
121370
121599
  });
@@ -121373,7 +121602,7 @@ var init_developer_controller = __esm(() => {
121373
121602
  getStatus
121374
121603
  };
121375
121604
  });
121376
- var logger45;
121605
+ var logger46;
121377
121606
  var add;
121378
121607
  var list2;
121379
121608
  var getStatus2;
@@ -121386,7 +121615,7 @@ var init_domain_controller = __esm(() => {
121386
121615
  init_config2();
121387
121616
  init_errors();
121388
121617
  init_utils11();
121389
- logger45 = log.scope("DomainController");
121618
+ logger46 = log.scope("DomainController");
121390
121619
  add = requireDeveloper(async (ctx) => {
121391
121620
  const slug2 = ctx.params.slug;
121392
121621
  if (!slug2) {
@@ -121399,13 +121628,13 @@ var init_domain_controller = __esm(() => {
121399
121628
  } catch (error2) {
121400
121629
  if (error2 instanceof exports_external.ZodError) {
121401
121630
  const details = formatZodError(error2);
121402
- logger45.warn("Add domain validation failed", { details });
121631
+ logger46.warn("Add domain validation failed", { details });
121403
121632
  throw ApiError.unprocessableEntity("Validation failed", details);
121404
121633
  }
121405
121634
  throw ApiError.badRequest("Invalid JSON body");
121406
121635
  }
121407
121636
  const environment = getPlatformEnvironment(ctx.config);
121408
- logger45.debug("Adding domain", {
121637
+ logger46.debug("Adding domain", {
121409
121638
  userId: ctx.user.id,
121410
121639
  slug: slug2,
121411
121640
  hostname: body2.hostname,
@@ -121419,7 +121648,7 @@ var init_domain_controller = __esm(() => {
121419
121648
  throw ApiError.badRequest("Missing game slug");
121420
121649
  }
121421
121650
  const environment = getPlatformEnvironment(ctx.config);
121422
- logger45.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
121651
+ logger46.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
121423
121652
  const domains22 = await ctx.services.domain.list(slug2, environment, ctx.user);
121424
121653
  return { domains: domains22 };
121425
121654
  });
@@ -121434,7 +121663,7 @@ var init_domain_controller = __esm(() => {
121434
121663
  }
121435
121664
  const refresh = ctx.url.searchParams.get("refresh") === "true";
121436
121665
  const environment = getPlatformEnvironment(ctx.config);
121437
- logger45.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
121666
+ logger46.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
121438
121667
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
121439
121668
  });
121440
121669
  remove2 = requireDeveloper(async (ctx) => {
@@ -121447,7 +121676,7 @@ var init_domain_controller = __esm(() => {
121447
121676
  throw ApiError.badRequest("Missing hostname");
121448
121677
  }
121449
121678
  const environment = getPlatformEnvironment(ctx.config);
121450
- logger45.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
121679
+ logger46.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
121451
121680
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
121452
121681
  });
121453
121682
  domains2 = {
@@ -121457,7 +121686,7 @@ var init_domain_controller = __esm(() => {
121457
121686
  remove: remove2
121458
121687
  };
121459
121688
  });
121460
- var logger46;
121689
+ var logger47;
121461
121690
  var list3;
121462
121691
  var listManageable;
121463
121692
  var getSubjects;
@@ -121474,17 +121703,17 @@ var init_game_controller = __esm(() => {
121474
121703
  init_src4();
121475
121704
  init_errors();
121476
121705
  init_utils11();
121477
- logger46 = log.scope("GameController");
121706
+ logger47 = log.scope("GameController");
121478
121707
  list3 = requireNonAnonymous(async (ctx) => {
121479
- logger46.debug("Listing games", { userId: ctx.user.id });
121708
+ logger47.debug("Listing games", { userId: ctx.user.id });
121480
121709
  return ctx.services.game.list(ctx.user);
121481
121710
  });
121482
121711
  listManageable = requireNonAnonymous(async (ctx) => {
121483
- logger46.debug("Listing manageable games", { userId: ctx.user.id });
121712
+ logger47.debug("Listing manageable games", { userId: ctx.user.id });
121484
121713
  return ctx.services.game.listManageable(ctx.user);
121485
121714
  });
121486
121715
  getSubjects = requireNonAnonymous(async (ctx) => {
121487
- logger46.debug("Getting game subjects", { userId: ctx.user.id });
121716
+ logger47.debug("Getting game subjects", { userId: ctx.user.id });
121488
121717
  return ctx.services.game.getSubjects();
121489
121718
  });
121490
121719
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -121495,7 +121724,7 @@ var init_game_controller = __esm(() => {
121495
121724
  if (!isValidUUID(gameId)) {
121496
121725
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121497
121726
  }
121498
- logger46.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
121727
+ logger47.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
121499
121728
  return ctx.services.game.getById(gameId, ctx.user);
121500
121729
  });
121501
121730
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -121503,7 +121732,7 @@ var init_game_controller = __esm(() => {
121503
121732
  if (!slug2) {
121504
121733
  throw ApiError.badRequest("Missing game slug");
121505
121734
  }
121506
- logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
121735
+ logger47.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
121507
121736
  return ctx.services.game.getBySlug(slug2, ctx.user);
121508
121737
  });
121509
121738
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -121514,7 +121743,7 @@ var init_game_controller = __esm(() => {
121514
121743
  if (!isValidUUID(gameId)) {
121515
121744
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121516
121745
  }
121517
- logger46.debug("Getting game manifest by ID", {
121746
+ logger47.debug("Getting game manifest by ID", {
121518
121747
  userId: ctx.user.id,
121519
121748
  gameId,
121520
121749
  launchId: ctx.launchId
@@ -121533,12 +121762,12 @@ var init_game_controller = __esm(() => {
121533
121762
  } catch (error2) {
121534
121763
  if (error2 instanceof exports_external.ZodError) {
121535
121764
  const details = formatZodError(error2);
121536
- logger46.warn("Upsert game validation failed", { details });
121765
+ logger47.warn("Upsert game validation failed", { details });
121537
121766
  throw ApiError.unprocessableEntity("Validation failed", details);
121538
121767
  }
121539
121768
  throw ApiError.badRequest("Invalid JSON body");
121540
121769
  }
121541
- logger46.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
121770
+ logger47.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
121542
121771
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
121543
121772
  });
121544
121773
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -121549,7 +121778,7 @@ var init_game_controller = __esm(() => {
121549
121778
  if (!isValidUUID(gameId)) {
121550
121779
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121551
121780
  }
121552
- logger46.debug("Deleting game", { userId: ctx.user.id, gameId });
121781
+ logger47.debug("Deleting game", { userId: ctx.user.id, gameId });
121553
121782
  await ctx.services.game.delete(gameId, ctx.user);
121554
121783
  });
121555
121784
  games2 = {
@@ -121563,7 +121792,7 @@ var init_game_controller = __esm(() => {
121563
121792
  remove: remove3
121564
121793
  };
121565
121794
  });
121566
- var logger47;
121795
+ var logger48;
121567
121796
  var list4;
121568
121797
  var addItem;
121569
121798
  var removeItem;
@@ -121574,9 +121803,9 @@ var init_inventory_controller = __esm(() => {
121574
121803
  init_src2();
121575
121804
  init_errors();
121576
121805
  init_utils11();
121577
- logger47 = log.scope("InventoryController");
121806
+ logger48 = log.scope("InventoryController");
121578
121807
  list4 = requireNonAnonymous(async (ctx) => {
121579
- logger47.debug("Listing inventory", { userId: ctx.user.id });
121808
+ logger48.debug("Listing inventory", { userId: ctx.user.id });
121580
121809
  return ctx.services.inventory.list(ctx.user);
121581
121810
  });
121582
121811
  addItem = requireNonAnonymous(async (ctx) => {
@@ -121587,12 +121816,12 @@ var init_inventory_controller = __esm(() => {
121587
121816
  } catch (error2) {
121588
121817
  if (error2 instanceof exports_external.ZodError) {
121589
121818
  const details = formatZodError(error2);
121590
- logger47.warn("Add inventory item validation failed", { details });
121819
+ logger48.warn("Add inventory item validation failed", { details });
121591
121820
  throw ApiError.unprocessableEntity("Invalid request body", details);
121592
121821
  }
121593
121822
  throw ApiError.badRequest("Invalid JSON body");
121594
121823
  }
121595
- logger47.debug("Adding item", {
121824
+ logger48.debug("Adding item", {
121596
121825
  userId: ctx.user.id,
121597
121826
  itemId: body2.itemId,
121598
121827
  qty: body2.qty
@@ -121607,12 +121836,12 @@ var init_inventory_controller = __esm(() => {
121607
121836
  } catch (error2) {
121608
121837
  if (error2 instanceof exports_external.ZodError) {
121609
121838
  const details = formatZodError(error2);
121610
- logger47.warn("Remove inventory item validation failed", { details });
121839
+ logger48.warn("Remove inventory item validation failed", { details });
121611
121840
  throw ApiError.unprocessableEntity("Invalid request body", details);
121612
121841
  }
121613
121842
  throw ApiError.badRequest("Invalid JSON body");
121614
121843
  }
121615
- logger47.debug("Removing item", {
121844
+ logger48.debug("Removing item", {
121616
121845
  userId: ctx.user.id,
121617
121846
  itemId: body2.itemId,
121618
121847
  qty: body2.qty
@@ -121625,7 +121854,7 @@ var init_inventory_controller = __esm(() => {
121625
121854
  removeItem
121626
121855
  };
121627
121856
  });
121628
- var logger48;
121857
+ var logger49;
121629
121858
  var list5;
121630
121859
  var getById3;
121631
121860
  var resolve2;
@@ -121644,10 +121873,10 @@ var init_item_controller = __esm(() => {
121644
121873
  init_src4();
121645
121874
  init_errors();
121646
121875
  init_utils11();
121647
- logger48 = log.scope("ItemController");
121876
+ logger49 = log.scope("ItemController");
121648
121877
  list5 = requireNonAnonymous(async (ctx) => {
121649
121878
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
121650
- logger48.debug("Listing items", { userId: ctx.user.id, gameId });
121879
+ logger49.debug("Listing items", { userId: ctx.user.id, gameId });
121651
121880
  return ctx.services.item.list(gameId);
121652
121881
  });
121653
121882
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -121658,7 +121887,7 @@ var init_item_controller = __esm(() => {
121658
121887
  if (!isValidUUID(itemId)) {
121659
121888
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121660
121889
  }
121661
- logger48.debug("Getting item", { userId: ctx.user.id, itemId });
121890
+ logger49.debug("Getting item", { userId: ctx.user.id, itemId });
121662
121891
  return ctx.services.item.getById(itemId);
121663
121892
  });
121664
121893
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -121670,7 +121899,7 @@ var init_item_controller = __esm(() => {
121670
121899
  if (gameId && !isValidUUID(gameId)) {
121671
121900
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121672
121901
  }
121673
- logger48.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
121902
+ logger49.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
121674
121903
  return ctx.services.item.resolveBySlug(slug2, gameId);
121675
121904
  });
121676
121905
  create3 = requireRole(["admin"], async (ctx) => {
@@ -121681,12 +121910,12 @@ var init_item_controller = __esm(() => {
121681
121910
  } catch (error2) {
121682
121911
  if (error2 instanceof exports_external.ZodError) {
121683
121912
  const details = formatZodError(error2);
121684
- logger48.warn("Create item validation failed", { details });
121913
+ logger49.warn("Create item validation failed", { details });
121685
121914
  throw ApiError.unprocessableEntity("Validation failed", details);
121686
121915
  }
121687
121916
  throw ApiError.badRequest("Invalid JSON body");
121688
121917
  }
121689
- logger48.debug("Creating item", {
121918
+ logger49.debug("Creating item", {
121690
121919
  userId: ctx.user.id,
121691
121920
  slug: body2.slug,
121692
121921
  displayName: body2.displayName
@@ -121708,7 +121937,7 @@ var init_item_controller = __esm(() => {
121708
121937
  } catch (error2) {
121709
121938
  if (error2 instanceof exports_external.ZodError) {
121710
121939
  const details = formatZodError(error2);
121711
- logger48.warn("Update item validation failed", { details });
121940
+ logger49.warn("Update item validation failed", { details });
121712
121941
  throw ApiError.unprocessableEntity("Validation failed", details);
121713
121942
  }
121714
121943
  throw ApiError.badRequest("Invalid JSON body");
@@ -121716,7 +121945,7 @@ var init_item_controller = __esm(() => {
121716
121945
  if (Object.keys(body2).length === 0) {
121717
121946
  throw ApiError.badRequest("No update data provided");
121718
121947
  }
121719
- logger48.debug("Updating item", {
121948
+ logger49.debug("Updating item", {
121720
121949
  userId: ctx.user.id,
121721
121950
  itemId,
121722
121951
  slug: body2.slug,
@@ -121733,7 +121962,7 @@ var init_item_controller = __esm(() => {
121733
121962
  if (!isValidUUID(itemId)) {
121734
121963
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121735
121964
  }
121736
- logger48.debug("Deleting item", { userId: ctx.user.id, itemId });
121965
+ logger49.debug("Deleting item", { userId: ctx.user.id, itemId });
121737
121966
  await ctx.services.item.delete(itemId);
121738
121967
  });
121739
121968
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -121744,7 +121973,7 @@ var init_item_controller = __esm(() => {
121744
121973
  if (!isValidUUID(gameId)) {
121745
121974
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
121746
121975
  }
121747
- logger48.debug("Listing game items", { userId: ctx.user.id, gameId });
121976
+ logger49.debug("Listing game items", { userId: ctx.user.id, gameId });
121748
121977
  return ctx.services.item.listByGame(gameId);
121749
121978
  });
121750
121979
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -121762,12 +121991,12 @@ var init_item_controller = __esm(() => {
121762
121991
  } catch (error2) {
121763
121992
  if (error2 instanceof exports_external.ZodError) {
121764
121993
  const details = formatZodError(error2);
121765
- logger48.warn("Create game item validation failed", { details });
121994
+ logger49.warn("Create game item validation failed", { details });
121766
121995
  throw ApiError.unprocessableEntity("Validation failed", details);
121767
121996
  }
121768
121997
  throw ApiError.badRequest("Invalid JSON body");
121769
121998
  }
121770
- logger48.debug("Creating game item", {
121999
+ logger49.debug("Creating game item", {
121771
122000
  userId: ctx.user.id,
121772
122001
  gameId,
121773
122002
  slug: body2.slug,
@@ -121794,7 +122023,7 @@ var init_item_controller = __esm(() => {
121794
122023
  } catch (error2) {
121795
122024
  if (error2 instanceof exports_external.ZodError) {
121796
122025
  const details = formatZodError(error2);
121797
- logger48.warn("Update game item validation failed", { details });
122026
+ logger49.warn("Update game item validation failed", { details });
121798
122027
  throw ApiError.unprocessableEntity("Validation failed", details);
121799
122028
  }
121800
122029
  throw ApiError.badRequest("Invalid JSON body");
@@ -121802,7 +122031,7 @@ var init_item_controller = __esm(() => {
121802
122031
  if (Object.keys(body2).length === 0) {
121803
122032
  throw ApiError.badRequest("No update data provided");
121804
122033
  }
121805
- logger48.debug("Updating game item", {
122034
+ logger49.debug("Updating game item", {
121806
122035
  userId: ctx.user.id,
121807
122036
  gameId,
121808
122037
  itemId,
@@ -121824,7 +122053,7 @@ var init_item_controller = __esm(() => {
121824
122053
  if (!isValidUUID(itemId)) {
121825
122054
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
121826
122055
  }
121827
- logger48.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
122056
+ logger49.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
121828
122057
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
121829
122058
  });
121830
122059
  items2 = {
@@ -121840,7 +122069,7 @@ var init_item_controller = __esm(() => {
121840
122069
  deleteForGame
121841
122070
  };
121842
122071
  });
121843
- var logger49;
122072
+ var logger50;
121844
122073
  var listKeys;
121845
122074
  var getStats;
121846
122075
  var seed;
@@ -121855,7 +122084,7 @@ var init_kv_controller = __esm(() => {
121855
122084
  init_src2();
121856
122085
  init_errors();
121857
122086
  init_utils11();
121858
- logger49 = log.scope("KVController");
122087
+ logger50 = log.scope("KVController");
121859
122088
  listKeys = requireDeveloper(async (ctx) => {
121860
122089
  const slug2 = ctx.params.slug;
121861
122090
  if (!slug2) {
@@ -121863,7 +122092,7 @@ var init_kv_controller = __esm(() => {
121863
122092
  }
121864
122093
  const url = ctx.url;
121865
122094
  const prefix2 = url.searchParams.get("prefix") || undefined;
121866
- logger49.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122095
+ logger50.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
121867
122096
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
121868
122097
  return { keys };
121869
122098
  });
@@ -121872,7 +122101,7 @@ var init_kv_controller = __esm(() => {
121872
122101
  if (!slug2) {
121873
122102
  throw ApiError.badRequest("Missing game slug");
121874
122103
  }
121875
- logger49.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
122104
+ logger50.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
121876
122105
  return ctx.services.kv.getStats(slug2, ctx.user);
121877
122106
  });
121878
122107
  seed = requireDeveloper(async (ctx) => {
@@ -121887,12 +122116,12 @@ var init_kv_controller = __esm(() => {
121887
122116
  } catch (error2) {
121888
122117
  if (error2 instanceof exports_external.ZodError) {
121889
122118
  const details = formatZodError(error2);
121890
- logger49.warn("Seed validation failed", { details });
122119
+ logger50.warn("Seed validation failed", { details });
121891
122120
  throw ApiError.unprocessableEntity("Validation failed", details);
121892
122121
  }
121893
122122
  throw ApiError.badRequest("Invalid JSON body");
121894
122123
  }
121895
- logger49.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
122124
+ logger50.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
121896
122125
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
121897
122126
  return { success: true, count: body2.entries.length };
121898
122127
  });
@@ -121902,7 +122131,7 @@ var init_kv_controller = __esm(() => {
121902
122131
  if (!slug2 || !key) {
121903
122132
  throw ApiError.badRequest("Missing game slug or key");
121904
122133
  }
121905
- logger49.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
122134
+ logger50.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
121906
122135
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
121907
122136
  return { key, value };
121908
122137
  });
@@ -121916,7 +122145,7 @@ var init_kv_controller = __esm(() => {
121916
122145
  if (!value) {
121917
122146
  throw ApiError.badRequest("Missing value in request body");
121918
122147
  }
121919
- logger49.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
122148
+ logger50.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
121920
122149
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
121921
122150
  return { success: true, key };
121922
122151
  });
@@ -121926,7 +122155,7 @@ var init_kv_controller = __esm(() => {
121926
122155
  if (!slug2 || !key) {
121927
122156
  throw ApiError.badRequest("Missing game slug or key");
121928
122157
  }
121929
- logger49.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
122158
+ logger50.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
121930
122159
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
121931
122160
  return { success: true, key };
121932
122161
  });
@@ -121936,7 +122165,7 @@ var init_kv_controller = __esm(() => {
121936
122165
  if (!slug2 || !key) {
121937
122166
  throw ApiError.badRequest("Missing game slug or key");
121938
122167
  }
121939
- logger49.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
122168
+ logger50.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
121940
122169
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
121941
122170
  return { key, metadata: metadata2 };
121942
122171
  });
@@ -121945,12 +122174,12 @@ var init_kv_controller = __esm(() => {
121945
122174
  if (!slug2) {
121946
122175
  throw ApiError.badRequest("Missing game slug");
121947
122176
  }
121948
- logger49.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
122177
+ logger50.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
121949
122178
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
121950
122179
  return { success: true, deleted };
121951
122180
  });
121952
122181
  });
121953
- var logger50;
122182
+ var logger51;
121954
122183
  var submitScore;
121955
122184
  var getGlobalLeaderboard;
121956
122185
  var getLeaderboard;
@@ -121964,7 +122193,7 @@ var init_leaderboard_controller = __esm(() => {
121964
122193
  init_src2();
121965
122194
  init_errors();
121966
122195
  init_utils11();
121967
- logger50 = log.scope("LeaderboardController");
122196
+ logger51 = log.scope("LeaderboardController");
121968
122197
  submitScore = requireAuth(async (ctx) => {
121969
122198
  const gameId = ctx.params.gameId;
121970
122199
  if (!gameId) {
@@ -121977,12 +122206,12 @@ var init_leaderboard_controller = __esm(() => {
121977
122206
  } catch (error2) {
121978
122207
  if (error2 instanceof exports_external.ZodError) {
121979
122208
  const details = formatZodError(error2);
121980
- logger50.warn("Submit score validation failed", { details });
122209
+ logger51.warn("Submit score validation failed", { details });
121981
122210
  throw ApiError.unprocessableEntity("Validation failed", details);
121982
122211
  }
121983
122212
  throw ApiError.badRequest("Invalid JSON body");
121984
122213
  }
121985
- logger50.debug("Submitting score", {
122214
+ logger51.debug("Submitting score", {
121986
122215
  userId: ctx.user.id,
121987
122216
  gameId,
121988
122217
  score: body2.score
@@ -122005,12 +122234,12 @@ var init_leaderboard_controller = __esm(() => {
122005
122234
  } catch (error2) {
122006
122235
  if (error2 instanceof exports_external.ZodError) {
122007
122236
  const details = formatZodError(error2);
122008
- logger50.warn("Get global leaderboard query validation failed", { details });
122237
+ logger51.warn("Get global leaderboard query validation failed", { details });
122009
122238
  throw ApiError.badRequest("Invalid query parameters", details);
122010
122239
  }
122011
122240
  throw ApiError.badRequest("Invalid query parameters");
122012
122241
  }
122013
- logger50.debug("Getting global leaderboard", {
122242
+ logger51.debug("Getting global leaderboard", {
122014
122243
  userId: ctx.user.id,
122015
122244
  gameId,
122016
122245
  ...query
@@ -122033,12 +122262,12 @@ var init_leaderboard_controller = __esm(() => {
122033
122262
  } catch (error2) {
122034
122263
  if (error2 instanceof exports_external.ZodError) {
122035
122264
  const details = formatZodError(error2);
122036
- logger50.warn("Get leaderboard query validation failed", { details });
122265
+ logger51.warn("Get leaderboard query validation failed", { details });
122037
122266
  throw ApiError.badRequest("Invalid query parameters", details);
122038
122267
  }
122039
122268
  throw ApiError.badRequest("Invalid query parameters");
122040
122269
  }
122041
- logger50.debug("Getting leaderboard", {
122270
+ logger51.debug("Getting leaderboard", {
122042
122271
  userId: ctx.user.id,
122043
122272
  gameId,
122044
122273
  ...query
@@ -122050,7 +122279,7 @@ var init_leaderboard_controller = __esm(() => {
122050
122279
  if (!gameId || !userId) {
122051
122280
  throw ApiError.badRequest("Game ID and User ID are required");
122052
122281
  }
122053
- logger50.debug("Getting user rank", {
122282
+ logger51.debug("Getting user rank", {
122054
122283
  requesterId: ctx.user.id,
122055
122284
  gameId,
122056
122285
  targetUserId: userId
@@ -122065,7 +122294,7 @@ var init_leaderboard_controller = __esm(() => {
122065
122294
  const url = ctx.url;
122066
122295
  const limit = Math.min(Number(url.searchParams.get("limit") || "50"), 100);
122067
122296
  const gameId = url.searchParams.get("gameId") || undefined;
122068
- logger50.debug("Getting user all scores", {
122297
+ logger51.debug("Getting user all scores", {
122069
122298
  requesterId: ctx.user.id,
122070
122299
  targetUserId: userId,
122071
122300
  gameId,
@@ -122080,7 +122309,7 @@ var init_leaderboard_controller = __esm(() => {
122080
122309
  }
122081
122310
  const url = ctx.url;
122082
122311
  const limit = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
122083
- logger50.debug("Getting user scores", {
122312
+ logger51.debug("Getting user scores", {
122084
122313
  requesterId: ctx.user.id,
122085
122314
  gameId,
122086
122315
  targetUserId: userId,
@@ -122098,7 +122327,7 @@ var init_leaderboard_controller = __esm(() => {
122098
122327
  };
122099
122328
  });
122100
122329
  async function listConfigs(ctx) {
122101
- logger51.debug("Listing level configs");
122330
+ logger52.debug("Listing level configs");
122102
122331
  return ctx.services.level.listConfigs();
122103
122332
  }
122104
122333
  async function getConfig(ctx) {
@@ -122110,10 +122339,10 @@ async function getConfig(ctx) {
122110
122339
  if (isNaN(level) || level < 1) {
122111
122340
  throw ApiError.badRequest("Level must be a positive integer");
122112
122341
  }
122113
- logger51.debug("Getting level config", { level });
122342
+ logger52.debug("Getting level config", { level });
122114
122343
  return ctx.services.level.getConfig(level);
122115
122344
  }
122116
- var logger51;
122345
+ var logger52;
122117
122346
  var getByUser;
122118
122347
  var getProgress;
122119
122348
  var levels;
@@ -122121,13 +122350,13 @@ var init_level_controller = __esm(() => {
122121
122350
  init_src2();
122122
122351
  init_errors();
122123
122352
  init_utils11();
122124
- logger51 = log.scope("LevelController");
122353
+ logger52 = log.scope("LevelController");
122125
122354
  getByUser = requireNonAnonymous(async (ctx) => {
122126
- logger51.debug("Getting user level", { userId: ctx.user.id });
122355
+ logger52.debug("Getting user level", { userId: ctx.user.id });
122127
122356
  return ctx.services.level.getByUser(ctx.user);
122128
122357
  });
122129
122358
  getProgress = requireNonAnonymous(async (ctx) => {
122130
- logger51.debug("Getting level progress", { userId: ctx.user.id });
122359
+ logger52.debug("Getting level progress", { userId: ctx.user.id });
122131
122360
  return ctx.services.level.getProgress(ctx.user);
122132
122361
  });
122133
122362
  levels = {
@@ -122137,14 +122366,14 @@ var init_level_controller = __esm(() => {
122137
122366
  getProgress
122138
122367
  };
122139
122368
  });
122140
- var logger52;
122369
+ var logger53;
122141
122370
  var generateToken;
122142
122371
  var logs;
122143
122372
  var init_logs_controller = __esm(() => {
122144
122373
  init_src2();
122145
122374
  init_errors();
122146
122375
  init_utils11();
122147
- logger52 = log.scope("LogsController");
122376
+ logger53 = log.scope("LogsController");
122148
122377
  generateToken = requireDeveloper(async (ctx) => {
122149
122378
  const slug2 = ctx.params.slug;
122150
122379
  if (!slug2) {
@@ -122163,7 +122392,7 @@ var init_logs_controller = __esm(() => {
122163
122392
  }
122164
122393
  throw ApiError.badRequest("Invalid JSON body");
122165
122394
  }
122166
- logger52.debug("Generating log stream token", {
122395
+ logger53.debug("Generating log stream token", {
122167
122396
  userId: ctx.user.id,
122168
122397
  slug: slug2,
122169
122398
  environment: body2.environment
@@ -122174,22 +122403,22 @@ var init_logs_controller = __esm(() => {
122174
122403
  generateToken
122175
122404
  };
122176
122405
  });
122177
- var logger53;
122406
+ var logger54;
122178
122407
  var getStatus3;
122179
122408
  var lti;
122180
122409
  var init_lti_controller = __esm(() => {
122181
122410
  init_src2();
122182
122411
  init_utils11();
122183
- logger53 = log.scope("LtiController");
122412
+ logger54 = log.scope("LtiController");
122184
122413
  getStatus3 = requireNonAnonymous(async (ctx) => {
122185
- logger53.debug("Getting status", { userId: ctx.user.id });
122414
+ logger54.debug("Getting status", { userId: ctx.user.id });
122186
122415
  return ctx.services.lti.getStatus(ctx.user);
122187
122416
  });
122188
122417
  lti = {
122189
122418
  getStatus: getStatus3
122190
122419
  };
122191
122420
  });
122192
- var logger54;
122421
+ var logger55;
122193
122422
  var getByIdentifier;
122194
122423
  var getElements;
122195
122424
  var getObjects;
@@ -122203,13 +122432,13 @@ var init_map_controller = __esm(() => {
122203
122432
  init_src4();
122204
122433
  init_errors();
122205
122434
  init_utils11();
122206
- logger54 = log.scope("MapController");
122435
+ logger55 = log.scope("MapController");
122207
122436
  getByIdentifier = requireNonAnonymous(async (ctx) => {
122208
122437
  const identifier = ctx.params.identifier;
122209
122438
  if (!identifier) {
122210
122439
  throw ApiError.badRequest("Missing map identifier");
122211
122440
  }
122212
- logger54.debug("Getting map", { userId: ctx.user.id, identifier });
122441
+ logger55.debug("Getting map", { userId: ctx.user.id, identifier });
122213
122442
  return ctx.services.map.getByIdentifier(identifier);
122214
122443
  });
122215
122444
  getElements = requireNonAnonymous(async (ctx) => {
@@ -122220,7 +122449,7 @@ var init_map_controller = __esm(() => {
122220
122449
  if (!isValidUUID(mapId)) {
122221
122450
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
122222
122451
  }
122223
- logger54.debug("Getting map elements", { userId: ctx.user.id, mapId });
122452
+ logger55.debug("Getting map elements", { userId: ctx.user.id, mapId });
122224
122453
  return ctx.services.map.getElements(mapId);
122225
122454
  });
122226
122455
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -122231,7 +122460,7 @@ var init_map_controller = __esm(() => {
122231
122460
  if (!isValidUUID(mapId)) {
122232
122461
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
122233
122462
  }
122234
- logger54.debug("Getting map objects", { userId: ctx.user.id, mapId });
122463
+ logger55.debug("Getting map objects", { userId: ctx.user.id, mapId });
122235
122464
  return ctx.services.map.getObjects(mapId, ctx.user.id);
122236
122465
  });
122237
122466
  createObject = requireNonAnonymous(async (ctx) => {
@@ -122253,12 +122482,12 @@ var init_map_controller = __esm(() => {
122253
122482
  } catch (error2) {
122254
122483
  if (error2 instanceof exports_external.ZodError) {
122255
122484
  const details = formatZodError(error2);
122256
- logger54.warn("Create map object validation failed", { details });
122485
+ logger55.warn("Create map object validation failed", { details });
122257
122486
  throw ApiError.unprocessableEntity("Validation failed", details);
122258
122487
  }
122259
122488
  throw ApiError.badRequest("Invalid JSON body");
122260
122489
  }
122261
- logger54.debug("Creating map object", {
122490
+ logger55.debug("Creating map object", {
122262
122491
  userId: ctx.user.id,
122263
122492
  mapId,
122264
122493
  itemId: body2.itemId,
@@ -122282,7 +122511,7 @@ var init_map_controller = __esm(() => {
122282
122511
  if (!isValidUUID(objectId)) {
122283
122512
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
122284
122513
  }
122285
- logger54.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
122514
+ logger55.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
122286
122515
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
122287
122516
  });
122288
122517
  maps2 = {
@@ -122293,7 +122522,7 @@ var init_map_controller = __esm(() => {
122293
122522
  deleteObject
122294
122523
  };
122295
122524
  });
122296
- var logger55;
122525
+ var logger56;
122297
122526
  var list6;
122298
122527
  var updateStatus;
122299
122528
  var getStats2;
@@ -122306,7 +122535,7 @@ var init_notification_controller = __esm(() => {
122306
122535
  init_src2();
122307
122536
  init_errors();
122308
122537
  init_utils11();
122309
- logger55 = log.scope("NotificationController");
122538
+ logger56 = log.scope("NotificationController");
122310
122539
  list6 = requireNonAnonymous(async (ctx) => {
122311
122540
  const query = {
122312
122541
  status: ctx.url.searchParams.get("status") || undefined,
@@ -122317,10 +122546,10 @@ var init_notification_controller = __esm(() => {
122317
122546
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
122318
122547
  if (!result.success) {
122319
122548
  const details = formatZodError(result.error);
122320
- logger55.warn("List notifications query validation failed", { details });
122549
+ logger56.warn("List notifications query validation failed", { details });
122321
122550
  throw ApiError.badRequest("Invalid query parameters", details);
122322
122551
  }
122323
- logger55.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
122552
+ logger56.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
122324
122553
  return ctx.services.notification.list(ctx.user, result.data);
122325
122554
  });
122326
122555
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -122335,12 +122564,12 @@ var init_notification_controller = __esm(() => {
122335
122564
  } catch (error2) {
122336
122565
  if (error2 instanceof exports_external.ZodError) {
122337
122566
  const details = formatZodError(error2);
122338
- logger55.warn("Update notification status validation failed", { details });
122567
+ logger56.warn("Update notification status validation failed", { details });
122339
122568
  throw ApiError.unprocessableEntity("Invalid request body", details);
122340
122569
  }
122341
122570
  throw ApiError.badRequest("Invalid JSON body");
122342
122571
  }
122343
- logger55.debug("Updating status", {
122572
+ logger56.debug("Updating status", {
122344
122573
  userId: ctx.user.id,
122345
122574
  notificationId,
122346
122575
  status: body2.status
@@ -122350,7 +122579,7 @@ var init_notification_controller = __esm(() => {
122350
122579
  getStats2 = requireNonAnonymous(async (ctx) => {
122351
122580
  const startDate = ctx.url.searchParams.get("startDate");
122352
122581
  const endDate = ctx.url.searchParams.get("endDate");
122353
- logger55.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
122582
+ logger56.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
122354
122583
  return ctx.services.notification.getStats(ctx.user, {
122355
122584
  startDate: startDate ? new Date(startDate) : undefined,
122356
122585
  endDate: endDate ? new Date(endDate) : undefined
@@ -122364,12 +122593,12 @@ var init_notification_controller = __esm(() => {
122364
122593
  } catch (error2) {
122365
122594
  if (error2 instanceof exports_external.ZodError) {
122366
122595
  const details = formatZodError(error2);
122367
- logger55.warn("Create notification validation failed", { details });
122596
+ logger56.warn("Create notification validation failed", { details });
122368
122597
  throw ApiError.unprocessableEntity("Invalid request body", details);
122369
122598
  }
122370
122599
  throw ApiError.badRequest("Invalid JSON body");
122371
122600
  }
122372
- logger55.debug("Creating notification", {
122601
+ logger56.debug("Creating notification", {
122373
122602
  userId: ctx.user.id,
122374
122603
  targetUserId: body2.userId,
122375
122604
  type: body2.type
@@ -122387,12 +122616,12 @@ var init_notification_controller = __esm(() => {
122387
122616
  });
122388
122617
  });
122389
122618
  deliver = requireNonAnonymous(async (ctx) => {
122390
- logger55.debug("Delivering notifications", { userId: ctx.user.id });
122619
+ logger56.debug("Delivering notifications", { userId: ctx.user.id });
122391
122620
  try {
122392
122621
  await ctx.services.notification.deliverPending(ctx.user.id);
122393
122622
  return { success: true };
122394
122623
  } catch (error2) {
122395
- logger55.error("Failed to deliver notifications", { error: error2 });
122624
+ logger56.error("Failed to deliver notifications", { error: error2 });
122396
122625
  throw ApiError.internal("Failed to deliver notifications");
122397
122626
  }
122398
122627
  });
@@ -122404,16 +122633,16 @@ var init_notification_controller = __esm(() => {
122404
122633
  deliver
122405
122634
  };
122406
122635
  });
122407
- var logger56;
122636
+ var logger57;
122408
122637
  var generateToken2;
122409
122638
  var realtime;
122410
122639
  var init_realtime_controller = __esm(() => {
122411
122640
  init_src2();
122412
122641
  init_utils11();
122413
- logger56 = log.scope("RealtimeController");
122642
+ logger57 = log.scope("RealtimeController");
122414
122643
  generateToken2 = requireNonAnonymous(async (ctx) => {
122415
122644
  const gameIdOrSlug = ctx.params.gameId;
122416
- logger56.debug("Generating token", {
122645
+ logger57.debug("Generating token", {
122417
122646
  userId: ctx.user.id,
122418
122647
  gameId: gameIdOrSlug || "global",
122419
122648
  launchId: ctx.launchId
@@ -122424,7 +122653,7 @@ var init_realtime_controller = __esm(() => {
122424
122653
  generateToken: generateToken2
122425
122654
  };
122426
122655
  });
122427
- var logger57;
122656
+ var logger58;
122428
122657
  var listKeys2;
122429
122658
  var setSecrets;
122430
122659
  var deleteSecret;
@@ -122435,13 +122664,13 @@ var init_secrets_controller = __esm(() => {
122435
122664
  init_src2();
122436
122665
  init_errors();
122437
122666
  init_utils11();
122438
- logger57 = log.scope("SecretsController");
122667
+ logger58 = log.scope("SecretsController");
122439
122668
  listKeys2 = requireDeveloper(async (ctx) => {
122440
122669
  const slug2 = ctx.params.slug;
122441
122670
  if (!slug2) {
122442
122671
  throw ApiError.badRequest("Missing game slug");
122443
122672
  }
122444
- logger57.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
122673
+ logger58.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
122445
122674
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
122446
122675
  return { keys };
122447
122676
  });
@@ -122457,12 +122686,12 @@ var init_secrets_controller = __esm(() => {
122457
122686
  } catch (error2) {
122458
122687
  if (error2 instanceof exports_external.ZodError) {
122459
122688
  const details = formatZodError(error2);
122460
- logger57.warn("Set secrets validation failed", { details });
122689
+ logger58.warn("Set secrets validation failed", { details });
122461
122690
  throw ApiError.unprocessableEntity("Validation failed", details);
122462
122691
  }
122463
122692
  throw ApiError.badRequest("Invalid JSON body");
122464
122693
  }
122465
- logger57.debug("Setting secrets", {
122694
+ logger58.debug("Setting secrets", {
122466
122695
  userId: ctx.user.id,
122467
122696
  slug: slug2,
122468
122697
  keyCount: Object.keys(body2).length
@@ -122479,7 +122708,7 @@ var init_secrets_controller = __esm(() => {
122479
122708
  if (!key) {
122480
122709
  throw ApiError.badRequest("Missing secret key");
122481
122710
  }
122482
- logger57.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
122711
+ logger58.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
122483
122712
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
122484
122713
  return { success: true };
122485
122714
  });
@@ -122489,7 +122718,7 @@ var init_secrets_controller = __esm(() => {
122489
122718
  deleteSecret
122490
122719
  };
122491
122720
  });
122492
- var logger58;
122721
+ var logger59;
122493
122722
  var seed2;
122494
122723
  var init_seed_controller = __esm(() => {
122495
122724
  init_esm();
@@ -122497,7 +122726,7 @@ var init_seed_controller = __esm(() => {
122497
122726
  init_src2();
122498
122727
  init_errors();
122499
122728
  init_utils11();
122500
- logger58 = log.scope("SeedController");
122729
+ logger59 = log.scope("SeedController");
122501
122730
  seed2 = requireDeveloper(async (ctx) => {
122502
122731
  const slug2 = ctx.params.slug;
122503
122732
  if (!slug2) {
@@ -122510,12 +122739,12 @@ var init_seed_controller = __esm(() => {
122510
122739
  } catch (error2) {
122511
122740
  if (error2 instanceof exports_external.ZodError) {
122512
122741
  const details = formatZodError(error2);
122513
- logger58.warn("Seed database validation failed", { details });
122742
+ logger59.warn("Seed database validation failed", { details });
122514
122743
  throw ApiError.unprocessableEntity("Validation failed", details);
122515
122744
  }
122516
122745
  throw ApiError.badRequest("Invalid JSON body");
122517
122746
  }
122518
- logger58.debug("Seeding database", {
122747
+ logger59.debug("Seeding database", {
122519
122748
  userId: ctx.user.id,
122520
122749
  slug: slug2,
122521
122750
  codeLength: body2.code.length,
@@ -122524,7 +122753,7 @@ var init_seed_controller = __esm(() => {
122524
122753
  return ctx.services.seed.seed(slug2, body2.code, ctx.user, body2.secrets);
122525
122754
  });
122526
122755
  });
122527
- var logger59;
122756
+ var logger60;
122528
122757
  var start2;
122529
122758
  var end;
122530
122759
  var mintToken;
@@ -122533,13 +122762,13 @@ var init_session_controller = __esm(() => {
122533
122762
  init_src2();
122534
122763
  init_errors();
122535
122764
  init_utils11();
122536
- logger59 = log.scope("SessionController");
122765
+ logger60 = log.scope("SessionController");
122537
122766
  start2 = requireAuth(async (ctx) => {
122538
122767
  const gameIdOrSlug = ctx.params.gameId;
122539
122768
  if (!gameIdOrSlug) {
122540
122769
  throw ApiError.badRequest("Missing game ID or slug");
122541
122770
  }
122542
- logger59.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122771
+ logger60.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122543
122772
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
122544
122773
  });
122545
122774
  end = requireAuth(async (ctx) => {
@@ -122551,7 +122780,7 @@ var init_session_controller = __esm(() => {
122551
122780
  if (!sessionId) {
122552
122781
  throw ApiError.badRequest("Missing session ID");
122553
122782
  }
122554
- logger59.debug("Ending session", {
122783
+ logger60.debug("Ending session", {
122555
122784
  userId: ctx.user.id,
122556
122785
  gameIdOrSlug,
122557
122786
  sessionId,
@@ -122564,7 +122793,7 @@ var init_session_controller = __esm(() => {
122564
122793
  if (!gameIdOrSlug) {
122565
122794
  throw ApiError.badRequest("Missing game ID or slug");
122566
122795
  }
122567
- logger59.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122796
+ logger60.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
122568
122797
  return ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
122569
122798
  });
122570
122799
  sessions2 = {
@@ -122573,22 +122802,22 @@ var init_session_controller = __esm(() => {
122573
122802
  mintToken
122574
122803
  };
122575
122804
  });
122576
- var logger60;
122805
+ var logger61;
122577
122806
  var getShopView;
122578
122807
  var shop;
122579
122808
  var init_shop_controller = __esm(() => {
122580
122809
  init_src2();
122581
122810
  init_utils11();
122582
- logger60 = log.scope("ShopController");
122811
+ logger61 = log.scope("ShopController");
122583
122812
  getShopView = requireNonAnonymous(async (ctx) => {
122584
- logger60.debug("Getting shop view", { userId: ctx.user.id });
122813
+ logger61.debug("Getting shop view", { userId: ctx.user.id });
122585
122814
  return ctx.services.shop.getShopView(ctx.user);
122586
122815
  });
122587
122816
  shop = {
122588
122817
  getShopView
122589
122818
  };
122590
122819
  });
122591
- var logger61;
122820
+ var logger62;
122592
122821
  var list7;
122593
122822
  var getById4;
122594
122823
  var create5;
@@ -122607,9 +122836,9 @@ var init_shop_listing_controller = __esm(() => {
122607
122836
  init_src4();
122608
122837
  init_errors();
122609
122838
  init_utils11();
122610
- logger61 = log.scope("ShopListingController");
122839
+ logger62 = log.scope("ShopListingController");
122611
122840
  list7 = requireAdmin(async (ctx) => {
122612
- logger61.debug("Listing shop listings", { userId: ctx.user.id });
122841
+ logger62.debug("Listing shop listings", { userId: ctx.user.id });
122613
122842
  return ctx.services.shopListing.list();
122614
122843
  });
122615
122844
  getById4 = requireAdmin(async (ctx) => {
@@ -122620,7 +122849,7 @@ var init_shop_listing_controller = __esm(() => {
122620
122849
  if (!isValidUUID(listingId)) {
122621
122850
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
122622
122851
  }
122623
- logger61.debug("Getting listing", { userId: ctx.user.id, listingId });
122852
+ logger62.debug("Getting listing", { userId: ctx.user.id, listingId });
122624
122853
  return ctx.services.shopListing.getById(listingId);
122625
122854
  });
122626
122855
  create5 = requireAdmin(async (ctx) => {
@@ -122631,12 +122860,12 @@ var init_shop_listing_controller = __esm(() => {
122631
122860
  } catch (error2) {
122632
122861
  if (error2 instanceof exports_external.ZodError) {
122633
122862
  const details = formatZodError(error2);
122634
- logger61.warn("Create shop listing validation failed", { details });
122863
+ logger62.warn("Create shop listing validation failed", { details });
122635
122864
  throw ApiError.unprocessableEntity("Validation failed", details);
122636
122865
  }
122637
122866
  throw ApiError.badRequest("Invalid JSON body");
122638
122867
  }
122639
- logger61.debug("Creating listing", {
122868
+ logger62.debug("Creating listing", {
122640
122869
  userId: ctx.user.id,
122641
122870
  itemId: body2.itemId,
122642
122871
  currencyId: body2.currencyId,
@@ -122659,12 +122888,12 @@ var init_shop_listing_controller = __esm(() => {
122659
122888
  } catch (error2) {
122660
122889
  if (error2 instanceof exports_external.ZodError) {
122661
122890
  const details = formatZodError(error2);
122662
- logger61.warn("Update shop listing validation failed", { details });
122891
+ logger62.warn("Update shop listing validation failed", { details });
122663
122892
  throw ApiError.unprocessableEntity("Validation failed", details);
122664
122893
  }
122665
122894
  throw ApiError.badRequest("Invalid JSON body");
122666
122895
  }
122667
- logger61.debug("Updating listing", {
122896
+ logger62.debug("Updating listing", {
122668
122897
  userId: ctx.user.id,
122669
122898
  listingId,
122670
122899
  price: body2.price,
@@ -122681,7 +122910,7 @@ var init_shop_listing_controller = __esm(() => {
122681
122910
  if (!isValidUUID(listingId)) {
122682
122911
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
122683
122912
  }
122684
- logger61.debug("Deleting listing", { userId: ctx.user.id, listingId });
122913
+ logger62.debug("Deleting listing", { userId: ctx.user.id, listingId });
122685
122914
  await ctx.services.shopListing.delete(listingId);
122686
122915
  });
122687
122916
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -122692,7 +122921,7 @@ var init_shop_listing_controller = __esm(() => {
122692
122921
  if (!isValidUUID(gameId)) {
122693
122922
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
122694
122923
  }
122695
- logger61.debug("Listing game listings", { userId: ctx.user.id, gameId });
122924
+ logger62.debug("Listing game listings", { userId: ctx.user.id, gameId });
122696
122925
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
122697
122926
  });
122698
122927
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -122707,7 +122936,7 @@ var init_shop_listing_controller = __esm(() => {
122707
122936
  if (!isValidUUID(itemId)) {
122708
122937
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
122709
122938
  }
122710
- logger61.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
122939
+ logger62.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
122711
122940
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
122712
122941
  });
122713
122942
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -122729,12 +122958,12 @@ var init_shop_listing_controller = __esm(() => {
122729
122958
  } catch (error2) {
122730
122959
  if (error2 instanceof exports_external.ZodError) {
122731
122960
  const details = formatZodError(error2);
122732
- logger61.warn("Create game item listing validation failed", { details });
122961
+ logger62.warn("Create game item listing validation failed", { details });
122733
122962
  throw ApiError.unprocessableEntity("Validation failed", details);
122734
122963
  }
122735
122964
  throw ApiError.badRequest("Invalid JSON body");
122736
122965
  }
122737
- logger61.debug("Creating game item listing", {
122966
+ logger62.debug("Creating game item listing", {
122738
122967
  userId: ctx.user.id,
122739
122968
  gameId,
122740
122969
  itemId,
@@ -122762,12 +122991,12 @@ var init_shop_listing_controller = __esm(() => {
122762
122991
  } catch (error2) {
122763
122992
  if (error2 instanceof exports_external.ZodError) {
122764
122993
  const details = formatZodError(error2);
122765
- logger61.warn("Update game item listing validation failed", { details });
122994
+ logger62.warn("Update game item listing validation failed", { details });
122766
122995
  throw ApiError.unprocessableEntity("Validation failed", details);
122767
122996
  }
122768
122997
  throw ApiError.badRequest("Invalid JSON body");
122769
122998
  }
122770
- logger61.debug("Updating game item listing", {
122999
+ logger62.debug("Updating game item listing", {
122771
123000
  userId: ctx.user.id,
122772
123001
  gameId,
122773
123002
  itemId,
@@ -122789,7 +123018,7 @@ var init_shop_listing_controller = __esm(() => {
122789
123018
  if (!isValidUUID(itemId)) {
122790
123019
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
122791
123020
  }
122792
- logger61.debug("Deleting game item listing", {
123021
+ logger62.debug("Deleting game item listing", {
122793
123022
  userId: ctx.user.id,
122794
123023
  gameId,
122795
123024
  itemId
@@ -122814,20 +123043,20 @@ async function getBySlug2(ctx) {
122814
123043
  if (!slug2) {
122815
123044
  throw ApiError.badRequest("Template slug is required");
122816
123045
  }
122817
- logger62.debug("Getting sprite by slug", { slug: slug2 });
123046
+ logger63.debug("Getting sprite by slug", { slug: slug2 });
122818
123047
  return ctx.services.sprite.getBySlug(slug2);
122819
123048
  }
122820
- var logger62;
123049
+ var logger63;
122821
123050
  var sprites;
122822
123051
  var init_sprite_controller = __esm(() => {
122823
123052
  init_src2();
122824
123053
  init_errors();
122825
- logger62 = log.scope("SpriteController");
123054
+ logger63 = log.scope("SpriteController");
122826
123055
  sprites = {
122827
123056
  getBySlug: getBySlug2
122828
123057
  };
122829
123058
  });
122830
- var logger63;
123059
+ var logger64;
122831
123060
  var getTodayXp;
122832
123061
  var getTotalXp;
122833
123062
  var updateTodayXp;
@@ -122842,6 +123071,7 @@ var getConfig2;
122842
123071
  var deleteIntegrations;
122843
123072
  var endActivity;
122844
123073
  var heartbeat;
123074
+ var advanceCourse;
122845
123075
  var getStudentXp;
122846
123076
  var getRoster;
122847
123077
  var getStudentOverview;
@@ -122861,15 +123091,15 @@ var init_timeback_controller = __esm(() => {
122861
123091
  init_src4();
122862
123092
  init_errors();
122863
123093
  init_utils11();
122864
- logger63 = log.scope("TimebackController");
123094
+ logger64 = log.scope("TimebackController");
122865
123095
  getTodayXp = requireNonAnonymous(async (ctx) => {
122866
123096
  const date4 = ctx.url.searchParams.get("date") || undefined;
122867
123097
  const tz = ctx.url.searchParams.get("tz") || undefined;
122868
- logger63.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
123098
+ logger64.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
122869
123099
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
122870
123100
  });
122871
123101
  getTotalXp = requireNonAnonymous(async (ctx) => {
122872
- logger63.debug("Getting total XP", { userId: ctx.user.id });
123102
+ logger64.debug("Getting total XP", { userId: ctx.user.id });
122873
123103
  return ctx.services.timeback.getTotalXp(ctx.user.id);
122874
123104
  });
122875
123105
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -122880,18 +123110,18 @@ var init_timeback_controller = __esm(() => {
122880
123110
  } catch (error2) {
122881
123111
  if (error2 instanceof exports_external.ZodError) {
122882
123112
  const details = formatZodError(error2);
122883
- logger63.warn("Update today XP validation failed", { details });
123113
+ logger64.warn("Update today XP validation failed", { details });
122884
123114
  throw ApiError.unprocessableEntity("Validation failed", details);
122885
123115
  }
122886
123116
  throw ApiError.badRequest("Invalid JSON body");
122887
123117
  }
122888
- logger63.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
123118
+ logger64.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
122889
123119
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
122890
123120
  });
122891
123121
  getXpHistory = requireNonAnonymous(async (ctx) => {
122892
123122
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
122893
123123
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
122894
- logger63.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
123124
+ logger64.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
122895
123125
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
122896
123126
  });
122897
123127
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -122902,18 +123132,18 @@ var init_timeback_controller = __esm(() => {
122902
123132
  } catch (error2) {
122903
123133
  if (error2 instanceof exports_external.ZodError) {
122904
123134
  const details = formatZodError(error2);
122905
- logger63.warn("Populate student validation failed", { details });
123135
+ logger64.warn("Populate student validation failed", { details });
122906
123136
  throw ApiError.unprocessableEntity("Validation failed", details);
122907
123137
  }
122908
123138
  }
122909
- logger63.debug("Populating student", {
123139
+ logger64.debug("Populating student", {
122910
123140
  userId: ctx.user.id,
122911
123141
  hasProvidedNames: Boolean(providedNames)
122912
123142
  });
122913
123143
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
122914
123144
  });
122915
123145
  getUser = requireNonAnonymous(async (ctx) => {
122916
- logger63.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
123146
+ logger64.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
122917
123147
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
122918
123148
  });
122919
123149
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -122921,7 +123151,7 @@ var init_timeback_controller = __esm(() => {
122921
123151
  if (!timebackId) {
122922
123152
  throw ApiError.badRequest("Missing timebackId parameter");
122923
123153
  }
122924
- logger63.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
123154
+ logger64.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
122925
123155
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
122926
123156
  });
122927
123157
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -122932,12 +123162,12 @@ var init_timeback_controller = __esm(() => {
122932
123162
  } catch (error2) {
122933
123163
  if (error2 instanceof exports_external.ZodError) {
122934
123164
  const details = formatZodError(error2);
122935
- logger63.warn("Setup integration validation failed", { details });
123165
+ logger64.warn("Setup integration validation failed", { details });
122936
123166
  throw ApiError.unprocessableEntity("Validation failed", details);
122937
123167
  }
122938
123168
  throw ApiError.badRequest("Invalid JSON body");
122939
123169
  }
122940
- logger63.debug("Setting up integration", {
123170
+ logger64.debug("Setting up integration", {
122941
123171
  userId: ctx.user.id,
122942
123172
  gameId: body2.gameId
122943
123173
  });
@@ -122951,7 +123181,7 @@ var init_timeback_controller = __esm(() => {
122951
123181
  if (!isValidUUID(gameId)) {
122952
123182
  throw ApiError.unprocessableEntity("Invalid gameId format");
122953
123183
  }
122954
- logger63.debug("Getting integrations", { userId: ctx.user.id, gameId });
123184
+ logger64.debug("Getting integrations", { userId: ctx.user.id, gameId });
122955
123185
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
122956
123186
  });
122957
123187
  verifyIntegration = requireDeveloper(async (ctx) => {
@@ -122962,7 +123192,7 @@ var init_timeback_controller = __esm(() => {
122962
123192
  if (!isValidUUID(gameId)) {
122963
123193
  throw ApiError.unprocessableEntity("Invalid gameId format");
122964
123194
  }
122965
- logger63.debug("Verifying integration", { userId: ctx.user.id, gameId });
123195
+ logger64.debug("Verifying integration", { userId: ctx.user.id, gameId });
122966
123196
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
122967
123197
  });
122968
123198
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -122973,7 +123203,7 @@ var init_timeback_controller = __esm(() => {
122973
123203
  if (!isValidUUID(gameId)) {
122974
123204
  throw ApiError.unprocessableEntity("Invalid gameId format");
122975
123205
  }
122976
- logger63.debug("Getting config", { userId: ctx.user.id, gameId });
123206
+ logger64.debug("Getting config", { userId: ctx.user.id, gameId });
122977
123207
  return ctx.services.timeback.getConfig(gameId, ctx.user);
122978
123208
  });
122979
123209
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -122984,7 +123214,7 @@ var init_timeback_controller = __esm(() => {
122984
123214
  if (!isValidUUID(gameId)) {
122985
123215
  throw ApiError.unprocessableEntity("Invalid gameId format");
122986
123216
  }
122987
- logger63.debug("Deleting integrations", { userId: ctx.user.id, gameId });
123217
+ logger64.debug("Deleting integrations", { userId: ctx.user.id, gameId });
122988
123218
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
122989
123219
  });
122990
123220
  endActivity = requireDeveloper(async (ctx) => {
@@ -122995,7 +123225,7 @@ var init_timeback_controller = __esm(() => {
122995
123225
  } catch (error2) {
122996
123226
  if (error2 instanceof exports_external.ZodError) {
122997
123227
  const details = formatZodError(error2);
122998
- logger63.warn("End activity validation failed", { details });
123228
+ logger64.warn("End activity validation failed", { details });
122999
123229
  throw ApiError.unprocessableEntity("Validation failed", details);
123000
123230
  }
123001
123231
  throw ApiError.badRequest("Invalid JSON body");
@@ -123013,7 +123243,7 @@ var init_timeback_controller = __esm(() => {
123013
123243
  masteredUnits,
123014
123244
  extensions
123015
123245
  } = body2;
123016
- logger63.debug("Ending activity", { userId: ctx.user.id, gameId });
123246
+ logger64.debug("Ending activity", { userId: ctx.user.id, gameId });
123017
123247
  return ctx.services.timeback.endActivity({
123018
123248
  gameId,
123019
123249
  studentId,
@@ -123037,7 +123267,7 @@ var init_timeback_controller = __esm(() => {
123037
123267
  } catch (error2) {
123038
123268
  if (error2 instanceof exports_external.ZodError) {
123039
123269
  const details = formatZodError(error2);
123040
- logger63.warn("Heartbeat validation failed", { details });
123270
+ logger64.warn("Heartbeat validation failed", { details });
123041
123271
  throw ApiError.unprocessableEntity("Validation failed", details);
123042
123272
  }
123043
123273
  throw ApiError.badRequest("Invalid JSON body");
@@ -123053,7 +123283,7 @@ var init_timeback_controller = __esm(() => {
123053
123283
  windowSequence,
123054
123284
  isFinal
123055
123285
  } = body2;
123056
- logger63.debug("Recording heartbeat", {
123286
+ logger64.debug("Recording heartbeat", {
123057
123287
  userId: ctx.user.id,
123058
123288
  gameId,
123059
123289
  runId,
@@ -123076,6 +123306,19 @@ var init_timeback_controller = __esm(() => {
123076
123306
  user: ctx.user
123077
123307
  });
123078
123308
  });
123309
+ advanceCourse = requireDeveloper(async (ctx) => {
123310
+ const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
123311
+ logger64.debug("Advancing student manually", {
123312
+ userId: ctx.user.id,
123313
+ gameId: body2.gameId,
123314
+ studentId: body2.studentId,
123315
+ subject: body2.subject
123316
+ });
123317
+ return ctx.services.timeback.advanceCourse({
123318
+ ...body2,
123319
+ user: ctx.user
123320
+ });
123321
+ });
123079
123322
  getStudentXp = requireDeveloper(async (ctx) => {
123080
123323
  const timebackId = ctx.params.timebackId;
123081
123324
  if (!timebackId) {
@@ -123106,7 +123349,7 @@ var init_timeback_controller = __esm(() => {
123106
123349
  perCourse: includeOptions.includes("percourse"),
123107
123350
  today: includeOptions.includes("today")
123108
123351
  };
123109
- logger63.debug("Getting student XP", {
123352
+ logger64.debug("Getting student XP", {
123110
123353
  requesterId: ctx.user.id,
123111
123354
  timebackId,
123112
123355
  gameId,
@@ -123127,7 +123370,7 @@ var init_timeback_controller = __esm(() => {
123127
123370
  if (!gameId || !courseId) {
123128
123371
  throw ApiError.badRequest("Missing gameId or courseId parameter");
123129
123372
  }
123130
- logger63.debug("Getting course roster", {
123373
+ logger64.debug("Getting course roster", {
123131
123374
  requesterId: ctx.user.id,
123132
123375
  gameId,
123133
123376
  courseId
@@ -123141,7 +123384,7 @@ var init_timeback_controller = __esm(() => {
123141
123384
  if (!timebackId || !gameId) {
123142
123385
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
123143
123386
  }
123144
- logger63.debug("Getting student overview", {
123387
+ logger64.debug("Getting student overview", {
123145
123388
  requesterId: ctx.user.id,
123146
123389
  timebackId,
123147
123390
  gameId,
@@ -123160,7 +123403,7 @@ var init_timeback_controller = __esm(() => {
123160
123403
  if (!timebackId || !courseId || !gameId) {
123161
123404
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
123162
123405
  }
123163
- logger63.debug("Getting student activity", {
123406
+ logger64.debug("Getting student activity", {
123164
123407
  requesterId: ctx.user.id,
123165
123408
  timebackId,
123166
123409
  courseId,
@@ -123178,7 +123421,7 @@ var init_timeback_controller = __esm(() => {
123178
123421
  });
123179
123422
  grantXp = requireDeveloper(async (ctx) => {
123180
123423
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
123181
- logger63.debug("Granting manual XP", {
123424
+ logger64.debug("Granting manual XP", {
123182
123425
  requesterId: ctx.user.id,
123183
123426
  gameId: body2.gameId,
123184
123427
  courseId: body2.courseId,
@@ -123190,7 +123433,7 @@ var init_timeback_controller = __esm(() => {
123190
123433
  });
123191
123434
  adjustTime = requireDeveloper(async (ctx) => {
123192
123435
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
123193
- logger63.debug("Adjusting time spent", {
123436
+ logger64.debug("Adjusting time spent", {
123194
123437
  requesterId: ctx.user.id,
123195
123438
  gameId: body2.gameId,
123196
123439
  courseId: body2.courseId,
@@ -123202,7 +123445,7 @@ var init_timeback_controller = __esm(() => {
123202
123445
  });
123203
123446
  adjustMastery = requireDeveloper(async (ctx) => {
123204
123447
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
123205
- logger63.debug("Adjusting mastered units", {
123448
+ logger64.debug("Adjusting mastered units", {
123206
123449
  requesterId: ctx.user.id,
123207
123450
  gameId: body2.gameId,
123208
123451
  courseId: body2.courseId,
@@ -123214,7 +123457,7 @@ var init_timeback_controller = __esm(() => {
123214
123457
  });
123215
123458
  toggleCompletion = requireGameManagementAccess(async (ctx) => {
123216
123459
  const body2 = await parseRequestBody(ctx.request, ToggleCourseCompletionRequestSchema);
123217
- logger63.debug("Toggling course completion", {
123460
+ logger64.debug("Toggling course completion", {
123218
123461
  requesterId: ctx.user.id,
123219
123462
  gameId: body2.gameId,
123220
123463
  courseId: body2.courseId,
@@ -123230,7 +123473,7 @@ var init_timeback_controller = __esm(() => {
123230
123473
  if (!gameId || !courseId) {
123231
123474
  throw ApiError.badRequest("Missing gameId or courseId parameter");
123232
123475
  }
123233
- logger63.debug("Searching students for enrollment", {
123476
+ logger64.debug("Searching students for enrollment", {
123234
123477
  requesterId: ctx.user.id,
123235
123478
  gameId,
123236
123479
  courseId,
@@ -123240,7 +123483,7 @@ var init_timeback_controller = __esm(() => {
123240
123483
  });
123241
123484
  enrollStudent = requireGameManagementAccess(async (ctx) => {
123242
123485
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
123243
- logger63.debug("Enrolling student", {
123486
+ logger64.debug("Enrolling student", {
123244
123487
  requesterId: ctx.user.id,
123245
123488
  gameId: body2.gameId,
123246
123489
  courseId: body2.courseId,
@@ -123250,7 +123493,7 @@ var init_timeback_controller = __esm(() => {
123250
123493
  });
123251
123494
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
123252
123495
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
123253
- logger63.debug("Unenrolling student", {
123496
+ logger64.debug("Unenrolling student", {
123254
123497
  requesterId: ctx.user.id,
123255
123498
  gameId: body2.gameId,
123256
123499
  courseId: body2.courseId,
@@ -123273,6 +123516,7 @@ var init_timeback_controller = __esm(() => {
123273
123516
  deleteIntegrations,
123274
123517
  endActivity,
123275
123518
  heartbeat,
123519
+ advanceCourse,
123276
123520
  getStudentXp,
123277
123521
  getRoster,
123278
123522
  getStudentOverview,
@@ -123286,7 +123530,7 @@ var init_timeback_controller = __esm(() => {
123286
123530
  unenrollStudent
123287
123531
  };
123288
123532
  });
123289
- var logger64;
123533
+ var logger65;
123290
123534
  var initiate;
123291
123535
  var init_upload_controller = __esm(() => {
123292
123536
  init_esm();
@@ -123294,7 +123538,7 @@ var init_upload_controller = __esm(() => {
123294
123538
  init_src2();
123295
123539
  init_errors();
123296
123540
  init_utils11();
123297
- logger64 = log.scope("UploadController");
123541
+ logger65 = log.scope("UploadController");
123298
123542
  initiate = requireDeveloper(async (ctx) => {
123299
123543
  let body2;
123300
123544
  try {
@@ -123303,16 +123547,16 @@ var init_upload_controller = __esm(() => {
123303
123547
  } catch (error2) {
123304
123548
  if (error2 instanceof exports_external.ZodError) {
123305
123549
  const details = formatZodError(error2);
123306
- logger64.warn("Initiate upload validation failed", { details });
123550
+ logger65.warn("Initiate upload validation failed", { details });
123307
123551
  throw ApiError.unprocessableEntity("Validation failed", details);
123308
123552
  }
123309
123553
  throw ApiError.badRequest("Invalid JSON body");
123310
123554
  }
123311
- logger64.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
123555
+ logger65.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
123312
123556
  return ctx.services.upload.initiate(body2, ctx.user);
123313
123557
  });
123314
123558
  });
123315
- var logger65;
123559
+ var logger66;
123316
123560
  var getMe;
123317
123561
  var getDemoProfile;
123318
123562
  var updateDemoProfile;
@@ -123321,18 +123565,18 @@ var init_user_controller = __esm(() => {
123321
123565
  init_schemas_index();
123322
123566
  init_src2();
123323
123567
  init_utils11();
123324
- logger65 = log.scope("UserController");
123568
+ logger66 = log.scope("UserController");
123325
123569
  getMe = requireNonAnonymous(async (ctx) => {
123326
- logger65.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
123570
+ logger66.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
123327
123571
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
123328
123572
  });
123329
123573
  getDemoProfile = requireAnonymous(async (ctx) => {
123330
- logger65.debug("Getting demo profile", { userId: ctx.user.id });
123574
+ logger66.debug("Getting demo profile", { userId: ctx.user.id });
123331
123575
  return ctx.services.user.getDemoProfile(ctx.user.id);
123332
123576
  });
123333
123577
  updateDemoProfile = requireAnonymous(async (ctx) => {
123334
123578
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
123335
- logger65.debug("Updating demo profile", {
123579
+ logger66.debug("Updating demo profile", {
123336
123580
  userId: ctx.user.id,
123337
123581
  displayName: body2.displayName
123338
123582
  });
@@ -123344,13 +123588,13 @@ var init_user_controller = __esm(() => {
123344
123588
  updateDemoProfile
123345
123589
  };
123346
123590
  });
123347
- var logger66;
123591
+ var logger67;
123348
123592
  var init_verify_controller = __esm(() => {
123349
123593
  init_schemas_index();
123350
123594
  init_src2();
123351
123595
  init_errors();
123352
123596
  init_utils11();
123353
- logger66 = log.scope("VerifyController");
123597
+ logger67 = log.scope("VerifyController");
123354
123598
  });
123355
123599
  var init_controllers = __esm(() => {
123356
123600
  init_achievement_controller();
@@ -123647,7 +123891,7 @@ var init_uploads = __esm(() => {
123647
123891
  gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
123648
123892
  gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
123649
123893
  });
123650
- var logger67;
123894
+ var logger68;
123651
123895
  var gameDeployRouter;
123652
123896
  var init_deploy = __esm(() => {
123653
123897
  init_drizzle_orm();
@@ -123658,7 +123902,7 @@ var init_deploy = __esm(() => {
123658
123902
  init_src2();
123659
123903
  init_api();
123660
123904
  init_uploads();
123661
- logger67 = log.scope("SandboxDeploy");
123905
+ logger68 = log.scope("SandboxDeploy");
123662
123906
  gameDeployRouter = new Hono2;
123663
123907
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
123664
123908
  const user = c2.get("user");
@@ -123775,7 +124019,7 @@ var init_deploy = __esm(() => {
123775
124019
  completedAt: now2
123776
124020
  };
123777
124021
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
123778
- logger67.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
124022
+ logger68.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
123779
124023
  return c2.json({
123780
124024
  id: insertedJob.id,
123781
124025
  status: "succeeded",
@@ -124281,6 +124525,7 @@ var init_timeback6 = __esm(() => {
124281
124525
  timebackRouter.delete("/integrations/:gameId", handle2(timeback2.deleteIntegrations, { status: 204 }));
124282
124526
  timebackRouter.post("/end-activity", handle2(timeback2.endActivity));
124283
124527
  timebackRouter.post("/heartbeat", handle2(timeback2.heartbeat));
124528
+ timebackRouter.post("/advance-course", handle2(timeback2.advanceCourse));
124284
124529
  timebackRouter.get("/user", async (c2) => {
124285
124530
  const user = c2.get("user");
124286
124531
  const gameId = c2.get("gameId");
@@ -124366,7 +124611,7 @@ function verifyMockToken(idToken) {
124366
124611
  throw new Error("Invalid LTI token format");
124367
124612
  }
124368
124613
  }
124369
- var logger68;
124614
+ var logger69;
124370
124615
  var ltiRouter;
124371
124616
  var init_lti = __esm(() => {
124372
124617
  init_drizzle_orm();
@@ -124377,7 +124622,7 @@ var init_lti = __esm(() => {
124377
124622
  init_src2();
124378
124623
  init_constants();
124379
124624
  init_api();
124380
- logger68 = log.scope("SandboxLti");
124625
+ logger69 = log.scope("SandboxLti");
124381
124626
  ltiRouter = new Hono2;
124382
124627
  ltiRouter.post("/launch", async (c2) => {
124383
124628
  const db2 = c2.get("db");
@@ -124395,7 +124640,7 @@ var init_lti = __esm(() => {
124395
124640
  claims = verifyMockToken(idToken);
124396
124641
  } catch (error2) {
124397
124642
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
124398
- logger68.error("LTI token verification failed", { error: errorMessage });
124643
+ logger69.error("LTI token verification failed", { error: errorMessage });
124399
124644
  return c2.json({
124400
124645
  error: "invalid_token",
124401
124646
  message: errorMessage
@@ -124403,7 +124648,7 @@ var init_lti = __esm(() => {
124403
124648
  }
124404
124649
  const validationError = validateLtiClaims(claims);
124405
124650
  if (validationError) {
124406
- logger68.warn("LTI claims validation failed", {
124651
+ logger69.warn("LTI claims validation failed", {
124407
124652
  error: validationError,
124408
124653
  sub: claims.sub
124409
124654
  });
@@ -124423,7 +124668,7 @@ var init_lti = __esm(() => {
124423
124668
  createdAt: new Date,
124424
124669
  updatedAt: new Date
124425
124670
  });
124426
- logger68.info("LTI launch successful", { userId: user.id });
124671
+ logger69.info("LTI launch successful", { userId: user.id });
124427
124672
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
124428
124673
  const currentHost = new URL(c2.req.url).hostname;
124429
124674
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -124431,7 +124676,7 @@ var init_lti = __esm(() => {
124431
124676
  return c2.redirect(redirectPath);
124432
124677
  } catch (error2) {
124433
124678
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
124434
- logger68.error("Unexpected error during LTI launch", { error: errorMessage });
124679
+ logger69.error("Unexpected error during LTI launch", { error: errorMessage });
124435
124680
  return c2.json({
124436
124681
  error: "unexpected_error",
124437
124682
  message: "An unexpected error occurred during LTI launch"
@@ -125419,7 +125664,8 @@ var init_timeback7 = __esm7(() => {
125419
125664
  TIMEBACK_ROUTES2 = {
125420
125665
  END_ACTIVITY: "/integrations/timeback/end-activity",
125421
125666
  GET_XP: "/integrations/timeback/xp",
125422
- HEARTBEAT: "/integrations/timeback/heartbeat"
125667
+ HEARTBEAT: "/integrations/timeback/heartbeat",
125668
+ ADVANCE_COURSE: "/integrations/timeback/advance-course"
125423
125669
  };
125424
125670
  TIMEBACK_COURSE_DEFAULTS2 = {
125425
125671
  gradingScheme: "STANDARD",
@@ -126096,7 +126342,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
126096
126342
  // package.json
126097
126343
  var package_default2 = {
126098
126344
  name: "@playcademy/vite-plugin",
126099
- version: "0.2.26-beta.3",
126345
+ version: "0.2.26-beta.4",
126100
126346
  type: "module",
126101
126347
  exports: {
126102
126348
  ".": {