@playcademy/sandbox 0.3.17-beta.17 → 0.3.17-beta.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -401,7 +401,8 @@ var init_timeback2 = __esm(() => {
401
401
  TIMEBACK_ROUTES = {
402
402
  END_ACTIVITY: "/integrations/timeback/end-activity",
403
403
  GET_XP: "/integrations/timeback/xp",
404
- HEARTBEAT: "/integrations/timeback/heartbeat"
404
+ HEARTBEAT: "/integrations/timeback/heartbeat",
405
+ ADVANCE_COURSE: "/integrations/timeback/advance-course"
405
406
  };
406
407
  TIMEBACK_COURSE_DEFAULTS = {
407
408
  gradingScheme: "STANDARD",
@@ -1329,7 +1330,7 @@ var package_default;
1329
1330
  var init_package = __esm(() => {
1330
1331
  package_default = {
1331
1332
  name: "@playcademy/sandbox",
1332
- version: "0.3.17-beta.17",
1333
+ version: "0.3.17-beta.19",
1333
1334
  description: "Local development server for Playcademy game development",
1334
1335
  type: "module",
1335
1336
  exports: {
@@ -28900,7 +28901,8 @@ var init_constants3 = __esm(() => {
28900
28901
  TIMEBACK: {
28901
28902
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
28902
28903
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
28903
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`
28904
+ HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
28905
+ ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`
28904
28906
  }
28905
28907
  };
28906
28908
  });
@@ -31231,6 +31233,7 @@ class TimebackAdminService {
31231
31233
  await client.edubridge.enrollments.enroll(data.studentId, data.courseId, {
31232
31234
  role: "student"
31233
31235
  });
31236
+ client.invalidateEnrollments(data.studentId);
31234
31237
  return { status: "ok" };
31235
31238
  }
31236
31239
  async unenrollStudent(data, user) {
@@ -31243,6 +31246,7 @@ class TimebackAdminService {
31243
31246
  throw new NotFoundError("Timeback integration", `${data.gameId}:${data.courseId}`);
31244
31247
  }
31245
31248
  await client.edubridge.enrollments.unenroll(data.studentId, data.courseId);
31249
+ client.invalidateEnrollments(data.studentId);
31246
31250
  return { status: "ok" };
31247
31251
  }
31248
31252
  async getCompletionStatus(client, courseId, studentId) {
@@ -31299,8 +31303,106 @@ var init_timeback_admin_service = __esm(() => {
31299
31303
  logger16 = log.scope("TimebackAdminService");
31300
31304
  });
31301
31305
 
31306
+ // ../api-core/src/utils/timeback-promotion.util.ts
31307
+ async function promoteCompletedCourse({
31308
+ db: db2,
31309
+ client,
31310
+ currentIntegration,
31311
+ studentId,
31312
+ enrollments: prefetchedEnrollments
31313
+ }) {
31314
+ const subjectIntegrations = await db2.query.gameTimebackIntegrations.findMany({
31315
+ where: and(eq(gameTimebackIntegrations.gameId, currentIntegration.gameId), eq(gameTimebackIntegrations.subject, currentIntegration.subject))
31316
+ });
31317
+ const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
31318
+ if (!nextIntegration) {
31319
+ logger17.debug("Skipping promotion because no next course is configured", {
31320
+ gameId: currentIntegration.gameId,
31321
+ studentId,
31322
+ grade: currentIntegration.grade,
31323
+ subject: currentIntegration.subject,
31324
+ currentCourseId: currentIntegration.courseId
31325
+ });
31326
+ return {
31327
+ status: "no-next-course",
31328
+ currentCourseId: currentIntegration.courseId
31329
+ };
31330
+ }
31331
+ const enrollments = prefetchedEnrollments ?? await client.edubridge.enrollments.listByUser(studentId);
31332
+ const currentEnrollment = enrollments.find((enrollment) => enrollment.course.id === currentIntegration.courseId);
31333
+ const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
31334
+ if (!currentEnrollment) {
31335
+ if (nextEnrollment) {
31336
+ logger17.debug("Skipping promotion because student is already on the next course", {
31337
+ gameId: currentIntegration.gameId,
31338
+ studentId,
31339
+ grade: currentIntegration.grade,
31340
+ subject: currentIntegration.subject,
31341
+ currentCourseId: currentIntegration.courseId,
31342
+ nextCourseId: nextIntegration.courseId
31343
+ });
31344
+ return {
31345
+ status: "already-promoted",
31346
+ currentCourseId: currentIntegration.courseId,
31347
+ nextCourseId: nextIntegration.courseId
31348
+ };
31349
+ }
31350
+ logger17.debug("Skipping promotion because student is not enrolled in the current course", {
31351
+ gameId: currentIntegration.gameId,
31352
+ studentId,
31353
+ grade: currentIntegration.grade,
31354
+ subject: currentIntegration.subject,
31355
+ currentCourseId: currentIntegration.courseId,
31356
+ nextCourseId: nextIntegration.courseId
31357
+ });
31358
+ return {
31359
+ status: "not-enrolled",
31360
+ currentCourseId: currentIntegration.courseId,
31361
+ nextCourseId: nextIntegration.courseId
31362
+ };
31363
+ }
31364
+ const schoolId = currentEnrollment.school.id;
31365
+ let mutatedEnrollments = false;
31366
+ try {
31367
+ if (!nextEnrollment) {
31368
+ await client.edubridge.enrollments.enroll(studentId, nextIntegration.courseId, {
31369
+ role: "student",
31370
+ ...schoolId ? { schoolId } : {}
31371
+ });
31372
+ mutatedEnrollments = true;
31373
+ }
31374
+ await client.edubridge.enrollments.unenroll(studentId, currentIntegration.courseId, schoolId ? { schoolId } : {});
31375
+ mutatedEnrollments = true;
31376
+ } finally {
31377
+ if (mutatedEnrollments) {
31378
+ client.invalidateEnrollments(studentId);
31379
+ }
31380
+ }
31381
+ logger17.info("Promoted student to next course", {
31382
+ gameId: currentIntegration.gameId,
31383
+ studentId,
31384
+ subject: currentIntegration.subject,
31385
+ fromGrade: currentIntegration.grade,
31386
+ toGrade: nextIntegration.grade,
31387
+ currentCourseId: currentIntegration.courseId,
31388
+ nextCourseId: nextIntegration.courseId
31389
+ });
31390
+ return {
31391
+ status: "promoted",
31392
+ currentCourseId: currentIntegration.courseId,
31393
+ nextCourseId: nextIntegration.courseId
31394
+ };
31395
+ }
31396
+ var logger17;
31397
+ var init_timeback_promotion_util = __esm(() => {
31398
+ init_drizzle_orm();
31399
+ init_tables_index();
31400
+ init_src2();
31401
+ logger17 = log.scope("TimebackPromotion");
31402
+ });
31403
+
31302
31404
  // ../api-core/src/services/timeback.service.ts
31303
- var logger17, TimebackService;
31405
+ var logger18, TimebackService;
31304
31406
  var init_timeback_service = __esm(() => {
31305
31407
  init_drizzle_orm();
31306
31408
  init_src();
@@ -31309,8 +31411,9 @@ var init_timeback_service = __esm(() => {
31309
31411
  init_types4();
31310
31412
  init_src4();
31311
31413
  init_errors();
31414
+ init_timeback_promotion_util();
31312
31415
  init_timeback_util();
31313
- logger17 = log.scope("TimebackService");
31416
+ logger18 = log.scope("TimebackService");
31314
31417
  TimebackService = class TimebackService {
31315
31418
  static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
31316
31419
  static processedHeartbeatWindows = new Map;
@@ -31356,7 +31459,7 @@ var init_timeback_service = __esm(() => {
31356
31459
  }
31357
31460
  requireClient() {
31358
31461
  if (!this.deps.timeback) {
31359
- logger17.error("Timeback client not available in context");
31462
+ logger18.error("Timeback client not available in context");
31360
31463
  throw new ValidationError("Timeback integration not available in this environment");
31361
31464
  }
31362
31465
  return this.deps.timeback;
@@ -31409,7 +31512,7 @@ var init_timeback_service = __esm(() => {
31409
31512
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
31410
31513
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
31411
31514
  if (!result) {
31412
- logger17.error("Daily XP upsert returned no rows", { userId, date: targetDate });
31515
+ logger18.error("Daily XP upsert returned no rows", { userId, date: targetDate });
31413
31516
  throw new InternalError("Failed to update daily XP record");
31414
31517
  }
31415
31518
  return { xp: result.xp, date: result.date.toISOString() };
@@ -31440,7 +31543,7 @@ var init_timeback_service = __esm(() => {
31440
31543
  columns: { id: true, timebackId: true }
31441
31544
  });
31442
31545
  if (dbUser?.timebackId) {
31443
- logger17.info("Student already onboarded", { userId: user.id });
31546
+ logger18.info("Student already onboarded", { userId: user.id });
31444
31547
  return { status: "already_populated" };
31445
31548
  }
31446
31549
  let timebackId;
@@ -31449,7 +31552,7 @@ var init_timeback_service = __esm(() => {
31449
31552
  const existingUser = await client.oneroster.users.findByEmail(user.email);
31450
31553
  timebackId = existingUser.sourcedId;
31451
31554
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
31452
- logger17.info("Found existing student in OneRoster", {
31555
+ logger18.info("Found existing student in OneRoster", {
31453
31556
  userId: user.id,
31454
31557
  timebackId
31455
31558
  });
@@ -31478,7 +31581,7 @@ var init_timeback_service = __esm(() => {
31478
31581
  }
31479
31582
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
31480
31583
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
31481
- logger17.info("Created student in OneRoster", { userId: user.id, timebackId });
31584
+ logger18.info("Created student in OneRoster", { userId: user.id, timebackId });
31482
31585
  }
31483
31586
  const assessments = await this.fetchAssessments(timebackId);
31484
31587
  await db2.transaction(async (tx) => {
@@ -31512,7 +31615,7 @@ var init_timeback_service = __esm(() => {
31512
31615
  }
31513
31616
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
31514
31617
  if (!updated) {
31515
- logger17.error("User Timeback ID update returned no rows", {
31618
+ logger18.error("User Timeback ID update returned no rows", {
31516
31619
  userId: user.id,
31517
31620
  timebackId
31518
31621
  });
@@ -31536,13 +31639,13 @@ var init_timeback_service = __esm(() => {
31536
31639
  }
31537
31640
  offset += limit;
31538
31641
  }
31539
- logger17.debug("Fetched assessments", {
31642
+ logger18.debug("Fetched assessments", {
31540
31643
  studentSourcedId,
31541
31644
  totalCount: allAssessments.length
31542
31645
  });
31543
31646
  return allAssessments;
31544
31647
  } catch (error) {
31545
- logger17.warn("Failed to fetch assessments", { studentSourcedId, error });
31648
+ logger18.warn("Failed to fetch assessments", { studentSourcedId, error });
31546
31649
  return [];
31547
31650
  }
31548
31651
  }
@@ -31655,7 +31758,7 @@ var init_timeback_service = __esm(() => {
31655
31758
  masterableUnits: derivedMasterableUnits
31656
31759
  } = courseConfig;
31657
31760
  if (!isTimebackSubject(subjectInput)) {
31658
- logger17.warn("Invalid Timeback subject in course config", {
31761
+ logger18.warn("Invalid Timeback subject in course config", {
31659
31762
  subject: subjectInput,
31660
31763
  courseCode,
31661
31764
  title
@@ -31663,7 +31766,7 @@ var init_timeback_service = __esm(() => {
31663
31766
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
31664
31767
  }
31665
31768
  if (!isTimebackGrade(grade)) {
31666
- logger17.warn("Invalid Timeback grade in course config", {
31769
+ logger18.warn("Invalid Timeback grade in course config", {
31667
31770
  grade,
31668
31771
  courseCode,
31669
31772
  title
@@ -31675,7 +31778,7 @@ var init_timeback_service = __esm(() => {
31675
31778
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
31676
31779
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
31677
31780
  if (typeof totalXp !== "number") {
31678
- logger17.warn("Course missing totalXp in Timeback config", {
31781
+ logger18.warn("Course missing totalXp in Timeback config", {
31679
31782
  courseCode,
31680
31783
  title
31681
31784
  });
@@ -31876,7 +31979,7 @@ var init_timeback_service = __esm(() => {
31876
31979
  ...runId ? { runId } : {}
31877
31980
  });
31878
31981
  }
31879
- logger17.info("Recorded activity completion", {
31982
+ logger18.info("Recorded activity completion", {
31880
31983
  gameId,
31881
31984
  courseId: integration.courseId,
31882
31985
  studentId,
@@ -31893,6 +31996,72 @@ var init_timeback_service = __esm(() => {
31893
31996
  inProgress: result.inProgress
31894
31997
  };
31895
31998
  }
31999
+ async advanceCourse({
32000
+ gameId,
32001
+ studentId,
32002
+ subject,
32003
+ user
32004
+ }) {
32005
+ const client = this.requireClient();
32006
+ const db2 = this.deps.db;
32007
+ await this.deps.validateDeveloperAccess(user, gameId);
32008
+ const integrations = await db2.query.gameTimebackIntegrations.findMany({
32009
+ where: subject ? and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.subject, subject)) : eq(gameTimebackIntegrations.gameId, gameId)
32010
+ });
32011
+ if (integrations.length === 0) {
32012
+ throw new NotFoundError(subject ? `Timeback integrations for game (subject ${subject})` : "Timeback integrations for game");
32013
+ }
32014
+ const enrollments = await client.edubridge.enrollments.listByUser(studentId);
32015
+ const enrolledCourseIds = new Set(enrollments.map((e) => e.course.id));
32016
+ const enrolledIntegrations = integrations.filter((integration) => enrolledCourseIds.has(integration.courseId));
32017
+ if (enrolledIntegrations.length === 0) {
32018
+ throw new NotFoundError(subject ? `Active enrollment for game ladder (subject ${subject})` : "Active enrollment for game ladder");
32019
+ }
32020
+ const subjectsInPlay = new Set(enrolledIntegrations.map((i2) => i2.subject));
32021
+ if (subjectsInPlay.size > 1) {
32022
+ throw new ValidationError(`Ambiguous Timeback advance: student is enrolled in ${subjectsInPlay.size} parallel ladders (${[...subjectsInPlay].join(", ")}); pass { subject } to disambiguate`);
32023
+ }
32024
+ const currentIntegration = enrolledIntegrations.toSorted((left, right) => left.grade - right.grade)[0];
32025
+ const masteryStatus = await client.getMasteryStatus(currentIntegration.courseId, studentId);
32026
+ if (!masteryStatus) {
32027
+ 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().`);
32028
+ }
32029
+ if (!masteryStatus.isComplete) {
32030
+ const promotion2 = {
32031
+ status: "not-mastered",
32032
+ currentCourseId: currentIntegration.courseId,
32033
+ masteredUnits: masteryStatus.masteredUnits,
32034
+ masterableUnits: masteryStatus.masterableUnits
32035
+ };
32036
+ logger18.debug("Skipping course advancement because mastery is incomplete", {
32037
+ gameId,
32038
+ studentId,
32039
+ subject: currentIntegration.subject,
32040
+ grade: currentIntegration.grade,
32041
+ currentCourseId: currentIntegration.courseId,
32042
+ masteredUnits: masteryStatus.masteredUnits,
32043
+ masterableUnits: masteryStatus.masterableUnits
32044
+ });
32045
+ return { status: "ok", promotion: promotion2 };
32046
+ }
32047
+ const promotion = await promoteCompletedCourse({
32048
+ db: db2,
32049
+ client,
32050
+ currentIntegration,
32051
+ studentId,
32052
+ enrollments
32053
+ });
32054
+ logger18.info("Manually advanced student", {
32055
+ gameId,
32056
+ studentId,
32057
+ subject: currentIntegration.subject,
32058
+ grade: currentIntegration.grade,
32059
+ promotionStatus: promotion.status,
32060
+ currentCourseId: promotion.currentCourseId,
32061
+ nextCourseId: promotion.nextCourseId
32062
+ });
32063
+ return { status: "ok", promotion };
32064
+ }
31896
32065
  async recordHeartbeat({
31897
32066
  gameId,
31898
32067
  studentId,
@@ -31915,7 +32084,7 @@ var init_timeback_service = __esm(() => {
31915
32084
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
31916
32085
  const effectiveResumeId = resumeId ?? runId;
31917
32086
  if (TimebackService.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
31918
- logger17.debug("Skipping duplicate heartbeat window", {
32087
+ logger18.debug("Skipping duplicate heartbeat window", {
31919
32088
  gameId,
31920
32089
  studentId,
31921
32090
  runId,
@@ -31928,7 +32097,7 @@ var init_timeback_service = __esm(() => {
31928
32097
  await this.deps.validateDeveloperAccess(user, gameId);
31929
32098
  const inFlightHeartbeat = TimebackService.getInFlightHeartbeatWindow(heartbeatWindowKey);
31930
32099
  if (inFlightHeartbeat) {
31931
- logger17.debug("Joining in-flight heartbeat window", {
32100
+ logger18.debug("Joining in-flight heartbeat window", {
31932
32101
  gameId,
31933
32102
  studentId,
31934
32103
  runId,
@@ -31965,7 +32134,7 @@ var init_timeback_service = __esm(() => {
31965
32134
  });
31966
32135
  }
31967
32136
  TimebackService.markHeartbeatWindowProcessed(heartbeatWindowKey);
31968
- logger17.debug("Recorded heartbeat", {
32137
+ logger18.debug("Recorded heartbeat", {
31969
32138
  gameId,
31970
32139
  courseId: integration.courseId,
31971
32140
  studentId,
@@ -32000,7 +32169,7 @@ var init_timeback_service = __esm(() => {
32000
32169
  });
32001
32170
  courseIds = integrations.map((i2) => i2.courseId);
32002
32171
  if (courseIds.length === 0) {
32003
- logger17.debug("No integrations found for game, returning 0 XP", {
32172
+ logger18.debug("No integrations found for game, returning 0 XP", {
32004
32173
  timebackId,
32005
32174
  gameId: options.gameId,
32006
32175
  grade: options.grade,
@@ -32017,7 +32186,7 @@ var init_timeback_service = __esm(() => {
32017
32186
  courseIds: courseIds.length > 0 ? courseIds : undefined,
32018
32187
  include: options?.include
32019
32188
  });
32020
- logger17.debug("Retrieved student XP", {
32189
+ logger18.debug("Retrieved student XP", {
32021
32190
  timebackId,
32022
32191
  gameId: options?.gameId,
32023
32192
  grade: options?.grade,
@@ -32046,15 +32215,15 @@ class UploadService {
32046
32215
  const { fileName, gameId } = request;
32047
32216
  const bucketName = this.deps.uploadBucket;
32048
32217
  if (!bucketName) {
32049
- logger18.error("Upload bucket not configured in environment");
32218
+ logger19.error("Upload bucket not configured in environment");
32050
32219
  throw new ValidationError("Upload bucket not configured");
32051
32220
  }
32052
32221
  await this.deps.validateDeveloperAccess(user, gameId);
32053
32222
  const version2 = ulid();
32054
32223
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
32055
- logger18.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
32224
+ logger19.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
32056
32225
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
32057
- logger18.info("Presigned URL generated", {
32226
+ logger19.info("Presigned URL generated", {
32058
32227
  userId: user.id,
32059
32228
  gameId,
32060
32229
  version: version2
@@ -32067,12 +32236,12 @@ class UploadService {
32067
32236
  };
32068
32237
  }
32069
32238
  }
32070
- var logger18;
32239
+ var logger19;
32071
32240
  var init_upload_service = __esm(() => {
32072
32241
  init_node();
32073
32242
  init_src2();
32074
32243
  init_errors();
32075
- logger18 = log.scope("UploadService");
32244
+ logger19 = log.scope("UploadService");
32076
32245
  });
32077
32246
 
32078
32247
  // ../api-core/src/services/factory/platform.ts
@@ -32377,7 +32546,7 @@ class AchievementService {
32377
32546
  results.push(result);
32378
32547
  }
32379
32548
  }
32380
- logger19.debug("Listed current achievements", { userId: user.id, count: results.length });
32549
+ logger20.debug("Listed current achievements", { userId: user.id, count: results.length });
32381
32550
  return results;
32382
32551
  }
32383
32552
  async listHistory(user, limit) {
@@ -32395,14 +32564,14 @@ class AchievementService {
32395
32564
  createdAt: c.createdAt,
32396
32565
  scopeKey: c.scopeKey
32397
32566
  }));
32398
- logger19.debug("Listed achievement history", { userId: user.id, count: results.length });
32567
+ logger20.debug("Listed achievement history", { userId: user.id, count: results.length });
32399
32568
  return results;
32400
32569
  }
32401
32570
  async submitProgress(achievementId, user) {
32402
32571
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
32403
32572
  broadcast: false
32404
32573
  });
32405
- logger19.debug("Submitted progress", {
32574
+ logger20.debug("Submitted progress", {
32406
32575
  userId: user.id,
32407
32576
  achievementId,
32408
32577
  wasNewClaim
@@ -32434,7 +32603,7 @@ class AchievementService {
32434
32603
  rewardCredits
32435
32604
  }).returning();
32436
32605
  if (!newClaim) {
32437
- logger19.error("Achievement claim insert returned no rows", {
32606
+ logger20.error("Achievement claim insert returned no rows", {
32438
32607
  userId,
32439
32608
  achievementId,
32440
32609
  scopeKey
@@ -32443,7 +32612,7 @@ class AchievementService {
32443
32612
  }
32444
32613
  await this.deps.addCredits(userId, rewardCredits);
32445
32614
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
32446
- logger19.info("Awarded achievement", {
32615
+ logger20.info("Awarded achievement", {
32447
32616
  userId,
32448
32617
  achievementId,
32449
32618
  scopeKey,
@@ -32480,7 +32649,7 @@ class AchievementService {
32480
32649
  return { title, body: body2 };
32481
32650
  }
32482
32651
  }
32483
- var logger19;
32652
+ var logger20;
32484
32653
  var init_achievement_service = __esm(() => {
32485
32654
  init_drizzle_orm();
32486
32655
  init_tables_index();
@@ -32489,7 +32658,7 @@ var init_achievement_service = __esm(() => {
32489
32658
  init_errors();
32490
32659
  init_leaderboard_util();
32491
32660
  init_scope_util();
32492
- logger19 = log.scope("AchievementService");
32661
+ logger20 = log.scope("AchievementService");
32493
32662
  });
32494
32663
 
32495
32664
  // ../api-core/src/services/inventory.service.ts
@@ -32517,7 +32686,7 @@ class InventoryService {
32517
32686
  },
32518
32687
  updatedAt: inventoryItems.updatedAt
32519
32688
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
32520
- logger20.debug("Listed inventory", { userId: user.id, count: inventory.length });
32689
+ logger21.debug("Listed inventory", { userId: user.id, count: inventory.length });
32521
32690
  return inventory;
32522
32691
  }
32523
32692
  async addItem(itemId, quantity, user) {
@@ -32534,7 +32703,7 @@ class InventoryService {
32534
32703
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
32535
32704
  return inserted?.quantity ?? 0;
32536
32705
  });
32537
- logger20.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
32706
+ logger21.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
32538
32707
  return { newTotal };
32539
32708
  }
32540
32709
  async removeItem(itemId, quantity, user) {
@@ -32545,7 +32714,7 @@ class InventoryService {
32545
32714
  }
32546
32715
  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);
32547
32716
  if (!currentItem) {
32548
- logger20.warn("Insufficient inventory for removal", {
32717
+ logger21.warn("Insufficient inventory for removal", {
32549
32718
  userId: user.id,
32550
32719
  itemId,
32551
32720
  requestedQuantity: quantity
@@ -32555,13 +32724,13 @@ class InventoryService {
32555
32724
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
32556
32725
  return updated?.quantity ?? 0;
32557
32726
  });
32558
- logger20.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
32727
+ logger21.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
32559
32728
  return { newTotal };
32560
32729
  }
32561
32730
  async addCredits(userId, amount) {
32562
32731
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
32563
32732
  if (!creditsItem) {
32564
- logger20.error("Primary currency not found", {
32733
+ logger21.error("Primary currency not found", {
32565
32734
  userId,
32566
32735
  amount
32567
32736
  });
@@ -32574,17 +32743,17 @@ class InventoryService {
32574
32743
  updatedAt: new Date
32575
32744
  }
32576
32745
  });
32577
- logger20.debug("Added credits", { userId, amount });
32746
+ logger21.debug("Added credits", { userId, amount });
32578
32747
  }
32579
32748
  }
32580
- var logger20;
32749
+ var logger21;
32581
32750
  var init_inventory_service = __esm(() => {
32582
32751
  init_drizzle_orm();
32583
32752
  init_src();
32584
32753
  init_tables_index();
32585
32754
  init_src2();
32586
32755
  init_errors();
32587
- logger20 = log.scope("InventoryService");
32756
+ logger21 = log.scope("InventoryService");
32588
32757
  });
32589
32758
 
32590
32759
  // ../types/src/notification.ts
@@ -32639,7 +32808,7 @@ class LeaderboardService {
32639
32808
  sessionId
32640
32809
  }).returning();
32641
32810
  if (!newScore) {
32642
- logger21.error("Score insert returned no rows", { userId, gameId, score: input.score });
32811
+ logger22.error("Score insert returned no rows", { userId, gameId, score: input.score });
32643
32812
  throw new InternalError("Failed to insert score");
32644
32813
  }
32645
32814
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -32667,7 +32836,7 @@ class LeaderboardService {
32667
32836
  movedUpWithinTop3
32668
32837
  });
32669
32838
  }
32670
- logger21.info("Score submitted", {
32839
+ logger22.info("Score submitted", {
32671
32840
  gameId,
32672
32841
  userId,
32673
32842
  isAnonymousUser,
@@ -32744,7 +32913,7 @@ class LeaderboardService {
32744
32913
  });
32745
32914
  }
32746
32915
  } catch (error) {
32747
- logger21.warn("Failed to publish notification", { error });
32916
+ logger22.warn("Failed to publish notification", { error });
32748
32917
  }
32749
32918
  }
32750
32919
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -32863,7 +33032,7 @@ class LeaderboardService {
32863
33032
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
32864
33033
  }
32865
33034
  }
32866
- var logger21;
33035
+ var logger22;
32867
33036
  var init_leaderboard_service = __esm(() => {
32868
33037
  init_drizzle_orm();
32869
33038
  init_src();
@@ -32873,7 +33042,7 @@ var init_leaderboard_service = __esm(() => {
32873
33042
  init_notification();
32874
33043
  init_errors();
32875
33044
  init_leaderboard_util();
32876
- logger21 = log.scope("LeaderboardService");
33045
+ logger22 = log.scope("LeaderboardService");
32877
33046
  });
32878
33047
 
32879
33048
  // ../api-core/src/services/level.service.ts
@@ -32889,9 +33058,9 @@ class LevelService {
32889
33058
  for (const config2 of configs) {
32890
33059
  levelConfigCache.set(config2.level, config2);
32891
33060
  }
32892
- logger22.info("Cache pre-warmed", { count: configs.length });
33061
+ logger23.info("Cache pre-warmed", { count: configs.length });
32893
33062
  } catch (error) {
32894
- logger22.error("Cache pre-warm failed", { error });
33063
+ logger23.error("Cache pre-warm failed", { error });
32895
33064
  }
32896
33065
  }
32897
33066
  async getConfig(level) {
@@ -32925,7 +33094,7 @@ class LevelService {
32925
33094
  totalXP
32926
33095
  }).returning();
32927
33096
  if (!newUserLevel) {
32928
- logger22.error("User level insert returned no rows", { userId: user.id });
33097
+ logger23.error("User level insert returned no rows", { userId: user.id });
32929
33098
  throw new InternalError("Failed to create user level cache record");
32930
33099
  }
32931
33100
  userLevel = newUserLevel;
@@ -32940,7 +33109,7 @@ class LevelService {
32940
33109
  userLevel = updatedUserLevel;
32941
33110
  }
32942
33111
  }
32943
- logger22.debug("Retrieved user level", {
33112
+ logger23.debug("Retrieved user level", {
32944
33113
  userId: user.id,
32945
33114
  totalXP,
32946
33115
  currentLevel,
@@ -32951,7 +33120,7 @@ class LevelService {
32951
33120
  async getProgress(user) {
32952
33121
  const userLevel = await this.getByUser(user);
32953
33122
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
32954
- logger22.debug("Retrieved progress", { userId: user.id });
33123
+ logger23.debug("Retrieved progress", { userId: user.id });
32955
33124
  return {
32956
33125
  level: userLevel.currentLevel,
32957
33126
  currentXp: userLevel.currentXp,
@@ -32985,7 +33154,7 @@ class LevelService {
32985
33154
  if (leveledUp && previousUserLevel) {
32986
33155
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
32987
33156
  }
32988
- logger22.info("Synced from Timeback", {
33157
+ logger23.info("Synced from Timeback", {
32989
33158
  userId,
32990
33159
  totalXP,
32991
33160
  currentLevel,
@@ -33026,7 +33195,7 @@ class LevelService {
33026
33195
  }
33027
33196
  if (totalCredits > 0) {
33028
33197
  await this.deps.addCredits(userId, totalCredits);
33029
- logger22.info("Awarded level-up credits", {
33198
+ logger23.info("Awarded level-up credits", {
33030
33199
  userId,
33031
33200
  fromLevel,
33032
33201
  toLevel,
@@ -33064,14 +33233,14 @@ class LevelService {
33064
33233
  };
33065
33234
  }
33066
33235
  }
33067
- var logger22, levelConfigCache = null;
33236
+ var logger23, levelConfigCache = null;
33068
33237
  var init_level_service = __esm(() => {
33069
33238
  init_drizzle_orm();
33070
33239
  init_src();
33071
33240
  init_tables_index();
33072
33241
  init_src2();
33073
33242
  init_errors();
33074
- logger22 = log.scope("LevelService");
33243
+ logger23 = log.scope("LevelService");
33075
33244
  });
33076
33245
 
33077
33246
  // ../realtime/src/server/domain/events.ts
@@ -33092,13 +33261,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
33092
33261
  });
33093
33262
  if (!res.ok) {
33094
33263
  const text3 = await res.text().catch(() => "");
33095
- logger23.warn("Failed to publish to user", {
33264
+ logger24.warn("Failed to publish to user", {
33096
33265
  status: res.status,
33097
33266
  body: text3
33098
33267
  });
33099
33268
  }
33100
33269
  } catch (error) {
33101
- logger23.error("Publish to user error", { error });
33270
+ logger24.error("Publish to user error", { error });
33102
33271
  }
33103
33272
  }
33104
33273
 
@@ -33120,7 +33289,7 @@ class NotificationService {
33120
33289
  conditions2.push(eq(notifications.type, type));
33121
33290
  }
33122
33291
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
33123
- logger23.debug("Listed notifications", { userId: user.id, count: results.length });
33292
+ logger24.debug("Listed notifications", { userId: user.id, count: results.length });
33124
33293
  return results;
33125
33294
  }
33126
33295
  async updateStatus(notificationId, status, method) {
@@ -33139,7 +33308,7 @@ class NotificationService {
33139
33308
  if (!updated) {
33140
33309
  throw new NotFoundError("Notification", notificationId);
33141
33310
  }
33142
- logger23.debug("Updated status", { notificationId, status });
33311
+ logger24.debug("Updated status", { notificationId, status });
33143
33312
  return updated;
33144
33313
  }
33145
33314
  async getStats(user, options) {
@@ -33165,7 +33334,7 @@ class NotificationService {
33165
33334
  const clicked = statsMap.clicked || 0;
33166
33335
  const dismissed = statsMap.dismissed || 0;
33167
33336
  const expired = statsMap.expired || 0;
33168
- logger23.debug("Retrieved stats", { userId: user.id, total });
33337
+ logger24.debug("Retrieved stats", { userId: user.id, total });
33169
33338
  return {
33170
33339
  total,
33171
33340
  delivered,
@@ -33214,7 +33383,7 @@ class NotificationService {
33214
33383
  options: { data, clickUrl, metadata: metadata2 }
33215
33384
  });
33216
33385
  }
33217
- logger23.debug("Created notification", {
33386
+ logger24.debug("Created notification", {
33218
33387
  userId,
33219
33388
  type,
33220
33389
  id: notificationId,
@@ -33222,7 +33391,7 @@ class NotificationService {
33222
33391
  });
33223
33392
  return notificationId;
33224
33393
  } catch (error) {
33225
- logger23.error("Failed to create notification", { userId, type, error });
33394
+ logger24.error("Failed to create notification", { userId, type, error });
33226
33395
  return null;
33227
33396
  }
33228
33397
  }
@@ -33236,7 +33405,7 @@ class NotificationService {
33236
33405
  }) {
33237
33406
  const realtimeConfig = this.deps.realtime;
33238
33407
  if (!realtimeConfig) {
33239
- logger23.warn("No realtime config for publish");
33408
+ logger24.warn("No realtime config for publish");
33240
33409
  return;
33241
33410
  }
33242
33411
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -33278,13 +33447,13 @@ class NotificationService {
33278
33447
  metadata: data.metadata || {}
33279
33448
  }).returning();
33280
33449
  if (!notification) {
33281
- logger23.error("Notification insert returned no rows", {
33450
+ logger24.error("Notification insert returned no rows", {
33282
33451
  userId: data.userId,
33283
33452
  type: data.type
33284
33453
  });
33285
33454
  throw new InternalError("Failed to create notification");
33286
33455
  }
33287
- logger23.info("Inserted notification", {
33456
+ logger24.info("Inserted notification", {
33288
33457
  notificationId: notification.id,
33289
33458
  userId: notification.userId,
33290
33459
  type: notification.type
@@ -33294,7 +33463,7 @@ class NotificationService {
33294
33463
  async deliverPending(userId) {
33295
33464
  const realtimeConfig = this.deps.realtime;
33296
33465
  if (!realtimeConfig) {
33297
- logger23.warn("No realtime config for delivery");
33466
+ logger24.warn("No realtime config for delivery");
33298
33467
  return;
33299
33468
  }
33300
33469
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -33321,13 +33490,13 @@ class NotificationService {
33321
33490
  metadata: notification.metadata,
33322
33491
  clickUrl: notification.clickUrl
33323
33492
  });
33324
- logger23.info("Delivered notification", {
33493
+ logger24.info("Delivered notification", {
33325
33494
  notificationId: notification.id,
33326
33495
  userId,
33327
33496
  type: notification.type
33328
33497
  });
33329
33498
  } catch (error) {
33330
- logger23.warn("Failed to deliver", {
33499
+ logger24.warn("Failed to deliver", {
33331
33500
  notificationId: notification.id,
33332
33501
  error
33333
33502
  });
@@ -33335,7 +33504,7 @@ class NotificationService {
33335
33504
  }
33336
33505
  }
33337
33506
  }
33338
- var logger23;
33507
+ var logger24;
33339
33508
  var init_notification_service = __esm(() => {
33340
33509
  init_drizzle_orm();
33341
33510
  init_src();
@@ -33344,7 +33513,7 @@ var init_notification_service = __esm(() => {
33344
33513
  init_events();
33345
33514
  init_notification();
33346
33515
  init_errors();
33347
- logger23 = log.scope("NotificationService");
33516
+ logger24 = log.scope("NotificationService");
33348
33517
  });
33349
33518
 
33350
33519
  // ../api-core/src/services/factory/player.ts
@@ -33468,7 +33637,7 @@ class CharacterService {
33468
33637
  createdAt: characterComponents.createdAt,
33469
33638
  updatedAt: characterComponents.updatedAt
33470
33639
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
33471
- logger24.debug("Listed available components", {
33640
+ logger25.debug("Listed available components", {
33472
33641
  level,
33473
33642
  count: components.length
33474
33643
  });
@@ -33486,7 +33655,7 @@ class CharacterService {
33486
33655
  }
33487
33656
  }
33488
33657
  });
33489
- logger24.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
33658
+ logger25.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
33490
33659
  return pc ?? null;
33491
33660
  }
33492
33661
  async getByUserId(userId) {
@@ -33501,7 +33670,7 @@ class CharacterService {
33501
33670
  }
33502
33671
  }
33503
33672
  });
33504
- logger24.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
33673
+ logger25.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
33505
33674
  return pc ?? null;
33506
33675
  }
33507
33676
  async create(input, user) {
@@ -33516,13 +33685,13 @@ class CharacterService {
33516
33685
  }
33517
33686
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
33518
33687
  if (!characterRow) {
33519
- logger24.error("Character insert returned no rows", { userId: user.id });
33688
+ logger25.error("Character insert returned no rows", { userId: user.id });
33520
33689
  throw new InternalError("Failed to create character in database");
33521
33690
  }
33522
33691
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
33523
33692
  return characterRow;
33524
33693
  });
33525
- logger24.info("Created character", { userId: user.id, characterId: result.id });
33694
+ logger25.info("Created character", { userId: user.id, characterId: result.id });
33526
33695
  return result;
33527
33696
  }
33528
33697
  async update(input, user) {
@@ -33534,7 +33703,7 @@ class CharacterService {
33534
33703
  if (!row) {
33535
33704
  throw new NotFoundError("Player character");
33536
33705
  }
33537
- logger24.info("Updated character", {
33706
+ logger25.info("Updated character", {
33538
33707
  userId: user.id,
33539
33708
  characterId: row.id,
33540
33709
  updatedFields: Object.keys(input)
@@ -33556,7 +33725,7 @@ class CharacterService {
33556
33725
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
33557
33726
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
33558
33727
  if (!validation.isValid) {
33559
- logger24.warn("Accessory validation failed", {
33728
+ logger25.warn("Accessory validation failed", {
33560
33729
  userId: user.id,
33561
33730
  slot,
33562
33731
  accessoryComponentId,
@@ -33572,14 +33741,14 @@ class CharacterService {
33572
33741
  slot
33573
33742
  }).returning();
33574
33743
  if (!result) {
33575
- logger24.error("Accessory insert returned no rows", {
33744
+ logger25.error("Accessory insert returned no rows", {
33576
33745
  userId: user.id,
33577
33746
  slot,
33578
33747
  accessoryComponentId
33579
33748
  });
33580
33749
  throw new InternalError("Failed to equip accessory");
33581
33750
  }
33582
- logger24.info("Equipped accessory", {
33751
+ logger25.info("Equipped accessory", {
33583
33752
  userId: user.id,
33584
33753
  slot,
33585
33754
  accessoryComponentId
@@ -33600,7 +33769,7 @@ class CharacterService {
33600
33769
  const playerLevel = userLevel?.currentLevel ?? 1;
33601
33770
  const validation = validateAccessoryRemoval(slot, playerLevel);
33602
33771
  if (!validation.isValid) {
33603
- logger24.warn("Accessory removal validation failed", {
33772
+ logger25.warn("Accessory removal validation failed", {
33604
33773
  userId: user.id,
33605
33774
  slot,
33606
33775
  playerLevel,
@@ -33609,17 +33778,17 @@ class CharacterService {
33609
33778
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
33610
33779
  }
33611
33780
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
33612
- logger24.info("Removed accessory", { userId: user.id, slot });
33781
+ logger25.info("Removed accessory", { userId: user.id, slot });
33613
33782
  }
33614
33783
  }
33615
- var logger24;
33784
+ var logger25;
33616
33785
  var init_character_service = __esm(() => {
33617
33786
  init_drizzle_orm();
33618
33787
  init_tables_index();
33619
33788
  init_src2();
33620
33789
  init_errors();
33621
33790
  init_accessory_util();
33622
- logger24 = log.scope("CharacterService");
33791
+ logger25 = log.scope("CharacterService");
33623
33792
  });
33624
33793
 
33625
33794
  // ../api-core/src/services/currency.service.ts
@@ -33631,7 +33800,7 @@ class CurrencyService {
33631
33800
  async list() {
33632
33801
  const db2 = this.deps.db;
33633
33802
  const allCurrencies = await db2.query.currencies.findMany();
33634
- logger25.debug("Listed currencies", { count: allCurrencies.length });
33803
+ logger26.debug("Listed currencies", { count: allCurrencies.length });
33635
33804
  return allCurrencies;
33636
33805
  }
33637
33806
  async getById(currencyId) {
@@ -33642,7 +33811,7 @@ class CurrencyService {
33642
33811
  if (!currency) {
33643
33812
  throw new NotFoundError("Currency", currencyId);
33644
33813
  }
33645
- logger25.debug("Retrieved currency", { currencyId });
33814
+ logger26.debug("Retrieved currency", { currencyId });
33646
33815
  return currency;
33647
33816
  }
33648
33817
  async create(data) {
@@ -33650,13 +33819,13 @@ class CurrencyService {
33650
33819
  try {
33651
33820
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
33652
33821
  if (!newCurrency) {
33653
- logger25.error("Currency insert returned no rows", {
33822
+ logger26.error("Currency insert returned no rows", {
33654
33823
  itemId: data.itemId,
33655
33824
  symbol: data.symbol
33656
33825
  });
33657
33826
  throw new InternalError("Failed to create currency");
33658
33827
  }
33659
- logger25.info("Created currency", {
33828
+ logger26.info("Created currency", {
33660
33829
  currencyId: newCurrency.id,
33661
33830
  itemId: newCurrency.itemId,
33662
33831
  symbol: newCurrency.symbol,
@@ -33685,7 +33854,7 @@ class CurrencyService {
33685
33854
  if (!updatedCurrency) {
33686
33855
  throw new NotFoundError("Currency", currencyId);
33687
33856
  }
33688
- logger25.info("Updated currency", {
33857
+ logger26.info("Updated currency", {
33689
33858
  currencyId: updatedCurrency.id,
33690
33859
  updatedFields: Object.keys(data)
33691
33860
  });
@@ -33711,16 +33880,16 @@ class CurrencyService {
33711
33880
  if (result.length === 0) {
33712
33881
  throw new NotFoundError("Currency", currencyId);
33713
33882
  }
33714
- logger25.info("Deleted currency", { currencyId });
33883
+ logger26.info("Deleted currency", { currencyId });
33715
33884
  }
33716
33885
  }
33717
- var logger25;
33886
+ var logger26;
33718
33887
  var init_currency_service = __esm(() => {
33719
33888
  init_drizzle_orm();
33720
33889
  init_tables_index();
33721
33890
  init_src2();
33722
33891
  init_errors();
33723
- logger25 = log.scope("CurrencyService");
33892
+ logger26 = log.scope("CurrencyService");
33724
33893
  });
33725
33894
 
33726
33895
  // ../api-core/src/services/logs.service.ts
@@ -33739,11 +33908,11 @@ class LogsService {
33739
33908
  if (!game) {
33740
33909
  throw new NotFoundError("Game", slug2);
33741
33910
  }
33742
- logger26.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
33911
+ logger27.info("Admin accessing game logs", { adminId: user.id, slug: slug2, environment });
33743
33912
  } else {
33744
33913
  const isApprovedDev = user.developerStatus === "approved";
33745
33914
  if (!isApprovedDev) {
33746
- logger26.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
33915
+ logger27.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
33747
33916
  throw new AccessDeniedError("Must be an approved developer");
33748
33917
  }
33749
33918
  const game = await db2.query.games.findFirst({
@@ -33751,7 +33920,7 @@ class LogsService {
33751
33920
  columns: { id: true }
33752
33921
  });
33753
33922
  if (!game) {
33754
- logger26.warn("Developer attempted access to unowned game logs", {
33923
+ logger27.warn("Developer attempted access to unowned game logs", {
33755
33924
  userId: user.id,
33756
33925
  slug: slug2
33757
33926
  });
@@ -33761,7 +33930,7 @@ class LogsService {
33761
33930
  const isProduction3 = environment === "production";
33762
33931
  const workerId = getDeploymentId(slug2, isProduction3);
33763
33932
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
33764
- logger26.debug("Generated log stream token", {
33933
+ logger27.debug("Generated log stream token", {
33765
33934
  userId: user.id,
33766
33935
  slug: slug2,
33767
33936
  workerId
@@ -33769,14 +33938,14 @@ class LogsService {
33769
33938
  return { token, workerId };
33770
33939
  }
33771
33940
  }
33772
- var logger26;
33941
+ var logger27;
33773
33942
  var init_logs_service = __esm(() => {
33774
33943
  init_drizzle_orm();
33775
33944
  init_tables_index();
33776
33945
  init_src2();
33777
33946
  init_errors();
33778
33947
  init_deployment_util();
33779
- logger26 = log.scope("LogsService");
33948
+ logger27 = log.scope("LogsService");
33780
33949
  });
33781
33950
 
33782
33951
  // ../api-core/src/services/lti.service.ts
@@ -33829,7 +33998,7 @@ class MapService {
33829
33998
  if (!mapDetails) {
33830
33999
  throw new NotFoundError("Map", identifier);
33831
34000
  }
33832
- logger27.debug("Retrieved map", { identifier });
34001
+ logger28.debug("Retrieved map", { identifier });
33833
34002
  return mapDetails;
33834
34003
  }
33835
34004
  async getElements(mapId) {
@@ -33845,7 +34014,7 @@ class MapService {
33845
34014
  }
33846
34015
  }
33847
34016
  });
33848
- logger27.debug("Retrieved elements", { mapId, count: elements.length });
34017
+ logger28.debug("Retrieved elements", { mapId, count: elements.length });
33849
34018
  return elements;
33850
34019
  }
33851
34020
  async getObjects(mapId, userId) {
@@ -33866,7 +34035,7 @@ class MapService {
33866
34035
  }
33867
34036
  }
33868
34037
  });
33869
- logger27.debug("Retrieved objects", { mapId, userId, count: objects.length });
34038
+ logger28.debug("Retrieved objects", { mapId, userId, count: objects.length });
33870
34039
  return objects.map((object) => this.formatMapObjectWithItem(object));
33871
34040
  }
33872
34041
  async createObject(mapId, data, user) {
@@ -33893,7 +34062,7 @@ class MapService {
33893
34062
  throw new NotFoundError("Item", data.itemId);
33894
34063
  }
33895
34064
  if (!item.isPlaceable) {
33896
- logger27.warn("Attempted to place non-placeable item", {
34065
+ logger28.warn("Attempted to place non-placeable item", {
33897
34066
  userId: user.id,
33898
34067
  itemId: data.itemId,
33899
34068
  mapId
@@ -33907,7 +34076,7 @@ class MapService {
33907
34076
  };
33908
34077
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
33909
34078
  if (!createdObject) {
33910
- logger27.error("Map object insert returned no rows", {
34079
+ logger28.error("Map object insert returned no rows", {
33911
34080
  userId: user.id,
33912
34081
  mapId,
33913
34082
  itemId: data.itemId
@@ -33931,12 +34100,12 @@ class MapService {
33931
34100
  }
33932
34101
  });
33933
34102
  if (!objectWithItem) {
33934
- logger27.error("Map object query after insert returned no rows", {
34103
+ logger28.error("Map object query after insert returned no rows", {
33935
34104
  objectId: createdObject.id
33936
34105
  });
33937
34106
  throw new InternalError("Failed to retrieve created object");
33938
34107
  }
33939
- logger27.info("Created object", {
34108
+ logger28.info("Created object", {
33940
34109
  userId: user.id,
33941
34110
  mapId,
33942
34111
  objectId: createdObject.id,
@@ -33960,7 +34129,7 @@ class MapService {
33960
34129
  if (result.length === 0) {
33961
34130
  throw new NotFoundError("MapObject", objectId);
33962
34131
  }
33963
- logger27.info("Deleted object", {
34132
+ logger28.info("Deleted object", {
33964
34133
  userId: user.id,
33965
34134
  mapId,
33966
34135
  objectId
@@ -33989,13 +34158,13 @@ class MapService {
33989
34158
  };
33990
34159
  }
33991
34160
  }
33992
- var logger27;
34161
+ var logger28;
33993
34162
  var init_map_service = __esm(() => {
33994
34163
  init_drizzle_orm();
33995
34164
  init_tables_index();
33996
34165
  init_src2();
33997
34166
  init_errors();
33998
- logger27 = log.scope("MapService");
34167
+ logger28 = log.scope("MapService");
33999
34168
  });
34000
34169
 
34001
34170
  // ../api-core/src/services/realtime.service.ts
@@ -34030,20 +34199,20 @@ class RealtimeService {
34030
34199
  }
34031
34200
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
34032
34201
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
34033
- logger28.info("Generated token", {
34202
+ logger29.info("Generated token", {
34034
34203
  userId: user.id,
34035
34204
  gameId: resolvedGameId || "global"
34036
34205
  });
34037
34206
  return { token };
34038
34207
  }
34039
34208
  }
34040
- var logger28;
34209
+ var logger29;
34041
34210
  var init_realtime_service = __esm(() => {
34042
34211
  init_drizzle_orm();
34043
34212
  init_tables_index();
34044
34213
  init_src2();
34045
34214
  init_errors();
34046
- logger28 = log.scope("RealtimeService");
34215
+ logger29 = log.scope("RealtimeService");
34047
34216
  });
34048
34217
 
34049
34218
  // ../api-core/src/services/session.service.ts
@@ -34078,10 +34247,10 @@ class SessionService {
34078
34247
  };
34079
34248
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
34080
34249
  if (!newSession?.sessionId) {
34081
- logger29.error("Game session insert returned no rows", { userId, gameId });
34250
+ logger30.error("Game session insert returned no rows", { userId, gameId });
34082
34251
  throw new InternalError("Failed to create game session");
34083
34252
  }
34084
- logger29.info("Started new session", {
34253
+ logger30.info("Started new session", {
34085
34254
  sessionId: newSession.sessionId,
34086
34255
  gameId,
34087
34256
  userId
@@ -34102,23 +34271,23 @@ class SessionService {
34102
34271
  return { success: true, message: "Session already ended" };
34103
34272
  }
34104
34273
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
34105
- logger29.info("Ended session", { sessionId, gameId, userId });
34274
+ logger30.info("Ended session", { sessionId, gameId, userId });
34106
34275
  return { success: true };
34107
34276
  }
34108
34277
  async mintToken(gameIdOrSlug, userId) {
34109
34278
  const gameId = await this.resolveGameId(gameIdOrSlug);
34110
34279
  const result = await this.deps.mintGameToken(gameId, userId);
34111
- logger29.debug("Minted game token", { gameId, userId });
34280
+ logger30.debug("Minted game token", { gameId, userId });
34112
34281
  return result;
34113
34282
  }
34114
34283
  }
34115
- var logger29;
34284
+ var logger30;
34116
34285
  var init_session_service = __esm(() => {
34117
34286
  init_drizzle_orm();
34118
34287
  init_tables_index();
34119
34288
  init_src2();
34120
34289
  init_errors();
34121
- logger29 = log.scope("SessionService");
34290
+ logger30 = log.scope("SessionService");
34122
34291
  });
34123
34292
 
34124
34293
  // ../api-core/src/services/shop.service.ts
@@ -34164,7 +34333,7 @@ class ShopService {
34164
34333
  const shopItems = [];
34165
34334
  for (const listing of listingsWithRelations) {
34166
34335
  if (!listing.item || !listing.currency) {
34167
- logger30.warn("Listing missing item or currency, skipping", {
34336
+ logger31.warn("Listing missing item or currency, skipping", {
34168
34337
  listingId: listing.id
34169
34338
  });
34170
34339
  } else {
@@ -34181,7 +34350,7 @@ class ShopService {
34181
34350
  });
34182
34351
  }
34183
34352
  }
34184
- logger30.debug("Retrieved shop view", {
34353
+ logger31.debug("Retrieved shop view", {
34185
34354
  userId: user.id,
34186
34355
  itemCount: shopItems.length,
34187
34356
  currencyCount: shopCurrencies.length
@@ -34192,12 +34361,12 @@ class ShopService {
34192
34361
  };
34193
34362
  }
34194
34363
  }
34195
- var logger30;
34364
+ var logger31;
34196
34365
  var init_shop_service = __esm(() => {
34197
34366
  init_drizzle_orm();
34198
34367
  init_tables_index();
34199
34368
  init_src2();
34200
- logger30 = log.scope("ShopService");
34369
+ logger31 = log.scope("ShopService");
34201
34370
  });
34202
34371
 
34203
34372
  // ../api-core/src/services/sprite.service.ts
@@ -34214,17 +34383,17 @@ class SpriteService {
34214
34383
  if (!template) {
34215
34384
  throw new NotFoundError("SpriteTemplate", slug2);
34216
34385
  }
34217
- logger31.debug("Retrieved sprite", { slug: slug2 });
34386
+ logger32.debug("Retrieved sprite", { slug: slug2 });
34218
34387
  return template;
34219
34388
  }
34220
34389
  }
34221
- var logger31;
34390
+ var logger32;
34222
34391
  var init_sprite_service = __esm(() => {
34223
34392
  init_drizzle_orm();
34224
34393
  init_tables_index();
34225
34394
  init_src2();
34226
34395
  init_errors();
34227
- logger31 = log.scope("SpriteService");
34396
+ logger32 = log.scope("SpriteService");
34228
34397
  });
34229
34398
 
34230
34399
  // ../api-core/src/services/user.service.ts
@@ -34239,12 +34408,12 @@ class UserService {
34239
34408
  where: eq(users.id, user.id)
34240
34409
  });
34241
34410
  if (!userData) {
34242
- logger32.error("User not found", { userId: user.id });
34411
+ logger33.error("User not found", { userId: user.id });
34243
34412
  throw new NotFoundError("User", user.id);
34244
34413
  }
34245
34414
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
34246
34415
  if (gameId) {
34247
- logger32.debug("Fetched user profile (game context)", { userId: user.id, gameId });
34416
+ logger33.debug("Fetched user profile (game context)", { userId: user.id, gameId });
34248
34417
  return {
34249
34418
  id: userData.id,
34250
34419
  name: userData.name,
@@ -34257,7 +34426,7 @@ class UserService {
34257
34426
  const timebackAccount = await db2.query.accounts.findFirst({
34258
34427
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
34259
34428
  });
34260
- logger32.debug("Fetched user profile (platform context)", { userId: user.id });
34429
+ logger33.debug("Fetched user profile (platform context)", { userId: user.id });
34261
34430
  return {
34262
34431
  id: userData.id,
34263
34432
  name: userData.name,
@@ -34280,7 +34449,7 @@ class UserService {
34280
34449
  columns: { name: true }
34281
34450
  });
34282
34451
  if (!userData) {
34283
- logger32.error("Demo user not found", { userId });
34452
+ logger33.error("Demo user not found", { userId });
34284
34453
  throw new NotFoundError("User", userId);
34285
34454
  }
34286
34455
  return {
@@ -34294,10 +34463,10 @@ class UserService {
34294
34463
  updatedAt: new Date
34295
34464
  }).where(eq(users.id, userId)).returning({ name: users.name });
34296
34465
  if (!updatedUser) {
34297
- logger32.error("Demo user not found for profile update", { userId });
34466
+ logger33.error("Demo user not found for profile update", { userId });
34298
34467
  throw new NotFoundError("User", userId);
34299
34468
  }
34300
- logger32.debug("Updated demo profile", { userId, displayName });
34469
+ logger33.debug("Updated demo profile", { userId, displayName });
34301
34470
  return {
34302
34471
  displayName: updatedUser.name,
34303
34472
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -34310,7 +34479,7 @@ class UserService {
34310
34479
  ]);
34311
34480
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
34312
34481
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
34313
- logger32.debug("Fetched Timeback data", {
34482
+ logger33.debug("Fetched Timeback data", {
34314
34483
  timebackId,
34315
34484
  role,
34316
34485
  enrollmentCount: enrollments.length,
@@ -34319,9 +34488,9 @@ class UserService {
34319
34488
  return { id: timebackId, role, enrollments, organizations };
34320
34489
  }
34321
34490
  async fetchStudentProfile(timebackId) {
34322
- logger32.debug("Fetching student profile", { timebackId });
34491
+ logger33.debug("Fetching student profile", { timebackId });
34323
34492
  if (!this.deps.timeback) {
34324
- logger32.warn("Timeback client not available");
34493
+ logger33.warn("Timeback client not available");
34325
34494
  return { role: "student", organizations: [] };
34326
34495
  }
34327
34496
  try {
@@ -34349,14 +34518,14 @@ class UserService {
34349
34518
  }
34350
34519
  return { role, organizations: [...orgMap.values()] };
34351
34520
  } catch (error) {
34352
- logger32.warn("Failed to fetch student profile", { error, timebackId });
34521
+ logger33.warn("Failed to fetch student profile", { error, timebackId });
34353
34522
  return { role: "student", organizations: [] };
34354
34523
  }
34355
34524
  }
34356
34525
  async fetchEnrollments(timebackId) {
34357
- logger32.debug("Fetching enrollments", { timebackId });
34526
+ logger33.debug("Fetching enrollments", { timebackId });
34358
34527
  if (!this.deps.timeback) {
34359
- logger32.warn("Timeback client not available");
34528
+ logger33.warn("Timeback client not available");
34360
34529
  return [];
34361
34530
  }
34362
34531
  try {
@@ -34377,7 +34546,7 @@ class UserService {
34377
34546
  orgId: courseToSchool.get(i2.courseId)
34378
34547
  }));
34379
34548
  } catch (error) {
34380
- logger32.warn("Failed to fetch enrollments", { error, timebackId });
34549
+ logger33.warn("Failed to fetch enrollments", { error, timebackId });
34381
34550
  return [];
34382
34551
  }
34383
34552
  }
@@ -34392,14 +34561,14 @@ class UserService {
34392
34561
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
34393
34562
  }
34394
34563
  }
34395
- var logger32;
34564
+ var logger33;
34396
34565
  var init_user_service = __esm(() => {
34397
34566
  init_drizzle_orm();
34398
34567
  init_src();
34399
34568
  init_tables_index();
34400
34569
  init_src2();
34401
34570
  init_errors();
34402
- logger32 = log.scope("UserService");
34571
+ logger33 = log.scope("UserService");
34403
34572
  });
34404
34573
 
34405
34574
  // ../api-core/src/services/verify.service.ts
@@ -34409,16 +34578,16 @@ class VerifyService {
34409
34578
  this.deps = deps;
34410
34579
  }
34411
34580
  async verifyGameToken(token) {
34412
- logger33.debug("Verifying game token");
34581
+ logger34.debug("Verifying game token");
34413
34582
  const payload = await this.deps.validateGameToken(token);
34414
34583
  if (!payload) {
34415
- logger33.warn("Invalid or expired game token presented");
34584
+ logger34.warn("Invalid or expired game token presented");
34416
34585
  throw new ValidationError("Invalid or expired token");
34417
34586
  }
34418
34587
  const gameId = payload.sub;
34419
34588
  const userId = payload.uid;
34420
34589
  if (typeof gameId !== "string" || typeof userId !== "string") {
34421
- logger33.warn("Game token missing required claims", {
34590
+ logger34.warn("Game token missing required claims", {
34422
34591
  hasGameId: typeof gameId === "string",
34423
34592
  hasUserId: typeof userId === "string"
34424
34593
  });
@@ -34429,7 +34598,7 @@ class VerifyService {
34429
34598
  where: eq(users.id, userId)
34430
34599
  });
34431
34600
  if (!userData) {
34432
- logger33.error("User not found for valid token", {
34601
+ logger34.error("User not found for valid token", {
34433
34602
  userId
34434
34603
  });
34435
34604
  throw new NotFoundError("User", userId);
@@ -34443,7 +34612,7 @@ class VerifyService {
34443
34612
  family_name: undefined,
34444
34613
  timeback_id: userData.timebackId || undefined
34445
34614
  };
34446
- logger33.info("Token verified", { gameId, userId });
34615
+ logger34.info("Token verified", { gameId, userId });
34447
34616
  return {
34448
34617
  claims: payload,
34449
34618
  gameId,
@@ -34451,13 +34620,13 @@ class VerifyService {
34451
34620
  };
34452
34621
  }
34453
34622
  }
34454
- var logger33;
34623
+ var logger34;
34455
34624
  var init_verify_service = __esm(() => {
34456
34625
  init_drizzle_orm();
34457
34626
  init_tables_index();
34458
34627
  init_src2();
34459
34628
  init_errors();
34460
- logger33 = log.scope("VerifyService");
34629
+ logger34 = log.scope("VerifyService");
34461
34630
  });
34462
34631
 
34463
34632
  // ../api-core/src/services/factory/standalone.ts
@@ -36043,6 +36212,9 @@ class TimebackCacheManager {
36043
36212
  setEnrollments(studentId, enrollments) {
36044
36213
  this.enrollmentCache.set(studentId, enrollments);
36045
36214
  }
36215
+ clearEnrollments(studentId) {
36216
+ return this.enrollmentCache.delete(studentId);
36217
+ }
36046
36218
  clearAll() {
36047
36219
  this.studentCache.clear();
36048
36220
  this.assessmentLineItemCache.clear();
@@ -36081,6 +36253,41 @@ class MasteryTracker {
36081
36253
  if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
36082
36254
  return;
36083
36255
  }
36256
+ const status = await this.calculateStatus({
36257
+ studentId,
36258
+ courseId,
36259
+ resourceId,
36260
+ additionalMasteredUnits: masteredUnits
36261
+ });
36262
+ if (!status) {
36263
+ return;
36264
+ }
36265
+ return {
36266
+ pctCompleteApp: status.pctCompleteApp,
36267
+ masteryAchieved: status.historicalMasteredUnits < status.masterableUnits && status.isComplete
36268
+ };
36269
+ }
36270
+ async getStatus(input) {
36271
+ const status = await this.calculateStatus({
36272
+ ...input,
36273
+ additionalMasteredUnits: 0
36274
+ });
36275
+ if (!status) {
36276
+ return;
36277
+ }
36278
+ return {
36279
+ masteredUnits: status.masteredUnits,
36280
+ masterableUnits: status.masterableUnits,
36281
+ pctCompleteApp: status.pctCompleteApp,
36282
+ isComplete: status.isComplete
36283
+ };
36284
+ }
36285
+ async calculateStatus({
36286
+ studentId,
36287
+ courseId,
36288
+ resourceId,
36289
+ additionalMasteredUnits
36290
+ }) {
36084
36291
  const masterableUnits = await this.resolveMasterableUnits(resourceId);
36085
36292
  if (!masterableUnits || masterableUnits <= 0) {
36086
36293
  log.warn("[MasteryTracker] No masterableUnits configured for course", {
@@ -36098,11 +36305,16 @@ class MasteryTracker {
36098
36305
  return;
36099
36306
  }
36100
36307
  const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
36101
- const totalMastered = historicalMasteredUnits + masteredUnits;
36308
+ const totalMastered = historicalMasteredUnits + additionalMasteredUnits;
36102
36309
  const rawPct = totalMastered / masterableUnits * 100;
36103
36310
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
36104
- const masteryAchieved = historicalMasteredUnits < masterableUnits && totalMastered >= masterableUnits;
36105
- return { pctCompleteApp, masteryAchieved };
36311
+ return {
36312
+ masteredUnits: totalMastered,
36313
+ masterableUnits,
36314
+ pctCompleteApp,
36315
+ isComplete: totalMastered >= masterableUnits,
36316
+ historicalMasteredUnits
36317
+ };
36106
36318
  }
36107
36319
  async createCompletionEntry(studentId, courseId, classId, appName) {
36108
36320
  const ids = deriveSourcedIds2(courseId);
@@ -36698,6 +36910,7 @@ class TimebackClient {
36698
36910
  progressRecorder;
36699
36911
  sessionRecorder;
36700
36912
  adminEventRecorder;
36913
+ masteryTracker;
36701
36914
  constructor(config2) {
36702
36915
  this.baseUrl = TimebackClient.resolveBaseUrl(config2?.baseUrl);
36703
36916
  this.environment = process.env[ENV_VARS4.environment] === "staging" ? "staging" : "production";
@@ -36715,8 +36928,8 @@ class TimebackClient {
36715
36928
  this.edubridge = createEduBridgeNamespace(this);
36716
36929
  this.cacheManager = new TimebackCacheManager;
36717
36930
  this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
36718
- const masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
36719
- this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, masteryTracker);
36931
+ this.masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
36932
+ this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, this.masteryTracker);
36720
36933
  this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
36721
36934
  this.adminEventRecorder = new AdminEventRecorder(this.studentResolver, this.oneroster, this.caliper, this.environment);
36722
36935
  if (this.credentials) {
@@ -36874,6 +37087,18 @@ class TimebackClient {
36874
37087
  this.cacheManager.setEnrollments(studentId, enrollments);
36875
37088
  return enrollments;
36876
37089
  }
37090
+ invalidateEnrollments(studentId) {
37091
+ this.cacheManager.clearEnrollments(studentId);
37092
+ }
37093
+ async getMasteryStatus(courseId, studentId) {
37094
+ await this._ensureAuthenticated();
37095
+ const ids = deriveSourcedIds2(courseId);
37096
+ return this.masteryTracker.getStatus({
37097
+ studentId,
37098
+ courseId,
37099
+ resourceId: ids.resource
37100
+ });
37101
+ }
36877
37102
  async getStudentXp(studentId, options) {
36878
37103
  await this._ensureAuthenticated();
36879
37104
  const enrollments = await this.edubridge.enrollments.listByUser(studentId);
@@ -39401,7 +39626,7 @@ var humanize = (times) => {
39401
39626
  }
39402
39627
  }
39403
39628
  return `${status}`;
39404
- }, logger34 = (fn = console.log) => {
39629
+ }, logger35 = (fn = console.log) => {
39405
39630
  return async function logger2(c, next) {
39406
39631
  const { method, url } = c.req;
39407
39632
  const path = url.slice(url.indexOf("/", 8));
@@ -39582,7 +39807,7 @@ function createApp(db2, options) {
39582
39807
  const app = new Hono2;
39583
39808
  app.use("*", cors({ origin: "*", credentials: true }));
39584
39809
  if (options.verbose && !options.quiet) {
39585
- app.use("*", logger34());
39810
+ app.use("*", logger35());
39586
39811
  }
39587
39812
  app.use("/api/*", async (c, next) => {
39588
39813
  c.set("db", db2);
@@ -46015,12 +46240,12 @@ var init_session2 = __esm(() => {
46015
46240
  init_utils();
46016
46241
  init_dist5();
46017
46242
  PglitePreparedQuery = class PglitePreparedQuery extends PgPreparedQuery {
46018
- constructor(client, queryString, params, logger35, fields, name3, _isResponseInArrayMode, customResultMapper) {
46243
+ constructor(client, queryString, params, logger36, fields, name3, _isResponseInArrayMode, customResultMapper) {
46019
46244
  super({ sql: queryString, params });
46020
46245
  this.client = client;
46021
46246
  this.queryString = queryString;
46022
46247
  this.params = params;
46023
- this.logger = logger35;
46248
+ this.logger = logger36;
46024
46249
  this.fields = fields;
46025
46250
  this._isResponseInArrayMode = _isResponseInArrayMode;
46026
46251
  this.customResultMapper = customResultMapper;
@@ -46124,11 +46349,11 @@ var init_session2 = __esm(() => {
46124
46349
  // ../../node_modules/.bun/drizzle-orm@0.42.0+15de9685f71e59a4/node_modules/drizzle-orm/pglite/driver.js
46125
46350
  function construct(client, config2 = {}) {
46126
46351
  const dialect2 = new PgDialect({ casing: config2.casing });
46127
- let logger35;
46352
+ let logger36;
46128
46353
  if (config2.logger === true) {
46129
- logger35 = new DefaultLogger;
46354
+ logger36 = new DefaultLogger;
46130
46355
  } else if (config2.logger !== false) {
46131
- logger35 = config2.logger;
46356
+ logger36 = config2.logger;
46132
46357
  }
46133
46358
  let schema2;
46134
46359
  if (config2.schema) {
@@ -46139,7 +46364,7 @@ function construct(client, config2 = {}) {
46139
46364
  tableNamesMap: tablesConfig.tableNamesMap
46140
46365
  };
46141
46366
  }
46142
- const driver = new PgliteDriver(client, dialect2, { logger: logger35 });
46367
+ const driver = new PgliteDriver(client, dialect2, { logger: logger36 });
46143
46368
  const session2 = driver.createSession(schema2);
46144
46369
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
46145
46370
  db2.$client = client;
@@ -92391,8 +92616,8 @@ var init_currencies = __esm(() => {
92391
92616
  });
92392
92617
 
92393
92618
  // src/lib/logging/adapter.ts
92394
- function setLogger(logger35) {
92395
- customLogger = logger35;
92619
+ function setLogger(logger36) {
92620
+ customLogger = logger36;
92396
92621
  }
92397
92622
  function getLogger() {
92398
92623
  if (customLogger) {
@@ -92404,10 +92629,10 @@ function getLogger() {
92404
92629
  error: (msg) => console.error(msg)
92405
92630
  };
92406
92631
  }
92407
- var customLogger, logger35;
92632
+ var customLogger, logger36;
92408
92633
  var init_adapter = __esm(() => {
92409
92634
  init_config();
92410
- logger35 = {
92635
+ logger36 = {
92411
92636
  info: (msg) => {
92412
92637
  if (customLogger || !config.embedded) {
92413
92638
  getLogger().info(msg);
@@ -92493,7 +92718,7 @@ async function seedCoreGames(db2) {
92493
92718
  try {
92494
92719
  await db2.insert(games).values(gameData).onConflictDoNothing();
92495
92720
  } catch (error2) {
92496
- logger35.error(`Error seeding core game '${gameData.slug}': ${error2}`);
92721
+ logger36.error(`Error seeding core game '${gameData.slug}': ${error2}`);
92497
92722
  }
92498
92723
  }
92499
92724
  }
@@ -92539,7 +92764,7 @@ async function seedCurrentProjectGame(db2, project) {
92539
92764
  }
92540
92765
  return newGame;
92541
92766
  } catch (error2) {
92542
- logger35.error(`❌ Error seeding project game: ${error2}`);
92767
+ logger36.error(`❌ Error seeding project game: ${error2}`);
92543
92768
  throw error2;
92544
92769
  }
92545
92770
  }
@@ -93879,7 +94104,7 @@ function isValidAdminAttributionDate(value) {
93879
94104
  const date4 = new Date(Date.UTC(year3, month - 1, day, 12, 0, 0));
93880
94105
  return date4.getUTCFullYear() === year3 && date4.getUTCMonth() + 1 === month && date4.getUTCDate() === day;
93881
94106
  }
93882
- var TIMEBACK_GRADES, TIMEBACK_SUBJECTS5, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, TimebackActivityDataSchema, EndActivityRequestSchema, HeartbeatRequestSchema, PopulateStudentRequestSchema, DerivedPlatformCourseConfigSchema, TimebackBaseConfigSchema, PlatformTimebackSetupRequestSchema, AdminTimebackMutationBaseSchema, AdminAttributionDateSchema, GrantTimebackXpRequestSchema, AdjustTimebackTimeRequestSchema, AdjustTimebackMasteryRequestSchema, ToggleCourseCompletionRequestSchema, EnrollStudentRequestSchema, UnenrollStudentRequestSchema;
94107
+ var TIMEBACK_GRADES, TIMEBACK_SUBJECTS5, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, TimebackActivityDataSchema, EndActivityRequestSchema, AdvanceCourseRequestSchema, HeartbeatRequestSchema, PopulateStudentRequestSchema, DerivedPlatformCourseConfigSchema, TimebackBaseConfigSchema, PlatformTimebackSetupRequestSchema, AdminTimebackMutationBaseSchema, AdminAttributionDateSchema, GrantTimebackXpRequestSchema, AdjustTimebackTimeRequestSchema, AdjustTimebackMasteryRequestSchema, ToggleCourseCompletionRequestSchema, EnrollStudentRequestSchema, UnenrollStudentRequestSchema;
93883
94108
  var init_schemas11 = __esm(() => {
93884
94109
  init_esm();
93885
94110
  TIMEBACK_GRADES = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
@@ -93934,6 +94159,11 @@ var init_schemas11 = __esm(() => {
93934
94159
  masteredUnits: exports_external.number().nonnegative().optional(),
93935
94160
  extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
93936
94161
  });
94162
+ AdvanceCourseRequestSchema = exports_external.object({
94163
+ gameId: exports_external.string().uuid(),
94164
+ studentId: exports_external.string().min(1),
94165
+ subject: TimebackSubjectSchema.optional()
94166
+ });
93937
94167
  HeartbeatRequestSchema = exports_external.object({
93938
94168
  gameId: exports_external.string().uuid(),
93939
94169
  studentId: exports_external.string().min(1),
@@ -94216,7 +94446,7 @@ async function provisionLtiUser(db2, claims) {
94216
94446
  where: eq(users.id, existingAccount.userId)
94217
94447
  });
94218
94448
  if (user) {
94219
- logger36.info("Found user by LTI account", {
94449
+ logger37.info("Found user by LTI account", {
94220
94450
  userId: user.id,
94221
94451
  ltiTimebackId
94222
94452
  });
@@ -94245,13 +94475,13 @@ async function provisionLtiUser(db2, claims) {
94245
94475
  updatedAt: new Date
94246
94476
  }).returning({ id: accounts.id });
94247
94477
  if (!account) {
94248
- logger36.error("LTI account link insert returned no rows", {
94478
+ logger37.error("LTI account link insert returned no rows", {
94249
94479
  userId: existingUser.id,
94250
94480
  ltiTimebackId
94251
94481
  });
94252
94482
  throw new InternalError("Failed to link LTI account");
94253
94483
  }
94254
- logger36.info("Linked LTI account to existing user", {
94484
+ logger37.info("Linked LTI account to existing user", {
94255
94485
  userId: existingUser.id,
94256
94486
  ltiTimebackId
94257
94487
  });
@@ -94271,7 +94501,7 @@ async function provisionLtiUser(db2, claims) {
94271
94501
  updatedAt: new Date
94272
94502
  }).returning();
94273
94503
  if (!insertedUser) {
94274
- logger36.error("LTI user insert returned no rows", { email, ltiTimebackId });
94504
+ logger37.error("LTI user insert returned no rows", { email, ltiTimebackId });
94275
94505
  throw new InternalError("Failed to create user");
94276
94506
  }
94277
94507
  await tx.insert(accounts).values({
@@ -94286,7 +94516,7 @@ async function provisionLtiUser(db2, claims) {
94286
94516
  createdAt: new Date,
94287
94517
  updatedAt: new Date
94288
94518
  });
94289
- logger36.info("Provisioned new user from LTI", {
94519
+ logger37.info("Provisioned new user from LTI", {
94290
94520
  userId: insertedUser.id,
94291
94521
  ltiTimebackId
94292
94522
  });
@@ -94294,7 +94524,7 @@ async function provisionLtiUser(db2, claims) {
94294
94524
  });
94295
94525
  return createdUser;
94296
94526
  }
94297
- var logger36;
94527
+ var logger37;
94298
94528
  var init_lti_provisioning = __esm(() => {
94299
94529
  init_drizzle_orm();
94300
94530
  init_src();
@@ -94302,7 +94532,7 @@ var init_lti_provisioning = __esm(() => {
94302
94532
  init_src2();
94303
94533
  init_errors();
94304
94534
  init_lti_util();
94305
- logger36 = log.scope("LtiProvisioning");
94535
+ logger37 = log.scope("LtiProvisioning");
94306
94536
  });
94307
94537
 
94308
94538
  // ../api-core/src/utils/validation.util.ts
@@ -94350,21 +94580,21 @@ var init_utils11 = __esm(() => {
94350
94580
  });
94351
94581
 
94352
94582
  // ../api-core/src/controllers/achievement.controller.ts
94353
- var logger37, listCurrent, listHistory, postProgress, achievements3;
94583
+ var logger38, listCurrent, listHistory, postProgress, achievements3;
94354
94584
  var init_achievement_controller = __esm(() => {
94355
94585
  init_esm();
94356
94586
  init_schemas_index();
94357
94587
  init_src2();
94358
94588
  init_errors();
94359
94589
  init_utils11();
94360
- logger37 = log.scope("AchievementController");
94590
+ logger38 = log.scope("AchievementController");
94361
94591
  listCurrent = requireNonAnonymous(async (ctx) => {
94362
- logger37.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
94592
+ logger38.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
94363
94593
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
94364
94594
  });
94365
94595
  listHistory = requireNonAnonymous(async (ctx) => {
94366
94596
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
94367
- logger37.debug("Listing achievement history", { userId: ctx.user.id, limit });
94597
+ logger38.debug("Listing achievement history", { userId: ctx.user.id, limit });
94368
94598
  return ctx.services.achievement.listHistory(ctx.user, limit);
94369
94599
  });
94370
94600
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -94375,12 +94605,12 @@ var init_achievement_controller = __esm(() => {
94375
94605
  } catch (error2) {
94376
94606
  if (error2 instanceof exports_external.ZodError) {
94377
94607
  const details = formatZodError(error2);
94378
- logger37.warn("Submit achievement progress validation failed", { details });
94608
+ logger38.warn("Submit achievement progress validation failed", { details });
94379
94609
  throw ApiError.unprocessableEntity("Invalid request body", details);
94380
94610
  }
94381
94611
  throw ApiError.badRequest("Invalid JSON body");
94382
94612
  }
94383
- logger37.debug("Submitting progress", {
94613
+ logger38.debug("Submitting progress", {
94384
94614
  userId: ctx.user.id,
94385
94615
  achievementId: body2.achievementId
94386
94616
  });
@@ -94394,14 +94624,14 @@ var init_achievement_controller = __esm(() => {
94394
94624
  });
94395
94625
 
94396
94626
  // ../api-core/src/controllers/admin.controller.ts
94397
- var logger38, getAllowedOrigins;
94627
+ var logger39, getAllowedOrigins;
94398
94628
  var init_admin_controller = __esm(() => {
94399
94629
  init_src2();
94400
94630
  init_utils11();
94401
- logger38 = log.scope("AdminController");
94631
+ logger39 = log.scope("AdminController");
94402
94632
  getAllowedOrigins = requireAdmin(async (ctx) => {
94403
94633
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
94404
- logger38.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
94634
+ logger39.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
94405
94635
  if (shouldRefresh) {
94406
94636
  await ctx.providers.cache.refreshGameOrigins();
94407
94637
  }
@@ -94416,14 +94646,14 @@ var init_admin_controller = __esm(() => {
94416
94646
  });
94417
94647
 
94418
94648
  // ../api-core/src/controllers/bucket.controller.ts
94419
- var logger39, listFiles, getFile, putFile, deleteFile, initiateUpload;
94649
+ var logger40, listFiles, getFile, putFile, deleteFile, initiateUpload;
94420
94650
  var init_bucket_controller = __esm(() => {
94421
94651
  init_esm();
94422
94652
  init_schemas_index();
94423
94653
  init_src2();
94424
94654
  init_errors();
94425
94655
  init_utils11();
94426
- logger39 = log.scope("BucketController");
94656
+ logger40 = log.scope("BucketController");
94427
94657
  listFiles = requireDeveloper(async (ctx) => {
94428
94658
  const slug2 = ctx.params.slug;
94429
94659
  if (!slug2) {
@@ -94431,7 +94661,7 @@ var init_bucket_controller = __esm(() => {
94431
94661
  }
94432
94662
  const url = ctx.url;
94433
94663
  const prefix2 = url.searchParams.get("prefix") || undefined;
94434
- logger39.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
94664
+ logger40.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
94435
94665
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
94436
94666
  return { files };
94437
94667
  });
@@ -94441,7 +94671,7 @@ var init_bucket_controller = __esm(() => {
94441
94671
  if (!slug2 || !key) {
94442
94672
  throw ApiError.badRequest("Missing game slug or file key");
94443
94673
  }
94444
- logger39.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
94674
+ logger40.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
94445
94675
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
94446
94676
  return new Response(Buffer.from(object.body), {
94447
94677
  status: 200,
@@ -94460,7 +94690,7 @@ var init_bucket_controller = __esm(() => {
94460
94690
  const arrayBuffer = await ctx.request.arrayBuffer();
94461
94691
  const body2 = new Uint8Array(arrayBuffer);
94462
94692
  const contentType = ctx.request.headers.get("content-type") || undefined;
94463
- logger39.debug("Uploading file", {
94693
+ logger40.debug("Uploading file", {
94464
94694
  userId: ctx.user.id,
94465
94695
  slug: slug2,
94466
94696
  key,
@@ -94476,7 +94706,7 @@ var init_bucket_controller = __esm(() => {
94476
94706
  if (!slug2 || !key) {
94477
94707
  throw ApiError.badRequest("Missing game slug or file key");
94478
94708
  }
94479
- logger39.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
94709
+ logger40.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
94480
94710
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
94481
94711
  return { success: true, key };
94482
94712
  });
@@ -94488,12 +94718,12 @@ var init_bucket_controller = __esm(() => {
94488
94718
  } catch (error2) {
94489
94719
  if (error2 instanceof exports_external.ZodError) {
94490
94720
  const details = formatZodError(error2);
94491
- logger39.warn("Initiate upload validation failed", { details });
94721
+ logger40.warn("Initiate upload validation failed", { details });
94492
94722
  throw ApiError.unprocessableEntity("Validation failed", details);
94493
94723
  }
94494
94724
  throw ApiError.badRequest("Invalid JSON body");
94495
94725
  }
94496
- logger39.debug("Initiating multipart upload", {
94726
+ logger40.debug("Initiating multipart upload", {
94497
94727
  userId: ctx.user.id,
94498
94728
  gameId: body2.gameId,
94499
94729
  fileName: body2.fileName
@@ -94510,19 +94740,19 @@ async function listComponents(ctx) {
94510
94740
  if (!isNaN(parsed) && isFinite(parsed)) {
94511
94741
  level = Math.floor(Math.max(0, parsed));
94512
94742
  }
94513
- logger40.debug("Listing components", { level });
94743
+ logger41.debug("Listing components", { level });
94514
94744
  return ctx.services.character.listAvailableComponents(level);
94515
94745
  }
94516
- var logger40, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
94746
+ var logger41, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
94517
94747
  var init_character_controller = __esm(() => {
94518
94748
  init_esm();
94519
94749
  init_schemas_index();
94520
94750
  init_src2();
94521
94751
  init_errors();
94522
94752
  init_utils11();
94523
- logger40 = log.scope("CharacterController");
94753
+ logger41 = log.scope("CharacterController");
94524
94754
  get = requireNonAnonymous(async (ctx) => {
94525
- logger40.debug("Getting character", { userId: ctx.user.id });
94755
+ logger41.debug("Getting character", { userId: ctx.user.id });
94526
94756
  return ctx.services.character.getByUser(ctx.user);
94527
94757
  });
94528
94758
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -94530,7 +94760,7 @@ var init_character_controller = __esm(() => {
94530
94760
  if (!userId) {
94531
94761
  throw ApiError.badRequest("User ID is required in the URL path");
94532
94762
  }
94533
- logger40.debug("Getting character by user ID", { requestedUserId: userId });
94763
+ logger41.debug("Getting character by user ID", { requestedUserId: userId });
94534
94764
  return ctx.services.character.getByUserId(userId);
94535
94765
  });
94536
94766
  create = requireNonAnonymous(async (ctx) => {
@@ -94541,12 +94771,12 @@ var init_character_controller = __esm(() => {
94541
94771
  } catch (error2) {
94542
94772
  if (error2 instanceof exports_external.ZodError) {
94543
94773
  const details = formatZodError(error2);
94544
- logger40.warn("Create character validation failed", { details });
94774
+ logger41.warn("Create character validation failed", { details });
94545
94775
  throw ApiError.unprocessableEntity("Invalid request body", details);
94546
94776
  }
94547
94777
  throw ApiError.badRequest("Invalid JSON body");
94548
94778
  }
94549
- logger40.debug("Creating character", {
94779
+ logger41.debug("Creating character", {
94550
94780
  userId: ctx.user.id,
94551
94781
  bodyComponentId: body2.bodyComponentId,
94552
94782
  hairstyleComponentId: body2.hairstyleComponentId
@@ -94561,12 +94791,12 @@ var init_character_controller = __esm(() => {
94561
94791
  } catch (error2) {
94562
94792
  if (error2 instanceof exports_external.ZodError) {
94563
94793
  const details = formatZodError(error2);
94564
- logger40.warn("Update character validation failed", { details });
94794
+ logger41.warn("Update character validation failed", { details });
94565
94795
  throw ApiError.unprocessableEntity("Invalid request body", details);
94566
94796
  }
94567
94797
  throw ApiError.badRequest("Invalid JSON body");
94568
94798
  }
94569
- logger40.debug("Updating character", {
94799
+ logger41.debug("Updating character", {
94570
94800
  userId: ctx.user.id,
94571
94801
  bodyComponentId: body2.bodyComponentId,
94572
94802
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -94582,12 +94812,12 @@ var init_character_controller = __esm(() => {
94582
94812
  } catch (error2) {
94583
94813
  if (error2 instanceof exports_external.ZodError) {
94584
94814
  const details = formatZodError(error2);
94585
- logger40.warn("Equip accessory validation failed", { details });
94815
+ logger41.warn("Equip accessory validation failed", { details });
94586
94816
  throw ApiError.unprocessableEntity("Invalid request body", details);
94587
94817
  }
94588
94818
  throw ApiError.badRequest("Invalid JSON body");
94589
94819
  }
94590
- logger40.debug("Equipping accessory", {
94820
+ logger41.debug("Equipping accessory", {
94591
94821
  userId: ctx.user.id,
94592
94822
  slot: body2.slot,
94593
94823
  accessoryComponentId: body2.accessoryComponentId
@@ -94599,7 +94829,7 @@ var init_character_controller = __esm(() => {
94599
94829
  if (!slot) {
94600
94830
  throw ApiError.badRequest("Slot is required in the URL path");
94601
94831
  }
94602
- logger40.debug("Removing accessory", { userId: ctx.user.id, slot });
94832
+ logger41.debug("Removing accessory", { userId: ctx.user.id, slot });
94603
94833
  await ctx.services.character.removeAccessory(slot, ctx.user);
94604
94834
  return { success: true };
94605
94835
  });
@@ -94615,7 +94845,7 @@ var init_character_controller = __esm(() => {
94615
94845
  });
94616
94846
 
94617
94847
  // ../api-core/src/controllers/currency.controller.ts
94618
- var logger41, list, getById, create2, update3, remove, currencyController;
94848
+ var logger42, list, getById, create2, update3, remove, currencyController;
94619
94849
  var init_currency_controller = __esm(() => {
94620
94850
  init_esm();
94621
94851
  init_schemas_index();
@@ -94623,9 +94853,9 @@ var init_currency_controller = __esm(() => {
94623
94853
  init_src4();
94624
94854
  init_errors();
94625
94855
  init_utils11();
94626
- logger41 = log.scope("CurrencyController");
94856
+ logger42 = log.scope("CurrencyController");
94627
94857
  list = requireNonAnonymous(async (ctx) => {
94628
- logger41.debug("Listing currencies", { userId: ctx.user.id });
94858
+ logger42.debug("Listing currencies", { userId: ctx.user.id });
94629
94859
  return ctx.services.currency.list();
94630
94860
  });
94631
94861
  getById = requireNonAnonymous(async (ctx) => {
@@ -94636,7 +94866,7 @@ var init_currency_controller = __esm(() => {
94636
94866
  if (!isValidUUID(currencyId)) {
94637
94867
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
94638
94868
  }
94639
- logger41.debug("Getting currency", { userId: ctx.user.id, currencyId });
94869
+ logger42.debug("Getting currency", { userId: ctx.user.id, currencyId });
94640
94870
  return ctx.services.currency.getById(currencyId);
94641
94871
  });
94642
94872
  create2 = requireAdmin(async (ctx) => {
@@ -94647,12 +94877,12 @@ var init_currency_controller = __esm(() => {
94647
94877
  } catch (error2) {
94648
94878
  if (error2 instanceof exports_external.ZodError) {
94649
94879
  const details = formatZodError(error2);
94650
- logger41.warn("Create currency validation failed", { details });
94880
+ logger42.warn("Create currency validation failed", { details });
94651
94881
  throw ApiError.unprocessableEntity("Validation failed", details);
94652
94882
  }
94653
94883
  throw ApiError.badRequest("Invalid JSON body");
94654
94884
  }
94655
- logger41.debug("Creating currency", {
94885
+ logger42.debug("Creating currency", {
94656
94886
  userId: ctx.user.id,
94657
94887
  symbol: body2.symbol,
94658
94888
  itemId: body2.itemId,
@@ -94675,12 +94905,12 @@ var init_currency_controller = __esm(() => {
94675
94905
  } catch (error2) {
94676
94906
  if (error2 instanceof exports_external.ZodError) {
94677
94907
  const details = formatZodError(error2);
94678
- logger41.warn("Update currency validation failed", { details });
94908
+ logger42.warn("Update currency validation failed", { details });
94679
94909
  throw ApiError.unprocessableEntity("Validation failed", details);
94680
94910
  }
94681
94911
  throw ApiError.badRequest("Invalid JSON body");
94682
94912
  }
94683
- logger41.debug("Updating currency", {
94913
+ logger42.debug("Updating currency", {
94684
94914
  userId: ctx.user.id,
94685
94915
  currencyId,
94686
94916
  symbol: body2.symbol,
@@ -94697,7 +94927,7 @@ var init_currency_controller = __esm(() => {
94697
94927
  if (!isValidUUID(currencyId)) {
94698
94928
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
94699
94929
  }
94700
- logger41.debug("Deleting currency", { userId: ctx.user.id, currencyId });
94930
+ logger42.debug("Deleting currency", { userId: ctx.user.id, currencyId });
94701
94931
  await ctx.services.currency.delete(currencyId);
94702
94932
  });
94703
94933
  currencyController = {
@@ -94710,14 +94940,14 @@ var init_currency_controller = __esm(() => {
94710
94940
  });
94711
94941
 
94712
94942
  // ../api-core/src/controllers/database.controller.ts
94713
- var logger42, reset;
94943
+ var logger43, reset;
94714
94944
  var init_database_controller = __esm(() => {
94715
94945
  init_esm();
94716
94946
  init_schemas_index();
94717
94947
  init_src2();
94718
94948
  init_errors();
94719
94949
  init_utils11();
94720
- logger42 = log.scope("DatabaseController");
94950
+ logger43 = log.scope("DatabaseController");
94721
94951
  reset = requireDeveloper(async (ctx) => {
94722
94952
  const slug2 = ctx.params.slug;
94723
94953
  if (!slug2) {
@@ -94730,11 +94960,11 @@ var init_database_controller = __esm(() => {
94730
94960
  } catch (error2) {
94731
94961
  if (error2 instanceof exports_external.ZodError) {
94732
94962
  const details = formatZodError(error2);
94733
- logger42.warn("Database reset validation failed", { details });
94963
+ logger43.warn("Database reset validation failed", { details });
94734
94964
  throw ApiError.unprocessableEntity("Validation failed", details);
94735
94965
  }
94736
94966
  }
94737
- logger42.debug("Resetting database", {
94967
+ logger43.debug("Resetting database", {
94738
94968
  userId: ctx.user.id,
94739
94969
  slug: slug2,
94740
94970
  hasSchema: Boolean(body2.schema)
@@ -94752,7 +94982,7 @@ async function createJob(ctx) {
94752
94982
  let body2;
94753
94983
  try {
94754
94984
  const json4 = await ctx.request.json();
94755
- logger43.debug("Deploy request body", {
94985
+ logger44.debug("Deploy request body", {
94756
94986
  keys: Object.keys(json4 || {}),
94757
94987
  hasUploadToken: Boolean(json4?.uploadToken),
94758
94988
  hasCode: Boolean(json4?.code),
@@ -94762,7 +94992,7 @@ async function createJob(ctx) {
94762
94992
  } catch (error2) {
94763
94993
  if (error2 instanceof exports_external.ZodError) {
94764
94994
  const details = formatZodError(error2);
94765
- logger43.warn("Deploy validation failed", { details });
94995
+ logger44.warn("Deploy validation failed", { details });
94766
94996
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
94767
94997
  }
94768
94998
  throw ApiError.badRequest("Invalid JSON body");
@@ -94783,14 +95013,14 @@ async function getJob(ctx) {
94783
95013
  }
94784
95014
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
94785
95015
  }
94786
- var logger43, deploy;
95016
+ var logger44, deploy;
94787
95017
  var init_deploy_controller = __esm(() => {
94788
95018
  init_esm();
94789
95019
  init_schemas_index();
94790
95020
  init_src2();
94791
95021
  init_errors();
94792
95022
  init_utils11();
94793
- logger43 = log.scope("DeployController");
95023
+ logger44 = log.scope("DeployController");
94794
95024
  deploy = {
94795
95025
  createJob: requireDeveloper(createJob),
94796
95026
  getJob: requireDeveloper(getJob)
@@ -94798,17 +95028,17 @@ var init_deploy_controller = __esm(() => {
94798
95028
  });
94799
95029
 
94800
95030
  // ../api-core/src/controllers/developer.controller.ts
94801
- var logger44, apply, getStatus, developer;
95031
+ var logger45, apply, getStatus, developer;
94802
95032
  var init_developer_controller = __esm(() => {
94803
95033
  init_src2();
94804
95034
  init_utils11();
94805
- logger44 = log.scope("DeveloperController");
95035
+ logger45 = log.scope("DeveloperController");
94806
95036
  apply = requireNonAnonymous(async (ctx) => {
94807
- logger44.debug("Applying for developer status", { userId: ctx.user.id });
95037
+ logger45.debug("Applying for developer status", { userId: ctx.user.id });
94808
95038
  await ctx.services.developer.apply(ctx.user);
94809
95039
  });
94810
95040
  getStatus = requireNonAnonymous(async (ctx) => {
94811
- logger44.debug("Getting developer status", { userId: ctx.user.id });
95041
+ logger45.debug("Getting developer status", { userId: ctx.user.id });
94812
95042
  const status = await ctx.services.developer.getStatus(ctx.user.id);
94813
95043
  return { status };
94814
95044
  });
@@ -94819,7 +95049,7 @@ var init_developer_controller = __esm(() => {
94819
95049
  });
94820
95050
 
94821
95051
  // ../api-core/src/controllers/domain.controller.ts
94822
- var logger45, add, list2, getStatus2, remove2, domains2;
95052
+ var logger46, add, list2, getStatus2, remove2, domains2;
94823
95053
  var init_domain_controller = __esm(() => {
94824
95054
  init_esm();
94825
95055
  init_schemas_index();
@@ -94827,7 +95057,7 @@ var init_domain_controller = __esm(() => {
94827
95057
  init_config2();
94828
95058
  init_errors();
94829
95059
  init_utils11();
94830
- logger45 = log.scope("DomainController");
95060
+ logger46 = log.scope("DomainController");
94831
95061
  add = requireDeveloper(async (ctx) => {
94832
95062
  const slug2 = ctx.params.slug;
94833
95063
  if (!slug2) {
@@ -94840,13 +95070,13 @@ var init_domain_controller = __esm(() => {
94840
95070
  } catch (error2) {
94841
95071
  if (error2 instanceof exports_external.ZodError) {
94842
95072
  const details = formatZodError(error2);
94843
- logger45.warn("Add domain validation failed", { details });
95073
+ logger46.warn("Add domain validation failed", { details });
94844
95074
  throw ApiError.unprocessableEntity("Validation failed", details);
94845
95075
  }
94846
95076
  throw ApiError.badRequest("Invalid JSON body");
94847
95077
  }
94848
95078
  const environment = getPlatformEnvironment(ctx.config);
94849
- logger45.debug("Adding domain", {
95079
+ logger46.debug("Adding domain", {
94850
95080
  userId: ctx.user.id,
94851
95081
  slug: slug2,
94852
95082
  hostname: body2.hostname,
@@ -94860,7 +95090,7 @@ var init_domain_controller = __esm(() => {
94860
95090
  throw ApiError.badRequest("Missing game slug");
94861
95091
  }
94862
95092
  const environment = getPlatformEnvironment(ctx.config);
94863
- logger45.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
95093
+ logger46.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
94864
95094
  const domains2 = await ctx.services.domain.list(slug2, environment, ctx.user);
94865
95095
  return { domains: domains2 };
94866
95096
  });
@@ -94875,7 +95105,7 @@ var init_domain_controller = __esm(() => {
94875
95105
  }
94876
95106
  const refresh = ctx.url.searchParams.get("refresh") === "true";
94877
95107
  const environment = getPlatformEnvironment(ctx.config);
94878
- logger45.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
95108
+ logger46.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
94879
95109
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
94880
95110
  });
94881
95111
  remove2 = requireDeveloper(async (ctx) => {
@@ -94888,7 +95118,7 @@ var init_domain_controller = __esm(() => {
94888
95118
  throw ApiError.badRequest("Missing hostname");
94889
95119
  }
94890
95120
  const environment = getPlatformEnvironment(ctx.config);
94891
- logger45.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
95121
+ logger46.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
94892
95122
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
94893
95123
  });
94894
95124
  domains2 = {
@@ -94900,7 +95130,7 @@ var init_domain_controller = __esm(() => {
94900
95130
  });
94901
95131
 
94902
95132
  // ../api-core/src/controllers/game.controller.ts
94903
- var logger46, list3, listManageable, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
95133
+ var logger47, list3, listManageable, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
94904
95134
  var init_game_controller = __esm(() => {
94905
95135
  init_esm();
94906
95136
  init_schemas_index();
@@ -94908,17 +95138,17 @@ var init_game_controller = __esm(() => {
94908
95138
  init_src4();
94909
95139
  init_errors();
94910
95140
  init_utils11();
94911
- logger46 = log.scope("GameController");
95141
+ logger47 = log.scope("GameController");
94912
95142
  list3 = requireNonAnonymous(async (ctx) => {
94913
- logger46.debug("Listing games", { userId: ctx.user.id });
95143
+ logger47.debug("Listing games", { userId: ctx.user.id });
94914
95144
  return ctx.services.game.list(ctx.user);
94915
95145
  });
94916
95146
  listManageable = requireNonAnonymous(async (ctx) => {
94917
- logger46.debug("Listing manageable games", { userId: ctx.user.id });
95147
+ logger47.debug("Listing manageable games", { userId: ctx.user.id });
94918
95148
  return ctx.services.game.listManageable(ctx.user);
94919
95149
  });
94920
95150
  getSubjects = requireNonAnonymous(async (ctx) => {
94921
- logger46.debug("Getting game subjects", { userId: ctx.user.id });
95151
+ logger47.debug("Getting game subjects", { userId: ctx.user.id });
94922
95152
  return ctx.services.game.getSubjects();
94923
95153
  });
94924
95154
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -94929,7 +95159,7 @@ var init_game_controller = __esm(() => {
94929
95159
  if (!isValidUUID(gameId)) {
94930
95160
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
94931
95161
  }
94932
- logger46.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
95162
+ logger47.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
94933
95163
  return ctx.services.game.getById(gameId, ctx.user);
94934
95164
  });
94935
95165
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -94937,7 +95167,7 @@ var init_game_controller = __esm(() => {
94937
95167
  if (!slug2) {
94938
95168
  throw ApiError.badRequest("Missing game slug");
94939
95169
  }
94940
- logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
95170
+ logger47.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
94941
95171
  return ctx.services.game.getBySlug(slug2, ctx.user);
94942
95172
  });
94943
95173
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -94948,7 +95178,7 @@ var init_game_controller = __esm(() => {
94948
95178
  if (!isValidUUID(gameId)) {
94949
95179
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
94950
95180
  }
94951
- logger46.debug("Getting game manifest by ID", {
95181
+ logger47.debug("Getting game manifest by ID", {
94952
95182
  userId: ctx.user.id,
94953
95183
  gameId,
94954
95184
  launchId: ctx.launchId
@@ -94967,12 +95197,12 @@ var init_game_controller = __esm(() => {
94967
95197
  } catch (error2) {
94968
95198
  if (error2 instanceof exports_external.ZodError) {
94969
95199
  const details = formatZodError(error2);
94970
- logger46.warn("Upsert game validation failed", { details });
95200
+ logger47.warn("Upsert game validation failed", { details });
94971
95201
  throw ApiError.unprocessableEntity("Validation failed", details);
94972
95202
  }
94973
95203
  throw ApiError.badRequest("Invalid JSON body");
94974
95204
  }
94975
- logger46.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
95205
+ logger47.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
94976
95206
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
94977
95207
  });
94978
95208
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -94983,7 +95213,7 @@ var init_game_controller = __esm(() => {
94983
95213
  if (!isValidUUID(gameId)) {
94984
95214
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
94985
95215
  }
94986
- logger46.debug("Deleting game", { userId: ctx.user.id, gameId });
95216
+ logger47.debug("Deleting game", { userId: ctx.user.id, gameId });
94987
95217
  await ctx.services.game.delete(gameId, ctx.user);
94988
95218
  });
94989
95219
  games2 = {
@@ -94999,16 +95229,16 @@ var init_game_controller = __esm(() => {
94999
95229
  });
95000
95230
 
95001
95231
  // ../api-core/src/controllers/inventory.controller.ts
95002
- var logger47, list4, addItem, removeItem, inventory;
95232
+ var logger48, list4, addItem, removeItem, inventory;
95003
95233
  var init_inventory_controller = __esm(() => {
95004
95234
  init_esm();
95005
95235
  init_schemas_index();
95006
95236
  init_src2();
95007
95237
  init_errors();
95008
95238
  init_utils11();
95009
- logger47 = log.scope("InventoryController");
95239
+ logger48 = log.scope("InventoryController");
95010
95240
  list4 = requireNonAnonymous(async (ctx) => {
95011
- logger47.debug("Listing inventory", { userId: ctx.user.id });
95241
+ logger48.debug("Listing inventory", { userId: ctx.user.id });
95012
95242
  return ctx.services.inventory.list(ctx.user);
95013
95243
  });
95014
95244
  addItem = requireNonAnonymous(async (ctx) => {
@@ -95019,12 +95249,12 @@ var init_inventory_controller = __esm(() => {
95019
95249
  } catch (error2) {
95020
95250
  if (error2 instanceof exports_external.ZodError) {
95021
95251
  const details = formatZodError(error2);
95022
- logger47.warn("Add inventory item validation failed", { details });
95252
+ logger48.warn("Add inventory item validation failed", { details });
95023
95253
  throw ApiError.unprocessableEntity("Invalid request body", details);
95024
95254
  }
95025
95255
  throw ApiError.badRequest("Invalid JSON body");
95026
95256
  }
95027
- logger47.debug("Adding item", {
95257
+ logger48.debug("Adding item", {
95028
95258
  userId: ctx.user.id,
95029
95259
  itemId: body2.itemId,
95030
95260
  qty: body2.qty
@@ -95039,12 +95269,12 @@ var init_inventory_controller = __esm(() => {
95039
95269
  } catch (error2) {
95040
95270
  if (error2 instanceof exports_external.ZodError) {
95041
95271
  const details = formatZodError(error2);
95042
- logger47.warn("Remove inventory item validation failed", { details });
95272
+ logger48.warn("Remove inventory item validation failed", { details });
95043
95273
  throw ApiError.unprocessableEntity("Invalid request body", details);
95044
95274
  }
95045
95275
  throw ApiError.badRequest("Invalid JSON body");
95046
95276
  }
95047
- logger47.debug("Removing item", {
95277
+ logger48.debug("Removing item", {
95048
95278
  userId: ctx.user.id,
95049
95279
  itemId: body2.itemId,
95050
95280
  qty: body2.qty
@@ -95059,7 +95289,7 @@ var init_inventory_controller = __esm(() => {
95059
95289
  });
95060
95290
 
95061
95291
  // ../api-core/src/controllers/item.controller.ts
95062
- var logger48, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
95292
+ var logger49, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
95063
95293
  var init_item_controller = __esm(() => {
95064
95294
  init_esm();
95065
95295
  init_schemas_index();
@@ -95067,10 +95297,10 @@ var init_item_controller = __esm(() => {
95067
95297
  init_src4();
95068
95298
  init_errors();
95069
95299
  init_utils11();
95070
- logger48 = log.scope("ItemController");
95300
+ logger49 = log.scope("ItemController");
95071
95301
  list5 = requireNonAnonymous(async (ctx) => {
95072
95302
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
95073
- logger48.debug("Listing items", { userId: ctx.user.id, gameId });
95303
+ logger49.debug("Listing items", { userId: ctx.user.id, gameId });
95074
95304
  return ctx.services.item.list(gameId);
95075
95305
  });
95076
95306
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -95081,7 +95311,7 @@ var init_item_controller = __esm(() => {
95081
95311
  if (!isValidUUID(itemId)) {
95082
95312
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
95083
95313
  }
95084
- logger48.debug("Getting item", { userId: ctx.user.id, itemId });
95314
+ logger49.debug("Getting item", { userId: ctx.user.id, itemId });
95085
95315
  return ctx.services.item.getById(itemId);
95086
95316
  });
95087
95317
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -95093,7 +95323,7 @@ var init_item_controller = __esm(() => {
95093
95323
  if (gameId && !isValidUUID(gameId)) {
95094
95324
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
95095
95325
  }
95096
- logger48.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
95326
+ logger49.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
95097
95327
  return ctx.services.item.resolveBySlug(slug2, gameId);
95098
95328
  });
95099
95329
  create3 = requireRole(["admin"], async (ctx) => {
@@ -95104,12 +95334,12 @@ var init_item_controller = __esm(() => {
95104
95334
  } catch (error2) {
95105
95335
  if (error2 instanceof exports_external.ZodError) {
95106
95336
  const details = formatZodError(error2);
95107
- logger48.warn("Create item validation failed", { details });
95337
+ logger49.warn("Create item validation failed", { details });
95108
95338
  throw ApiError.unprocessableEntity("Validation failed", details);
95109
95339
  }
95110
95340
  throw ApiError.badRequest("Invalid JSON body");
95111
95341
  }
95112
- logger48.debug("Creating item", {
95342
+ logger49.debug("Creating item", {
95113
95343
  userId: ctx.user.id,
95114
95344
  slug: body2.slug,
95115
95345
  displayName: body2.displayName
@@ -95131,7 +95361,7 @@ var init_item_controller = __esm(() => {
95131
95361
  } catch (error2) {
95132
95362
  if (error2 instanceof exports_external.ZodError) {
95133
95363
  const details = formatZodError(error2);
95134
- logger48.warn("Update item validation failed", { details });
95364
+ logger49.warn("Update item validation failed", { details });
95135
95365
  throw ApiError.unprocessableEntity("Validation failed", details);
95136
95366
  }
95137
95367
  throw ApiError.badRequest("Invalid JSON body");
@@ -95139,7 +95369,7 @@ var init_item_controller = __esm(() => {
95139
95369
  if (Object.keys(body2).length === 0) {
95140
95370
  throw ApiError.badRequest("No update data provided");
95141
95371
  }
95142
- logger48.debug("Updating item", {
95372
+ logger49.debug("Updating item", {
95143
95373
  userId: ctx.user.id,
95144
95374
  itemId,
95145
95375
  slug: body2.slug,
@@ -95156,7 +95386,7 @@ var init_item_controller = __esm(() => {
95156
95386
  if (!isValidUUID(itemId)) {
95157
95387
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
95158
95388
  }
95159
- logger48.debug("Deleting item", { userId: ctx.user.id, itemId });
95389
+ logger49.debug("Deleting item", { userId: ctx.user.id, itemId });
95160
95390
  await ctx.services.item.delete(itemId);
95161
95391
  });
95162
95392
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -95167,7 +95397,7 @@ var init_item_controller = __esm(() => {
95167
95397
  if (!isValidUUID(gameId)) {
95168
95398
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
95169
95399
  }
95170
- logger48.debug("Listing game items", { userId: ctx.user.id, gameId });
95400
+ logger49.debug("Listing game items", { userId: ctx.user.id, gameId });
95171
95401
  return ctx.services.item.listByGame(gameId);
95172
95402
  });
95173
95403
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -95185,12 +95415,12 @@ var init_item_controller = __esm(() => {
95185
95415
  } catch (error2) {
95186
95416
  if (error2 instanceof exports_external.ZodError) {
95187
95417
  const details = formatZodError(error2);
95188
- logger48.warn("Create game item validation failed", { details });
95418
+ logger49.warn("Create game item validation failed", { details });
95189
95419
  throw ApiError.unprocessableEntity("Validation failed", details);
95190
95420
  }
95191
95421
  throw ApiError.badRequest("Invalid JSON body");
95192
95422
  }
95193
- logger48.debug("Creating game item", {
95423
+ logger49.debug("Creating game item", {
95194
95424
  userId: ctx.user.id,
95195
95425
  gameId,
95196
95426
  slug: body2.slug,
@@ -95217,7 +95447,7 @@ var init_item_controller = __esm(() => {
95217
95447
  } catch (error2) {
95218
95448
  if (error2 instanceof exports_external.ZodError) {
95219
95449
  const details = formatZodError(error2);
95220
- logger48.warn("Update game item validation failed", { details });
95450
+ logger49.warn("Update game item validation failed", { details });
95221
95451
  throw ApiError.unprocessableEntity("Validation failed", details);
95222
95452
  }
95223
95453
  throw ApiError.badRequest("Invalid JSON body");
@@ -95225,7 +95455,7 @@ var init_item_controller = __esm(() => {
95225
95455
  if (Object.keys(body2).length === 0) {
95226
95456
  throw ApiError.badRequest("No update data provided");
95227
95457
  }
95228
- logger48.debug("Updating game item", {
95458
+ logger49.debug("Updating game item", {
95229
95459
  userId: ctx.user.id,
95230
95460
  gameId,
95231
95461
  itemId,
@@ -95247,7 +95477,7 @@ var init_item_controller = __esm(() => {
95247
95477
  if (!isValidUUID(itemId)) {
95248
95478
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
95249
95479
  }
95250
- logger48.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
95480
+ logger49.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
95251
95481
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
95252
95482
  });
95253
95483
  items2 = {
@@ -95265,14 +95495,14 @@ var init_item_controller = __esm(() => {
95265
95495
  });
95266
95496
 
95267
95497
  // ../api-core/src/controllers/kv.controller.ts
95268
- var logger49, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
95498
+ var logger50, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
95269
95499
  var init_kv_controller = __esm(() => {
95270
95500
  init_esm();
95271
95501
  init_schemas_index();
95272
95502
  init_src2();
95273
95503
  init_errors();
95274
95504
  init_utils11();
95275
- logger49 = log.scope("KVController");
95505
+ logger50 = log.scope("KVController");
95276
95506
  listKeys = requireDeveloper(async (ctx) => {
95277
95507
  const slug2 = ctx.params.slug;
95278
95508
  if (!slug2) {
@@ -95280,7 +95510,7 @@ var init_kv_controller = __esm(() => {
95280
95510
  }
95281
95511
  const url = ctx.url;
95282
95512
  const prefix2 = url.searchParams.get("prefix") || undefined;
95283
- logger49.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
95513
+ logger50.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
95284
95514
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
95285
95515
  return { keys };
95286
95516
  });
@@ -95289,7 +95519,7 @@ var init_kv_controller = __esm(() => {
95289
95519
  if (!slug2) {
95290
95520
  throw ApiError.badRequest("Missing game slug");
95291
95521
  }
95292
- logger49.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
95522
+ logger50.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
95293
95523
  return ctx.services.kv.getStats(slug2, ctx.user);
95294
95524
  });
95295
95525
  seed = requireDeveloper(async (ctx) => {
@@ -95304,12 +95534,12 @@ var init_kv_controller = __esm(() => {
95304
95534
  } catch (error2) {
95305
95535
  if (error2 instanceof exports_external.ZodError) {
95306
95536
  const details = formatZodError(error2);
95307
- logger49.warn("Seed validation failed", { details });
95537
+ logger50.warn("Seed validation failed", { details });
95308
95538
  throw ApiError.unprocessableEntity("Validation failed", details);
95309
95539
  }
95310
95540
  throw ApiError.badRequest("Invalid JSON body");
95311
95541
  }
95312
- logger49.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
95542
+ logger50.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
95313
95543
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
95314
95544
  return { success: true, count: body2.entries.length };
95315
95545
  });
@@ -95319,7 +95549,7 @@ var init_kv_controller = __esm(() => {
95319
95549
  if (!slug2 || !key) {
95320
95550
  throw ApiError.badRequest("Missing game slug or key");
95321
95551
  }
95322
- logger49.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
95552
+ logger50.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
95323
95553
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
95324
95554
  return { key, value };
95325
95555
  });
@@ -95333,7 +95563,7 @@ var init_kv_controller = __esm(() => {
95333
95563
  if (!value) {
95334
95564
  throw ApiError.badRequest("Missing value in request body");
95335
95565
  }
95336
- logger49.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
95566
+ logger50.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
95337
95567
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
95338
95568
  return { success: true, key };
95339
95569
  });
@@ -95343,7 +95573,7 @@ var init_kv_controller = __esm(() => {
95343
95573
  if (!slug2 || !key) {
95344
95574
  throw ApiError.badRequest("Missing game slug or key");
95345
95575
  }
95346
- logger49.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
95576
+ logger50.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
95347
95577
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
95348
95578
  return { success: true, key };
95349
95579
  });
@@ -95353,7 +95583,7 @@ var init_kv_controller = __esm(() => {
95353
95583
  if (!slug2 || !key) {
95354
95584
  throw ApiError.badRequest("Missing game slug or key");
95355
95585
  }
95356
- logger49.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
95586
+ logger50.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
95357
95587
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
95358
95588
  return { key, metadata: metadata2 };
95359
95589
  });
@@ -95362,21 +95592,21 @@ var init_kv_controller = __esm(() => {
95362
95592
  if (!slug2) {
95363
95593
  throw ApiError.badRequest("Missing game slug");
95364
95594
  }
95365
- logger49.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
95595
+ logger50.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
95366
95596
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
95367
95597
  return { success: true, deleted };
95368
95598
  });
95369
95599
  });
95370
95600
 
95371
95601
  // ../api-core/src/controllers/leaderboard.controller.ts
95372
- var logger50, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
95602
+ var logger51, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
95373
95603
  var init_leaderboard_controller = __esm(() => {
95374
95604
  init_esm();
95375
95605
  init_schemas_index();
95376
95606
  init_src2();
95377
95607
  init_errors();
95378
95608
  init_utils11();
95379
- logger50 = log.scope("LeaderboardController");
95609
+ logger51 = log.scope("LeaderboardController");
95380
95610
  submitScore = requireAuth(async (ctx) => {
95381
95611
  const gameId = ctx.params.gameId;
95382
95612
  if (!gameId) {
@@ -95389,12 +95619,12 @@ var init_leaderboard_controller = __esm(() => {
95389
95619
  } catch (error2) {
95390
95620
  if (error2 instanceof exports_external.ZodError) {
95391
95621
  const details = formatZodError(error2);
95392
- logger50.warn("Submit score validation failed", { details });
95622
+ logger51.warn("Submit score validation failed", { details });
95393
95623
  throw ApiError.unprocessableEntity("Validation failed", details);
95394
95624
  }
95395
95625
  throw ApiError.badRequest("Invalid JSON body");
95396
95626
  }
95397
- logger50.debug("Submitting score", {
95627
+ logger51.debug("Submitting score", {
95398
95628
  userId: ctx.user.id,
95399
95629
  gameId,
95400
95630
  score: body2.score
@@ -95417,12 +95647,12 @@ var init_leaderboard_controller = __esm(() => {
95417
95647
  } catch (error2) {
95418
95648
  if (error2 instanceof exports_external.ZodError) {
95419
95649
  const details = formatZodError(error2);
95420
- logger50.warn("Get global leaderboard query validation failed", { details });
95650
+ logger51.warn("Get global leaderboard query validation failed", { details });
95421
95651
  throw ApiError.badRequest("Invalid query parameters", details);
95422
95652
  }
95423
95653
  throw ApiError.badRequest("Invalid query parameters");
95424
95654
  }
95425
- logger50.debug("Getting global leaderboard", {
95655
+ logger51.debug("Getting global leaderboard", {
95426
95656
  userId: ctx.user.id,
95427
95657
  gameId,
95428
95658
  ...query
@@ -95445,12 +95675,12 @@ var init_leaderboard_controller = __esm(() => {
95445
95675
  } catch (error2) {
95446
95676
  if (error2 instanceof exports_external.ZodError) {
95447
95677
  const details = formatZodError(error2);
95448
- logger50.warn("Get leaderboard query validation failed", { details });
95678
+ logger51.warn("Get leaderboard query validation failed", { details });
95449
95679
  throw ApiError.badRequest("Invalid query parameters", details);
95450
95680
  }
95451
95681
  throw ApiError.badRequest("Invalid query parameters");
95452
95682
  }
95453
- logger50.debug("Getting leaderboard", {
95683
+ logger51.debug("Getting leaderboard", {
95454
95684
  userId: ctx.user.id,
95455
95685
  gameId,
95456
95686
  ...query
@@ -95462,7 +95692,7 @@ var init_leaderboard_controller = __esm(() => {
95462
95692
  if (!gameId || !userId) {
95463
95693
  throw ApiError.badRequest("Game ID and User ID are required");
95464
95694
  }
95465
- logger50.debug("Getting user rank", {
95695
+ logger51.debug("Getting user rank", {
95466
95696
  requesterId: ctx.user.id,
95467
95697
  gameId,
95468
95698
  targetUserId: userId
@@ -95477,7 +95707,7 @@ var init_leaderboard_controller = __esm(() => {
95477
95707
  const url = ctx.url;
95478
95708
  const limit = Math.min(Number(url.searchParams.get("limit") || "50"), 100);
95479
95709
  const gameId = url.searchParams.get("gameId") || undefined;
95480
- logger50.debug("Getting user all scores", {
95710
+ logger51.debug("Getting user all scores", {
95481
95711
  requesterId: ctx.user.id,
95482
95712
  targetUserId: userId,
95483
95713
  gameId,
@@ -95492,7 +95722,7 @@ var init_leaderboard_controller = __esm(() => {
95492
95722
  }
95493
95723
  const url = ctx.url;
95494
95724
  const limit = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
95495
- logger50.debug("Getting user scores", {
95725
+ logger51.debug("Getting user scores", {
95496
95726
  requesterId: ctx.user.id,
95497
95727
  gameId,
95498
95728
  targetUserId: userId,
@@ -95512,7 +95742,7 @@ var init_leaderboard_controller = __esm(() => {
95512
95742
 
95513
95743
  // ../api-core/src/controllers/level.controller.ts
95514
95744
  async function listConfigs(ctx) {
95515
- logger51.debug("Listing level configs");
95745
+ logger52.debug("Listing level configs");
95516
95746
  return ctx.services.level.listConfigs();
95517
95747
  }
95518
95748
  async function getConfig(ctx) {
@@ -95524,21 +95754,21 @@ async function getConfig(ctx) {
95524
95754
  if (isNaN(level) || level < 1) {
95525
95755
  throw ApiError.badRequest("Level must be a positive integer");
95526
95756
  }
95527
- logger51.debug("Getting level config", { level });
95757
+ logger52.debug("Getting level config", { level });
95528
95758
  return ctx.services.level.getConfig(level);
95529
95759
  }
95530
- var logger51, getByUser, getProgress, levels;
95760
+ var logger52, getByUser, getProgress, levels;
95531
95761
  var init_level_controller = __esm(() => {
95532
95762
  init_src2();
95533
95763
  init_errors();
95534
95764
  init_utils11();
95535
- logger51 = log.scope("LevelController");
95765
+ logger52 = log.scope("LevelController");
95536
95766
  getByUser = requireNonAnonymous(async (ctx) => {
95537
- logger51.debug("Getting user level", { userId: ctx.user.id });
95767
+ logger52.debug("Getting user level", { userId: ctx.user.id });
95538
95768
  return ctx.services.level.getByUser(ctx.user);
95539
95769
  });
95540
95770
  getProgress = requireNonAnonymous(async (ctx) => {
95541
- logger51.debug("Getting level progress", { userId: ctx.user.id });
95771
+ logger52.debug("Getting level progress", { userId: ctx.user.id });
95542
95772
  return ctx.services.level.getProgress(ctx.user);
95543
95773
  });
95544
95774
  levels = {
@@ -95550,12 +95780,12 @@ var init_level_controller = __esm(() => {
95550
95780
  });
95551
95781
 
95552
95782
  // ../api-core/src/controllers/logs.controller.ts
95553
- var logger52, generateToken, logs;
95783
+ var logger53, generateToken, logs;
95554
95784
  var init_logs_controller = __esm(() => {
95555
95785
  init_src2();
95556
95786
  init_errors();
95557
95787
  init_utils11();
95558
- logger52 = log.scope("LogsController");
95788
+ logger53 = log.scope("LogsController");
95559
95789
  generateToken = requireDeveloper(async (ctx) => {
95560
95790
  const slug2 = ctx.params.slug;
95561
95791
  if (!slug2) {
@@ -95574,7 +95804,7 @@ var init_logs_controller = __esm(() => {
95574
95804
  }
95575
95805
  throw ApiError.badRequest("Invalid JSON body");
95576
95806
  }
95577
- logger52.debug("Generating log stream token", {
95807
+ logger53.debug("Generating log stream token", {
95578
95808
  userId: ctx.user.id,
95579
95809
  slug: slug2,
95580
95810
  environment: body2.environment
@@ -95587,13 +95817,13 @@ var init_logs_controller = __esm(() => {
95587
95817
  });
95588
95818
 
95589
95819
  // ../api-core/src/controllers/lti.controller.ts
95590
- var logger53, getStatus3, lti;
95820
+ var logger54, getStatus3, lti;
95591
95821
  var init_lti_controller = __esm(() => {
95592
95822
  init_src2();
95593
95823
  init_utils11();
95594
- logger53 = log.scope("LtiController");
95824
+ logger54 = log.scope("LtiController");
95595
95825
  getStatus3 = requireNonAnonymous(async (ctx) => {
95596
- logger53.debug("Getting status", { userId: ctx.user.id });
95826
+ logger54.debug("Getting status", { userId: ctx.user.id });
95597
95827
  return ctx.services.lti.getStatus(ctx.user);
95598
95828
  });
95599
95829
  lti = {
@@ -95602,7 +95832,7 @@ var init_lti_controller = __esm(() => {
95602
95832
  });
95603
95833
 
95604
95834
  // ../api-core/src/controllers/map.controller.ts
95605
- var logger54, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
95835
+ var logger55, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
95606
95836
  var init_map_controller = __esm(() => {
95607
95837
  init_esm();
95608
95838
  init_schemas_index();
@@ -95610,13 +95840,13 @@ var init_map_controller = __esm(() => {
95610
95840
  init_src4();
95611
95841
  init_errors();
95612
95842
  init_utils11();
95613
- logger54 = log.scope("MapController");
95843
+ logger55 = log.scope("MapController");
95614
95844
  getByIdentifier = requireNonAnonymous(async (ctx) => {
95615
95845
  const identifier = ctx.params.identifier;
95616
95846
  if (!identifier) {
95617
95847
  throw ApiError.badRequest("Missing map identifier");
95618
95848
  }
95619
- logger54.debug("Getting map", { userId: ctx.user.id, identifier });
95849
+ logger55.debug("Getting map", { userId: ctx.user.id, identifier });
95620
95850
  return ctx.services.map.getByIdentifier(identifier);
95621
95851
  });
95622
95852
  getElements = requireNonAnonymous(async (ctx) => {
@@ -95627,7 +95857,7 @@ var init_map_controller = __esm(() => {
95627
95857
  if (!isValidUUID(mapId)) {
95628
95858
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
95629
95859
  }
95630
- logger54.debug("Getting map elements", { userId: ctx.user.id, mapId });
95860
+ logger55.debug("Getting map elements", { userId: ctx.user.id, mapId });
95631
95861
  return ctx.services.map.getElements(mapId);
95632
95862
  });
95633
95863
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -95638,7 +95868,7 @@ var init_map_controller = __esm(() => {
95638
95868
  if (!isValidUUID(mapId)) {
95639
95869
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
95640
95870
  }
95641
- logger54.debug("Getting map objects", { userId: ctx.user.id, mapId });
95871
+ logger55.debug("Getting map objects", { userId: ctx.user.id, mapId });
95642
95872
  return ctx.services.map.getObjects(mapId, ctx.user.id);
95643
95873
  });
95644
95874
  createObject = requireNonAnonymous(async (ctx) => {
@@ -95660,12 +95890,12 @@ var init_map_controller = __esm(() => {
95660
95890
  } catch (error2) {
95661
95891
  if (error2 instanceof exports_external.ZodError) {
95662
95892
  const details = formatZodError(error2);
95663
- logger54.warn("Create map object validation failed", { details });
95893
+ logger55.warn("Create map object validation failed", { details });
95664
95894
  throw ApiError.unprocessableEntity("Validation failed", details);
95665
95895
  }
95666
95896
  throw ApiError.badRequest("Invalid JSON body");
95667
95897
  }
95668
- logger54.debug("Creating map object", {
95898
+ logger55.debug("Creating map object", {
95669
95899
  userId: ctx.user.id,
95670
95900
  mapId,
95671
95901
  itemId: body2.itemId,
@@ -95689,7 +95919,7 @@ var init_map_controller = __esm(() => {
95689
95919
  if (!isValidUUID(objectId)) {
95690
95920
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
95691
95921
  }
95692
- logger54.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
95922
+ logger55.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
95693
95923
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
95694
95924
  });
95695
95925
  maps2 = {
@@ -95702,14 +95932,14 @@ var init_map_controller = __esm(() => {
95702
95932
  });
95703
95933
 
95704
95934
  // ../api-core/src/controllers/notification.controller.ts
95705
- var logger55, list6, updateStatus, getStats2, create4, deliver, notifications2;
95935
+ var logger56, list6, updateStatus, getStats2, create4, deliver, notifications2;
95706
95936
  var init_notification_controller = __esm(() => {
95707
95937
  init_esm();
95708
95938
  init_schemas_index();
95709
95939
  init_src2();
95710
95940
  init_errors();
95711
95941
  init_utils11();
95712
- logger55 = log.scope("NotificationController");
95942
+ logger56 = log.scope("NotificationController");
95713
95943
  list6 = requireNonAnonymous(async (ctx) => {
95714
95944
  const query = {
95715
95945
  status: ctx.url.searchParams.get("status") || undefined,
@@ -95720,10 +95950,10 @@ var init_notification_controller = __esm(() => {
95720
95950
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
95721
95951
  if (!result.success) {
95722
95952
  const details = formatZodError(result.error);
95723
- logger55.warn("List notifications query validation failed", { details });
95953
+ logger56.warn("List notifications query validation failed", { details });
95724
95954
  throw ApiError.badRequest("Invalid query parameters", details);
95725
95955
  }
95726
- logger55.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
95956
+ logger56.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
95727
95957
  return ctx.services.notification.list(ctx.user, result.data);
95728
95958
  });
95729
95959
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -95738,12 +95968,12 @@ var init_notification_controller = __esm(() => {
95738
95968
  } catch (error2) {
95739
95969
  if (error2 instanceof exports_external.ZodError) {
95740
95970
  const details = formatZodError(error2);
95741
- logger55.warn("Update notification status validation failed", { details });
95971
+ logger56.warn("Update notification status validation failed", { details });
95742
95972
  throw ApiError.unprocessableEntity("Invalid request body", details);
95743
95973
  }
95744
95974
  throw ApiError.badRequest("Invalid JSON body");
95745
95975
  }
95746
- logger55.debug("Updating status", {
95976
+ logger56.debug("Updating status", {
95747
95977
  userId: ctx.user.id,
95748
95978
  notificationId,
95749
95979
  status: body2.status
@@ -95753,7 +95983,7 @@ var init_notification_controller = __esm(() => {
95753
95983
  getStats2 = requireNonAnonymous(async (ctx) => {
95754
95984
  const startDate = ctx.url.searchParams.get("startDate");
95755
95985
  const endDate = ctx.url.searchParams.get("endDate");
95756
- logger55.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
95986
+ logger56.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
95757
95987
  return ctx.services.notification.getStats(ctx.user, {
95758
95988
  startDate: startDate ? new Date(startDate) : undefined,
95759
95989
  endDate: endDate ? new Date(endDate) : undefined
@@ -95767,12 +95997,12 @@ var init_notification_controller = __esm(() => {
95767
95997
  } catch (error2) {
95768
95998
  if (error2 instanceof exports_external.ZodError) {
95769
95999
  const details = formatZodError(error2);
95770
- logger55.warn("Create notification validation failed", { details });
96000
+ logger56.warn("Create notification validation failed", { details });
95771
96001
  throw ApiError.unprocessableEntity("Invalid request body", details);
95772
96002
  }
95773
96003
  throw ApiError.badRequest("Invalid JSON body");
95774
96004
  }
95775
- logger55.debug("Creating notification", {
96005
+ logger56.debug("Creating notification", {
95776
96006
  userId: ctx.user.id,
95777
96007
  targetUserId: body2.userId,
95778
96008
  type: body2.type
@@ -95790,12 +96020,12 @@ var init_notification_controller = __esm(() => {
95790
96020
  });
95791
96021
  });
95792
96022
  deliver = requireNonAnonymous(async (ctx) => {
95793
- logger55.debug("Delivering notifications", { userId: ctx.user.id });
96023
+ logger56.debug("Delivering notifications", { userId: ctx.user.id });
95794
96024
  try {
95795
96025
  await ctx.services.notification.deliverPending(ctx.user.id);
95796
96026
  return { success: true };
95797
96027
  } catch (error2) {
95798
- logger55.error("Failed to deliver notifications", { error: error2 });
96028
+ logger56.error("Failed to deliver notifications", { error: error2 });
95799
96029
  throw ApiError.internal("Failed to deliver notifications");
95800
96030
  }
95801
96031
  });
@@ -95809,14 +96039,14 @@ var init_notification_controller = __esm(() => {
95809
96039
  });
95810
96040
 
95811
96041
  // ../api-core/src/controllers/realtime.controller.ts
95812
- var logger56, generateToken2, realtime;
96042
+ var logger57, generateToken2, realtime;
95813
96043
  var init_realtime_controller = __esm(() => {
95814
96044
  init_src2();
95815
96045
  init_utils11();
95816
- logger56 = log.scope("RealtimeController");
96046
+ logger57 = log.scope("RealtimeController");
95817
96047
  generateToken2 = requireNonAnonymous(async (ctx) => {
95818
96048
  const gameIdOrSlug = ctx.params.gameId;
95819
- logger56.debug("Generating token", {
96049
+ logger57.debug("Generating token", {
95820
96050
  userId: ctx.user.id,
95821
96051
  gameId: gameIdOrSlug || "global",
95822
96052
  launchId: ctx.launchId
@@ -95829,20 +96059,20 @@ var init_realtime_controller = __esm(() => {
95829
96059
  });
95830
96060
 
95831
96061
  // ../api-core/src/controllers/secrets.controller.ts
95832
- var logger57, listKeys2, setSecrets, deleteSecret, secrets;
96062
+ var logger58, listKeys2, setSecrets, deleteSecret, secrets;
95833
96063
  var init_secrets_controller = __esm(() => {
95834
96064
  init_esm();
95835
96065
  init_schemas_index();
95836
96066
  init_src2();
95837
96067
  init_errors();
95838
96068
  init_utils11();
95839
- logger57 = log.scope("SecretsController");
96069
+ logger58 = log.scope("SecretsController");
95840
96070
  listKeys2 = requireDeveloper(async (ctx) => {
95841
96071
  const slug2 = ctx.params.slug;
95842
96072
  if (!slug2) {
95843
96073
  throw ApiError.badRequest("Missing game slug");
95844
96074
  }
95845
- logger57.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
96075
+ logger58.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
95846
96076
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
95847
96077
  return { keys };
95848
96078
  });
@@ -95858,12 +96088,12 @@ var init_secrets_controller = __esm(() => {
95858
96088
  } catch (error2) {
95859
96089
  if (error2 instanceof exports_external.ZodError) {
95860
96090
  const details = formatZodError(error2);
95861
- logger57.warn("Set secrets validation failed", { details });
96091
+ logger58.warn("Set secrets validation failed", { details });
95862
96092
  throw ApiError.unprocessableEntity("Validation failed", details);
95863
96093
  }
95864
96094
  throw ApiError.badRequest("Invalid JSON body");
95865
96095
  }
95866
- logger57.debug("Setting secrets", {
96096
+ logger58.debug("Setting secrets", {
95867
96097
  userId: ctx.user.id,
95868
96098
  slug: slug2,
95869
96099
  keyCount: Object.keys(body2).length
@@ -95880,7 +96110,7 @@ var init_secrets_controller = __esm(() => {
95880
96110
  if (!key) {
95881
96111
  throw ApiError.badRequest("Missing secret key");
95882
96112
  }
95883
- logger57.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
96113
+ logger58.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
95884
96114
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
95885
96115
  return { success: true };
95886
96116
  });
@@ -95892,14 +96122,14 @@ var init_secrets_controller = __esm(() => {
95892
96122
  });
95893
96123
 
95894
96124
  // ../api-core/src/controllers/seed.controller.ts
95895
- var logger58, seed2;
96125
+ var logger59, seed2;
95896
96126
  var init_seed_controller = __esm(() => {
95897
96127
  init_esm();
95898
96128
  init_schemas_index();
95899
96129
  init_src2();
95900
96130
  init_errors();
95901
96131
  init_utils11();
95902
- logger58 = log.scope("SeedController");
96132
+ logger59 = log.scope("SeedController");
95903
96133
  seed2 = requireDeveloper(async (ctx) => {
95904
96134
  const slug2 = ctx.params.slug;
95905
96135
  if (!slug2) {
@@ -95912,12 +96142,12 @@ var init_seed_controller = __esm(() => {
95912
96142
  } catch (error2) {
95913
96143
  if (error2 instanceof exports_external.ZodError) {
95914
96144
  const details = formatZodError(error2);
95915
- logger58.warn("Seed database validation failed", { details });
96145
+ logger59.warn("Seed database validation failed", { details });
95916
96146
  throw ApiError.unprocessableEntity("Validation failed", details);
95917
96147
  }
95918
96148
  throw ApiError.badRequest("Invalid JSON body");
95919
96149
  }
95920
- logger58.debug("Seeding database", {
96150
+ logger59.debug("Seeding database", {
95921
96151
  userId: ctx.user.id,
95922
96152
  slug: slug2,
95923
96153
  codeLength: body2.code.length,
@@ -95928,18 +96158,18 @@ var init_seed_controller = __esm(() => {
95928
96158
  });
95929
96159
 
95930
96160
  // ../api-core/src/controllers/session.controller.ts
95931
- var logger59, start2, end, mintToken, sessions2;
96161
+ var logger60, start2, end, mintToken, sessions2;
95932
96162
  var init_session_controller = __esm(() => {
95933
96163
  init_src2();
95934
96164
  init_errors();
95935
96165
  init_utils11();
95936
- logger59 = log.scope("SessionController");
96166
+ logger60 = log.scope("SessionController");
95937
96167
  start2 = requireAuth(async (ctx) => {
95938
96168
  const gameIdOrSlug = ctx.params.gameId;
95939
96169
  if (!gameIdOrSlug) {
95940
96170
  throw ApiError.badRequest("Missing game ID or slug");
95941
96171
  }
95942
- logger59.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
96172
+ logger60.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
95943
96173
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
95944
96174
  });
95945
96175
  end = requireAuth(async (ctx) => {
@@ -95951,7 +96181,7 @@ var init_session_controller = __esm(() => {
95951
96181
  if (!sessionId) {
95952
96182
  throw ApiError.badRequest("Missing session ID");
95953
96183
  }
95954
- logger59.debug("Ending session", {
96184
+ logger60.debug("Ending session", {
95955
96185
  userId: ctx.user.id,
95956
96186
  gameIdOrSlug,
95957
96187
  sessionId,
@@ -95964,7 +96194,7 @@ var init_session_controller = __esm(() => {
95964
96194
  if (!gameIdOrSlug) {
95965
96195
  throw ApiError.badRequest("Missing game ID or slug");
95966
96196
  }
95967
- logger59.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
96197
+ logger60.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
95968
96198
  return ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
95969
96199
  });
95970
96200
  sessions2 = {
@@ -95975,13 +96205,13 @@ var init_session_controller = __esm(() => {
95975
96205
  });
95976
96206
 
95977
96207
  // ../api-core/src/controllers/shop.controller.ts
95978
- var logger60, getShopView, shop;
96208
+ var logger61, getShopView, shop;
95979
96209
  var init_shop_controller = __esm(() => {
95980
96210
  init_src2();
95981
96211
  init_utils11();
95982
- logger60 = log.scope("ShopController");
96212
+ logger61 = log.scope("ShopController");
95983
96213
  getShopView = requireNonAnonymous(async (ctx) => {
95984
- logger60.debug("Getting shop view", { userId: ctx.user.id });
96214
+ logger61.debug("Getting shop view", { userId: ctx.user.id });
95985
96215
  return ctx.services.shop.getShopView(ctx.user);
95986
96216
  });
95987
96217
  shop = {
@@ -95990,7 +96220,7 @@ var init_shop_controller = __esm(() => {
95990
96220
  });
95991
96221
 
95992
96222
  // ../api-core/src/controllers/shop-listing.controller.ts
95993
- var logger61, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
96223
+ var logger62, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
95994
96224
  var init_shop_listing_controller = __esm(() => {
95995
96225
  init_esm();
95996
96226
  init_schemas_index();
@@ -95998,9 +96228,9 @@ var init_shop_listing_controller = __esm(() => {
95998
96228
  init_src4();
95999
96229
  init_errors();
96000
96230
  init_utils11();
96001
- logger61 = log.scope("ShopListingController");
96231
+ logger62 = log.scope("ShopListingController");
96002
96232
  list7 = requireAdmin(async (ctx) => {
96003
- logger61.debug("Listing shop listings", { userId: ctx.user.id });
96233
+ logger62.debug("Listing shop listings", { userId: ctx.user.id });
96004
96234
  return ctx.services.shopListing.list();
96005
96235
  });
96006
96236
  getById4 = requireAdmin(async (ctx) => {
@@ -96011,7 +96241,7 @@ var init_shop_listing_controller = __esm(() => {
96011
96241
  if (!isValidUUID(listingId)) {
96012
96242
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
96013
96243
  }
96014
- logger61.debug("Getting listing", { userId: ctx.user.id, listingId });
96244
+ logger62.debug("Getting listing", { userId: ctx.user.id, listingId });
96015
96245
  return ctx.services.shopListing.getById(listingId);
96016
96246
  });
96017
96247
  create5 = requireAdmin(async (ctx) => {
@@ -96022,12 +96252,12 @@ var init_shop_listing_controller = __esm(() => {
96022
96252
  } catch (error2) {
96023
96253
  if (error2 instanceof exports_external.ZodError) {
96024
96254
  const details = formatZodError(error2);
96025
- logger61.warn("Create shop listing validation failed", { details });
96255
+ logger62.warn("Create shop listing validation failed", { details });
96026
96256
  throw ApiError.unprocessableEntity("Validation failed", details);
96027
96257
  }
96028
96258
  throw ApiError.badRequest("Invalid JSON body");
96029
96259
  }
96030
- logger61.debug("Creating listing", {
96260
+ logger62.debug("Creating listing", {
96031
96261
  userId: ctx.user.id,
96032
96262
  itemId: body2.itemId,
96033
96263
  currencyId: body2.currencyId,
@@ -96050,12 +96280,12 @@ var init_shop_listing_controller = __esm(() => {
96050
96280
  } catch (error2) {
96051
96281
  if (error2 instanceof exports_external.ZodError) {
96052
96282
  const details = formatZodError(error2);
96053
- logger61.warn("Update shop listing validation failed", { details });
96283
+ logger62.warn("Update shop listing validation failed", { details });
96054
96284
  throw ApiError.unprocessableEntity("Validation failed", details);
96055
96285
  }
96056
96286
  throw ApiError.badRequest("Invalid JSON body");
96057
96287
  }
96058
- logger61.debug("Updating listing", {
96288
+ logger62.debug("Updating listing", {
96059
96289
  userId: ctx.user.id,
96060
96290
  listingId,
96061
96291
  price: body2.price,
@@ -96072,7 +96302,7 @@ var init_shop_listing_controller = __esm(() => {
96072
96302
  if (!isValidUUID(listingId)) {
96073
96303
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
96074
96304
  }
96075
- logger61.debug("Deleting listing", { userId: ctx.user.id, listingId });
96305
+ logger62.debug("Deleting listing", { userId: ctx.user.id, listingId });
96076
96306
  await ctx.services.shopListing.delete(listingId);
96077
96307
  });
96078
96308
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -96083,7 +96313,7 @@ var init_shop_listing_controller = __esm(() => {
96083
96313
  if (!isValidUUID(gameId)) {
96084
96314
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96085
96315
  }
96086
- logger61.debug("Listing game listings", { userId: ctx.user.id, gameId });
96316
+ logger62.debug("Listing game listings", { userId: ctx.user.id, gameId });
96087
96317
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
96088
96318
  });
96089
96319
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -96098,7 +96328,7 @@ var init_shop_listing_controller = __esm(() => {
96098
96328
  if (!isValidUUID(itemId)) {
96099
96329
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96100
96330
  }
96101
- logger61.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
96331
+ logger62.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
96102
96332
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
96103
96333
  });
96104
96334
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -96120,12 +96350,12 @@ var init_shop_listing_controller = __esm(() => {
96120
96350
  } catch (error2) {
96121
96351
  if (error2 instanceof exports_external.ZodError) {
96122
96352
  const details = formatZodError(error2);
96123
- logger61.warn("Create game item listing validation failed", { details });
96353
+ logger62.warn("Create game item listing validation failed", { details });
96124
96354
  throw ApiError.unprocessableEntity("Validation failed", details);
96125
96355
  }
96126
96356
  throw ApiError.badRequest("Invalid JSON body");
96127
96357
  }
96128
- logger61.debug("Creating game item listing", {
96358
+ logger62.debug("Creating game item listing", {
96129
96359
  userId: ctx.user.id,
96130
96360
  gameId,
96131
96361
  itemId,
@@ -96153,12 +96383,12 @@ var init_shop_listing_controller = __esm(() => {
96153
96383
  } catch (error2) {
96154
96384
  if (error2 instanceof exports_external.ZodError) {
96155
96385
  const details = formatZodError(error2);
96156
- logger61.warn("Update game item listing validation failed", { details });
96386
+ logger62.warn("Update game item listing validation failed", { details });
96157
96387
  throw ApiError.unprocessableEntity("Validation failed", details);
96158
96388
  }
96159
96389
  throw ApiError.badRequest("Invalid JSON body");
96160
96390
  }
96161
- logger61.debug("Updating game item listing", {
96391
+ logger62.debug("Updating game item listing", {
96162
96392
  userId: ctx.user.id,
96163
96393
  gameId,
96164
96394
  itemId,
@@ -96180,7 +96410,7 @@ var init_shop_listing_controller = __esm(() => {
96180
96410
  if (!isValidUUID(itemId)) {
96181
96411
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96182
96412
  }
96183
- logger61.debug("Deleting game item listing", {
96413
+ logger62.debug("Deleting game item listing", {
96184
96414
  userId: ctx.user.id,
96185
96415
  gameId,
96186
96416
  itemId
@@ -96207,21 +96437,21 @@ async function getBySlug2(ctx) {
96207
96437
  if (!slug2) {
96208
96438
  throw ApiError.badRequest("Template slug is required");
96209
96439
  }
96210
- logger62.debug("Getting sprite by slug", { slug: slug2 });
96440
+ logger63.debug("Getting sprite by slug", { slug: slug2 });
96211
96441
  return ctx.services.sprite.getBySlug(slug2);
96212
96442
  }
96213
- var logger62, sprites;
96443
+ var logger63, sprites;
96214
96444
  var init_sprite_controller = __esm(() => {
96215
96445
  init_src2();
96216
96446
  init_errors();
96217
- logger62 = log.scope("SpriteController");
96447
+ logger63 = log.scope("SpriteController");
96218
96448
  sprites = {
96219
96449
  getBySlug: getBySlug2
96220
96450
  };
96221
96451
  });
96222
96452
 
96223
96453
  // ../api-core/src/controllers/timeback.controller.ts
96224
- var logger63, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, getStudentXp, getRoster, getStudentOverview, getStudentActivity, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
96454
+ var logger64, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getStudentActivity, grantXp, adjustTime, adjustMastery, toggleCompletion, searchStudents, enrollStudent, unenrollStudent, timeback2;
96225
96455
  var init_timeback_controller = __esm(() => {
96226
96456
  init_esm();
96227
96457
  init_schemas_index();
@@ -96229,15 +96459,15 @@ var init_timeback_controller = __esm(() => {
96229
96459
  init_src4();
96230
96460
  init_errors();
96231
96461
  init_utils11();
96232
- logger63 = log.scope("TimebackController");
96462
+ logger64 = log.scope("TimebackController");
96233
96463
  getTodayXp = requireNonAnonymous(async (ctx) => {
96234
96464
  const date4 = ctx.url.searchParams.get("date") || undefined;
96235
96465
  const tz = ctx.url.searchParams.get("tz") || undefined;
96236
- logger63.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
96466
+ logger64.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
96237
96467
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
96238
96468
  });
96239
96469
  getTotalXp = requireNonAnonymous(async (ctx) => {
96240
- logger63.debug("Getting total XP", { userId: ctx.user.id });
96470
+ logger64.debug("Getting total XP", { userId: ctx.user.id });
96241
96471
  return ctx.services.timeback.getTotalXp(ctx.user.id);
96242
96472
  });
96243
96473
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -96248,18 +96478,18 @@ var init_timeback_controller = __esm(() => {
96248
96478
  } catch (error2) {
96249
96479
  if (error2 instanceof exports_external.ZodError) {
96250
96480
  const details = formatZodError(error2);
96251
- logger63.warn("Update today XP validation failed", { details });
96481
+ logger64.warn("Update today XP validation failed", { details });
96252
96482
  throw ApiError.unprocessableEntity("Validation failed", details);
96253
96483
  }
96254
96484
  throw ApiError.badRequest("Invalid JSON body");
96255
96485
  }
96256
- logger63.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
96486
+ logger64.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
96257
96487
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
96258
96488
  });
96259
96489
  getXpHistory = requireNonAnonymous(async (ctx) => {
96260
96490
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
96261
96491
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
96262
- logger63.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
96492
+ logger64.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
96263
96493
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
96264
96494
  });
96265
96495
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -96270,18 +96500,18 @@ var init_timeback_controller = __esm(() => {
96270
96500
  } catch (error2) {
96271
96501
  if (error2 instanceof exports_external.ZodError) {
96272
96502
  const details = formatZodError(error2);
96273
- logger63.warn("Populate student validation failed", { details });
96503
+ logger64.warn("Populate student validation failed", { details });
96274
96504
  throw ApiError.unprocessableEntity("Validation failed", details);
96275
96505
  }
96276
96506
  }
96277
- logger63.debug("Populating student", {
96507
+ logger64.debug("Populating student", {
96278
96508
  userId: ctx.user.id,
96279
96509
  hasProvidedNames: Boolean(providedNames)
96280
96510
  });
96281
96511
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
96282
96512
  });
96283
96513
  getUser = requireNonAnonymous(async (ctx) => {
96284
- logger63.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
96514
+ logger64.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
96285
96515
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
96286
96516
  });
96287
96517
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -96289,7 +96519,7 @@ var init_timeback_controller = __esm(() => {
96289
96519
  if (!timebackId) {
96290
96520
  throw ApiError.badRequest("Missing timebackId parameter");
96291
96521
  }
96292
- logger63.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
96522
+ logger64.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
96293
96523
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
96294
96524
  });
96295
96525
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -96300,12 +96530,12 @@ var init_timeback_controller = __esm(() => {
96300
96530
  } catch (error2) {
96301
96531
  if (error2 instanceof exports_external.ZodError) {
96302
96532
  const details = formatZodError(error2);
96303
- logger63.warn("Setup integration validation failed", { details });
96533
+ logger64.warn("Setup integration validation failed", { details });
96304
96534
  throw ApiError.unprocessableEntity("Validation failed", details);
96305
96535
  }
96306
96536
  throw ApiError.badRequest("Invalid JSON body");
96307
96537
  }
96308
- logger63.debug("Setting up integration", {
96538
+ logger64.debug("Setting up integration", {
96309
96539
  userId: ctx.user.id,
96310
96540
  gameId: body2.gameId
96311
96541
  });
@@ -96319,7 +96549,7 @@ var init_timeback_controller = __esm(() => {
96319
96549
  if (!isValidUUID(gameId)) {
96320
96550
  throw ApiError.unprocessableEntity("Invalid gameId format");
96321
96551
  }
96322
- logger63.debug("Getting integrations", { userId: ctx.user.id, gameId });
96552
+ logger64.debug("Getting integrations", { userId: ctx.user.id, gameId });
96323
96553
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
96324
96554
  });
96325
96555
  verifyIntegration = requireDeveloper(async (ctx) => {
@@ -96330,7 +96560,7 @@ var init_timeback_controller = __esm(() => {
96330
96560
  if (!isValidUUID(gameId)) {
96331
96561
  throw ApiError.unprocessableEntity("Invalid gameId format");
96332
96562
  }
96333
- logger63.debug("Verifying integration", { userId: ctx.user.id, gameId });
96563
+ logger64.debug("Verifying integration", { userId: ctx.user.id, gameId });
96334
96564
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
96335
96565
  });
96336
96566
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -96341,7 +96571,7 @@ var init_timeback_controller = __esm(() => {
96341
96571
  if (!isValidUUID(gameId)) {
96342
96572
  throw ApiError.unprocessableEntity("Invalid gameId format");
96343
96573
  }
96344
- logger63.debug("Getting config", { userId: ctx.user.id, gameId });
96574
+ logger64.debug("Getting config", { userId: ctx.user.id, gameId });
96345
96575
  return ctx.services.timeback.getConfig(gameId, ctx.user);
96346
96576
  });
96347
96577
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -96352,7 +96582,7 @@ var init_timeback_controller = __esm(() => {
96352
96582
  if (!isValidUUID(gameId)) {
96353
96583
  throw ApiError.unprocessableEntity("Invalid gameId format");
96354
96584
  }
96355
- logger63.debug("Deleting integrations", { userId: ctx.user.id, gameId });
96585
+ logger64.debug("Deleting integrations", { userId: ctx.user.id, gameId });
96356
96586
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
96357
96587
  });
96358
96588
  endActivity = requireDeveloper(async (ctx) => {
@@ -96363,7 +96593,7 @@ var init_timeback_controller = __esm(() => {
96363
96593
  } catch (error2) {
96364
96594
  if (error2 instanceof exports_external.ZodError) {
96365
96595
  const details = formatZodError(error2);
96366
- logger63.warn("End activity validation failed", { details });
96596
+ logger64.warn("End activity validation failed", { details });
96367
96597
  throw ApiError.unprocessableEntity("Validation failed", details);
96368
96598
  }
96369
96599
  throw ApiError.badRequest("Invalid JSON body");
@@ -96381,7 +96611,7 @@ var init_timeback_controller = __esm(() => {
96381
96611
  masteredUnits,
96382
96612
  extensions
96383
96613
  } = body2;
96384
- logger63.debug("Ending activity", { userId: ctx.user.id, gameId });
96614
+ logger64.debug("Ending activity", { userId: ctx.user.id, gameId });
96385
96615
  return ctx.services.timeback.endActivity({
96386
96616
  gameId,
96387
96617
  studentId,
@@ -96405,7 +96635,7 @@ var init_timeback_controller = __esm(() => {
96405
96635
  } catch (error2) {
96406
96636
  if (error2 instanceof exports_external.ZodError) {
96407
96637
  const details = formatZodError(error2);
96408
- logger63.warn("Heartbeat validation failed", { details });
96638
+ logger64.warn("Heartbeat validation failed", { details });
96409
96639
  throw ApiError.unprocessableEntity("Validation failed", details);
96410
96640
  }
96411
96641
  throw ApiError.badRequest("Invalid JSON body");
@@ -96421,7 +96651,7 @@ var init_timeback_controller = __esm(() => {
96421
96651
  windowSequence,
96422
96652
  isFinal
96423
96653
  } = body2;
96424
- logger63.debug("Recording heartbeat", {
96654
+ logger64.debug("Recording heartbeat", {
96425
96655
  userId: ctx.user.id,
96426
96656
  gameId,
96427
96657
  runId,
@@ -96444,6 +96674,19 @@ var init_timeback_controller = __esm(() => {
96444
96674
  user: ctx.user
96445
96675
  });
96446
96676
  });
96677
+ advanceCourse = requireDeveloper(async (ctx) => {
96678
+ const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
96679
+ logger64.debug("Advancing student manually", {
96680
+ userId: ctx.user.id,
96681
+ gameId: body2.gameId,
96682
+ studentId: body2.studentId,
96683
+ subject: body2.subject
96684
+ });
96685
+ return ctx.services.timeback.advanceCourse({
96686
+ ...body2,
96687
+ user: ctx.user
96688
+ });
96689
+ });
96447
96690
  getStudentXp = requireDeveloper(async (ctx) => {
96448
96691
  const timebackId = ctx.params.timebackId;
96449
96692
  if (!timebackId) {
@@ -96474,7 +96717,7 @@ var init_timeback_controller = __esm(() => {
96474
96717
  perCourse: includeOptions.includes("percourse"),
96475
96718
  today: includeOptions.includes("today")
96476
96719
  };
96477
- logger63.debug("Getting student XP", {
96720
+ logger64.debug("Getting student XP", {
96478
96721
  requesterId: ctx.user.id,
96479
96722
  timebackId,
96480
96723
  gameId,
@@ -96495,7 +96738,7 @@ var init_timeback_controller = __esm(() => {
96495
96738
  if (!gameId || !courseId) {
96496
96739
  throw ApiError.badRequest("Missing gameId or courseId parameter");
96497
96740
  }
96498
- logger63.debug("Getting course roster", {
96741
+ logger64.debug("Getting course roster", {
96499
96742
  requesterId: ctx.user.id,
96500
96743
  gameId,
96501
96744
  courseId
@@ -96509,7 +96752,7 @@ var init_timeback_controller = __esm(() => {
96509
96752
  if (!timebackId || !gameId) {
96510
96753
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
96511
96754
  }
96512
- logger63.debug("Getting student overview", {
96755
+ logger64.debug("Getting student overview", {
96513
96756
  requesterId: ctx.user.id,
96514
96757
  timebackId,
96515
96758
  gameId,
@@ -96528,7 +96771,7 @@ var init_timeback_controller = __esm(() => {
96528
96771
  if (!timebackId || !courseId || !gameId) {
96529
96772
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
96530
96773
  }
96531
- logger63.debug("Getting student activity", {
96774
+ logger64.debug("Getting student activity", {
96532
96775
  requesterId: ctx.user.id,
96533
96776
  timebackId,
96534
96777
  courseId,
@@ -96546,7 +96789,7 @@ var init_timeback_controller = __esm(() => {
96546
96789
  });
96547
96790
  grantXp = requireDeveloper(async (ctx) => {
96548
96791
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
96549
- logger63.debug("Granting manual XP", {
96792
+ logger64.debug("Granting manual XP", {
96550
96793
  requesterId: ctx.user.id,
96551
96794
  gameId: body2.gameId,
96552
96795
  courseId: body2.courseId,
@@ -96558,7 +96801,7 @@ var init_timeback_controller = __esm(() => {
96558
96801
  });
96559
96802
  adjustTime = requireDeveloper(async (ctx) => {
96560
96803
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
96561
- logger63.debug("Adjusting time spent", {
96804
+ logger64.debug("Adjusting time spent", {
96562
96805
  requesterId: ctx.user.id,
96563
96806
  gameId: body2.gameId,
96564
96807
  courseId: body2.courseId,
@@ -96570,7 +96813,7 @@ var init_timeback_controller = __esm(() => {
96570
96813
  });
96571
96814
  adjustMastery = requireDeveloper(async (ctx) => {
96572
96815
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
96573
- logger63.debug("Adjusting mastered units", {
96816
+ logger64.debug("Adjusting mastered units", {
96574
96817
  requesterId: ctx.user.id,
96575
96818
  gameId: body2.gameId,
96576
96819
  courseId: body2.courseId,
@@ -96582,7 +96825,7 @@ var init_timeback_controller = __esm(() => {
96582
96825
  });
96583
96826
  toggleCompletion = requireGameManagementAccess(async (ctx) => {
96584
96827
  const body2 = await parseRequestBody(ctx.request, ToggleCourseCompletionRequestSchema);
96585
- logger63.debug("Toggling course completion", {
96828
+ logger64.debug("Toggling course completion", {
96586
96829
  requesterId: ctx.user.id,
96587
96830
  gameId: body2.gameId,
96588
96831
  courseId: body2.courseId,
@@ -96598,7 +96841,7 @@ var init_timeback_controller = __esm(() => {
96598
96841
  if (!gameId || !courseId) {
96599
96842
  throw ApiError.badRequest("Missing gameId or courseId parameter");
96600
96843
  }
96601
- logger63.debug("Searching students for enrollment", {
96844
+ logger64.debug("Searching students for enrollment", {
96602
96845
  requesterId: ctx.user.id,
96603
96846
  gameId,
96604
96847
  courseId,
@@ -96608,7 +96851,7 @@ var init_timeback_controller = __esm(() => {
96608
96851
  });
96609
96852
  enrollStudent = requireGameManagementAccess(async (ctx) => {
96610
96853
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
96611
- logger63.debug("Enrolling student", {
96854
+ logger64.debug("Enrolling student", {
96612
96855
  requesterId: ctx.user.id,
96613
96856
  gameId: body2.gameId,
96614
96857
  courseId: body2.courseId,
@@ -96618,7 +96861,7 @@ var init_timeback_controller = __esm(() => {
96618
96861
  });
96619
96862
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
96620
96863
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
96621
- logger63.debug("Unenrolling student", {
96864
+ logger64.debug("Unenrolling student", {
96622
96865
  requesterId: ctx.user.id,
96623
96866
  gameId: body2.gameId,
96624
96867
  courseId: body2.courseId,
@@ -96641,6 +96884,7 @@ var init_timeback_controller = __esm(() => {
96641
96884
  deleteIntegrations,
96642
96885
  endActivity,
96643
96886
  heartbeat,
96887
+ advanceCourse,
96644
96888
  getStudentXp,
96645
96889
  getRoster,
96646
96890
  getStudentOverview,
@@ -96656,14 +96900,14 @@ var init_timeback_controller = __esm(() => {
96656
96900
  });
96657
96901
 
96658
96902
  // ../api-core/src/controllers/upload.controller.ts
96659
- var logger64, initiate;
96903
+ var logger65, initiate;
96660
96904
  var init_upload_controller = __esm(() => {
96661
96905
  init_esm();
96662
96906
  init_schemas_index();
96663
96907
  init_src2();
96664
96908
  init_errors();
96665
96909
  init_utils11();
96666
- logger64 = log.scope("UploadController");
96910
+ logger65 = log.scope("UploadController");
96667
96911
  initiate = requireDeveloper(async (ctx) => {
96668
96912
  let body2;
96669
96913
  try {
@@ -96672,34 +96916,34 @@ var init_upload_controller = __esm(() => {
96672
96916
  } catch (error2) {
96673
96917
  if (error2 instanceof exports_external.ZodError) {
96674
96918
  const details = formatZodError(error2);
96675
- logger64.warn("Initiate upload validation failed", { details });
96919
+ logger65.warn("Initiate upload validation failed", { details });
96676
96920
  throw ApiError.unprocessableEntity("Validation failed", details);
96677
96921
  }
96678
96922
  throw ApiError.badRequest("Invalid JSON body");
96679
96923
  }
96680
- logger64.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
96924
+ logger65.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
96681
96925
  return ctx.services.upload.initiate(body2, ctx.user);
96682
96926
  });
96683
96927
  });
96684
96928
 
96685
96929
  // ../api-core/src/controllers/user.controller.ts
96686
- var logger65, getMe, getDemoProfile, updateDemoProfile, users2;
96930
+ var logger66, getMe, getDemoProfile, updateDemoProfile, users2;
96687
96931
  var init_user_controller = __esm(() => {
96688
96932
  init_schemas_index();
96689
96933
  init_src2();
96690
96934
  init_utils11();
96691
- logger65 = log.scope("UserController");
96935
+ logger66 = log.scope("UserController");
96692
96936
  getMe = requireNonAnonymous(async (ctx) => {
96693
- logger65.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
96937
+ logger66.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
96694
96938
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
96695
96939
  });
96696
96940
  getDemoProfile = requireAnonymous(async (ctx) => {
96697
- logger65.debug("Getting demo profile", { userId: ctx.user.id });
96941
+ logger66.debug("Getting demo profile", { userId: ctx.user.id });
96698
96942
  return ctx.services.user.getDemoProfile(ctx.user.id);
96699
96943
  });
96700
96944
  updateDemoProfile = requireAnonymous(async (ctx) => {
96701
96945
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
96702
- logger65.debug("Updating demo profile", {
96946
+ logger66.debug("Updating demo profile", {
96703
96947
  userId: ctx.user.id,
96704
96948
  displayName: body2.displayName
96705
96949
  });
@@ -96713,13 +96957,13 @@ var init_user_controller = __esm(() => {
96713
96957
  });
96714
96958
 
96715
96959
  // ../api-core/src/controllers/verify.controller.ts
96716
- var logger66;
96960
+ var logger67;
96717
96961
  var init_verify_controller = __esm(() => {
96718
96962
  init_schemas_index();
96719
96963
  init_src2();
96720
96964
  init_errors();
96721
96965
  init_utils11();
96722
- logger66 = log.scope("VerifyController");
96966
+ logger67 = log.scope("VerifyController");
96723
96967
  });
96724
96968
 
96725
96969
  // ../api-core/src/controllers/index.ts
@@ -97032,7 +97276,7 @@ var init_uploads = __esm(() => {
97032
97276
  });
97033
97277
 
97034
97278
  // src/routes/platform/games/deploy.ts
97035
- var logger67, gameDeployRouter;
97279
+ var logger68, gameDeployRouter;
97036
97280
  var init_deploy = __esm(() => {
97037
97281
  init_drizzle_orm();
97038
97282
  init_dist4();
@@ -97042,7 +97286,7 @@ var init_deploy = __esm(() => {
97042
97286
  init_src2();
97043
97287
  init_api();
97044
97288
  init_uploads();
97045
- logger67 = log.scope("SandboxDeploy");
97289
+ logger68 = log.scope("SandboxDeploy");
97046
97290
  gameDeployRouter = new Hono2;
97047
97291
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
97048
97292
  const user = c2.get("user");
@@ -97159,7 +97403,7 @@ var init_deploy = __esm(() => {
97159
97403
  completedAt: now2
97160
97404
  };
97161
97405
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
97162
- logger67.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
97406
+ logger68.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
97163
97407
  return c2.json({
97164
97408
  id: insertedJob.id,
97165
97409
  status: "succeeded",
@@ -97712,6 +97956,7 @@ var init_timeback6 = __esm(() => {
97712
97956
  timebackRouter.delete("/integrations/:gameId", handle2(timeback2.deleteIntegrations, { status: 204 }));
97713
97957
  timebackRouter.post("/end-activity", handle2(timeback2.endActivity));
97714
97958
  timebackRouter.post("/heartbeat", handle2(timeback2.heartbeat));
97959
+ timebackRouter.post("/advance-course", handle2(timeback2.advanceCourse));
97715
97960
  timebackRouter.get("/user", async (c2) => {
97716
97961
  const user = c2.get("user");
97717
97962
  const gameId = c2.get("gameId");
@@ -97799,7 +98044,7 @@ function verifyMockToken(idToken) {
97799
98044
  throw new Error("Invalid LTI token format");
97800
98045
  }
97801
98046
  }
97802
- var logger68, ltiRouter;
98047
+ var logger69, ltiRouter;
97803
98048
  var init_lti = __esm(() => {
97804
98049
  init_drizzle_orm();
97805
98050
  init_dist4();
@@ -97809,7 +98054,7 @@ var init_lti = __esm(() => {
97809
98054
  init_src2();
97810
98055
  init_constants();
97811
98056
  init_api();
97812
- logger68 = log.scope("SandboxLti");
98057
+ logger69 = log.scope("SandboxLti");
97813
98058
  ltiRouter = new Hono2;
97814
98059
  ltiRouter.post("/launch", async (c2) => {
97815
98060
  const db2 = c2.get("db");
@@ -97827,7 +98072,7 @@ var init_lti = __esm(() => {
97827
98072
  claims = verifyMockToken(idToken);
97828
98073
  } catch (error2) {
97829
98074
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
97830
- logger68.error("LTI token verification failed", { error: errorMessage });
98075
+ logger69.error("LTI token verification failed", { error: errorMessage });
97831
98076
  return c2.json({
97832
98077
  error: "invalid_token",
97833
98078
  message: errorMessage
@@ -97835,7 +98080,7 @@ var init_lti = __esm(() => {
97835
98080
  }
97836
98081
  const validationError = validateLtiClaims(claims);
97837
98082
  if (validationError) {
97838
- logger68.warn("LTI claims validation failed", {
98083
+ logger69.warn("LTI claims validation failed", {
97839
98084
  error: validationError,
97840
98085
  sub: claims.sub
97841
98086
  });
@@ -97855,7 +98100,7 @@ var init_lti = __esm(() => {
97855
98100
  createdAt: new Date,
97856
98101
  updatedAt: new Date
97857
98102
  });
97858
- logger68.info("LTI launch successful", { userId: user.id });
98103
+ logger69.info("LTI launch successful", { userId: user.id });
97859
98104
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
97860
98105
  const currentHost = new URL(c2.req.url).hostname;
97861
98106
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -97863,7 +98108,7 @@ var init_lti = __esm(() => {
97863
98108
  return c2.redirect(redirectPath);
97864
98109
  } catch (error2) {
97865
98110
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
97866
- logger68.error("Unexpected error during LTI launch", { error: errorMessage });
98111
+ logger69.error("Unexpected error during LTI launch", { error: errorMessage });
97867
98112
  return c2.json({
97868
98113
  error: "unexpected_error",
97869
98114
  message: "An unexpected error occurred during LTI launch"