@playcademy/sandbox 0.3.18-beta.2 → 0.3.19-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +848 -502
  2. package/dist/server.js +848 -502
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1316,7 +1316,7 @@ var package_default;
1316
1316
  var init_package = __esm(() => {
1317
1317
  package_default = {
1318
1318
  name: "@playcademy/sandbox",
1319
- version: "0.3.18-beta.2",
1319
+ version: "0.3.19-beta.1",
1320
1320
  description: "Local development server for Playcademy game development",
1321
1321
  type: "module",
1322
1322
  exports: {
@@ -30453,6 +30453,24 @@ var init_src4 = __esm(() => {
30453
30453
  init_pure();
30454
30454
  });
30455
30455
 
30456
+ // ../utils/src/timeback.ts
30457
+ function formatGradeLabel(grade) {
30458
+ switch (grade) {
30459
+ case -1: {
30460
+ return "Pre-K";
30461
+ }
30462
+ case 0: {
30463
+ return "Kindergarten";
30464
+ }
30465
+ case 13: {
30466
+ return "AP";
30467
+ }
30468
+ default: {
30469
+ return `Grade ${grade}`;
30470
+ }
30471
+ }
30472
+ }
30473
+
30456
30474
  // ../../node_modules/.bun/drizzle-zod@0.7.1+e9f18f9688af15ce/node_modules/drizzle-zod/index.mjs
30457
30475
  function isColumnType(column2, columnTypes) {
30458
30476
  return columnTypes.includes(column2.columnType);
@@ -31162,7 +31180,7 @@ function isValidAdminAttributionDate(value) {
31162
31180
  const date3 = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
31163
31181
  return date3.getUTCFullYear() === year && date3.getUTCMonth() + 1 === month && date3.getUTCDate() === day;
31164
31182
  }
31165
- var TIMEBACK_GRADES, TIMEBACK_SUBJECTS4, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, TimebackActivityDataSchema, EndActivityRequestSchema, GameActivityMetricsSchema, GameCourseMetricsSchema, GameMetricsResponseSchema, AdvanceCourseRequestSchema, HeartbeatRequestSchema, PopulateStudentRequestSchema, DerivedPlatformCourseConfigSchema, TimebackBaseConfigSchema, PlatformTimebackSetupRequestSchema, AdminTimebackMutationBaseSchema, AdminAttributionDateSchema, GrantTimebackXpRequestSchema, AdjustTimebackTimeRequestSchema, AdjustTimebackMasteryRequestSchema, EnrollStudentRequestSchema, UnenrollStudentRequestSchema, ReactivateEnrollmentRequestSchema, InsertAssessmentTestSchema, CreateAssessmentRequestSchema, ReorderAssessmentsRequestSchema, ReorderQuestionsRequestSchema;
31183
+ var TIMEBACK_GRADES, TIMEBACK_SUBJECTS4, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, CourseGoalsSchema, UpdateGameTimebackIntegrationRequestSchema, TimebackActivityDataSchema, EndActivityRequestSchema, GameActivityMetricsSchema, GameCourseMetricsSchema, GameMetricsResponseSchema, AdvanceCourseRequestSchema, HeartbeatRequestSchema, PopulateStudentRequestSchema, DerivedPlatformCourseConfigSchema, TimebackBaseConfigSchema, PlatformTimebackSetupRequestSchema, AdminTimebackMutationBaseSchema, AdminAttributionDateSchema, ADMIN_GRANT_XP_MIN = -1e5, ADMIN_GRANT_XP_MAX = 1e5, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE, GrantTimebackXpRequestSchema, AdjustTimebackTimeRequestSchema, AdjustTimebackMasteryRequestSchema, ReconcileMasteryForConfigChangeSchema, EnrollStudentRequestSchema, UnenrollStudentRequestSchema, ReactivateEnrollmentRequestSchema, InsertAssessmentTestSchema, CreateAssessmentRequestSchema, ReorderAssessmentsRequestSchema, ReorderQuestionsRequestSchema;
31166
31184
  var init_schemas11 = __esm(() => {
31167
31185
  init_drizzle_zod();
31168
31186
  init_esm();
@@ -31187,6 +31205,24 @@ var init_schemas11 = __esm(() => {
31187
31205
  xp: exports_external.number().min(0, "XP must be a non-negative number"),
31188
31206
  userTimestamp: exports_external.string().datetime().optional()
31189
31207
  });
31208
+ CourseGoalsSchema = exports_external.object({
31209
+ dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
31210
+ dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
31211
+ dailyActiveMinutes: exports_external.number().int().nonnegative().nullable().optional(),
31212
+ dailyAccuracy: exports_external.number().int().min(0).max(100).nullable().optional(),
31213
+ dailyMasteredUnits: exports_external.number().int().nonnegative().nullable().optional()
31214
+ });
31215
+ UpdateGameTimebackIntegrationRequestSchema = exports_external.object({
31216
+ title: exports_external.string().trim().min(1).optional(),
31217
+ courseCode: exports_external.string().trim().min(1).optional(),
31218
+ subject: TimebackSubjectSchema.optional(),
31219
+ totalXp: exports_external.number().int().nonnegative().nullable().optional(),
31220
+ masterableUnits: exports_external.number().int().nonnegative().nullable().optional(),
31221
+ goals: CourseGoalsSchema.optional(),
31222
+ publishStatus: exports_external.enum(["draft", "testing", "published", "deactivated"]).nullable().optional(),
31223
+ isSupplemental: exports_external.boolean().optional(),
31224
+ timebackVisible: exports_external.boolean().nullable().optional()
31225
+ });
31190
31226
  TimebackActivityDataSchema = exports_external.object({
31191
31227
  activityId: exports_external.string().min(1),
31192
31228
  activityName: exports_external.string().optional(),
@@ -31340,8 +31376,9 @@ var init_schemas11 = __esm(() => {
31340
31376
  });
31341
31377
  }
31342
31378
  });
31379
+ ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE = `Amount must be between ${ADMIN_GRANT_XP_MIN} and ${ADMIN_GRANT_XP_MAX}`;
31343
31380
  GrantTimebackXpRequestSchema = AdminTimebackMutationBaseSchema.extend({
31344
- xp: exports_external.number().min(-100, "Amount must be between -100 and 100").max(100, "Amount must be between -100 and 100").refine((value) => value !== 0, { message: "Amount cannot be 0" }),
31381
+ xp: exports_external.number().min(ADMIN_GRANT_XP_MIN, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE).max(ADMIN_GRANT_XP_MAX, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE).refine((value) => value !== 0, { message: "Amount cannot be 0" }),
31345
31382
  date: AdminAttributionDateSchema.optional(),
31346
31383
  useCurrentTime: exports_external.boolean().optional()
31347
31384
  });
@@ -31355,6 +31392,13 @@ var init_schemas11 = __esm(() => {
31355
31392
  date: AdminAttributionDateSchema.optional(),
31356
31393
  useCurrentTime: exports_external.boolean().optional()
31357
31394
  });
31395
+ ReconcileMasteryForConfigChangeSchema = exports_external.object({
31396
+ gameId: exports_external.string().uuid(),
31397
+ courseId: exports_external.string().min(1),
31398
+ oldMasterableUnits: exports_external.number().int().positive(),
31399
+ newMasterableUnits: exports_external.number().int().positive(),
31400
+ affectedStudentIds: exports_external.array(exports_external.string().min(1)).min(1).max(500)
31401
+ });
31358
31402
  EnrollStudentRequestSchema = exports_external.object({
31359
31403
  gameId: exports_external.string().uuid(),
31360
31404
  courseId: exports_external.string().min(1),
@@ -31510,6 +31554,65 @@ var init_timeback_admin_util = __esm(() => {
31510
31554
  init_errors();
31511
31555
  });
31512
31556
 
31557
+ // ../api-core/src/utils/timeback-mastery-completion.util.ts
31558
+ async function upsertMasteryCompletionEntry(params) {
31559
+ const { client, courseId, studentId, appName, action } = params;
31560
+ const ids = deriveSourcedIds(courseId);
31561
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
31562
+ const resultId = `${lineItemId}:${studentId}:completion`;
31563
+ if (action === "complete") {
31564
+ await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
31565
+ sourcedId: lineItemId,
31566
+ title: "Mastery Completion",
31567
+ status: ONEROSTER_STATUS.active,
31568
+ course: { sourcedId: ids.course },
31569
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
31570
+ });
31571
+ await client.oneroster.assessmentResults.upsert(resultId, {
31572
+ sourcedId: resultId,
31573
+ status: ONEROSTER_STATUS.active,
31574
+ assessmentLineItem: { sourcedId: lineItemId },
31575
+ student: { sourcedId: studentId },
31576
+ score: 100,
31577
+ scoreDate: new Date().toISOString(),
31578
+ scoreStatus: SCORE_STATUS.fullyGraded,
31579
+ inProgress: "false",
31580
+ metadata: {
31581
+ isMasteryCompletion: true,
31582
+ adminAction: true,
31583
+ appName
31584
+ }
31585
+ });
31586
+ } else {
31587
+ try {
31588
+ await client.oneroster.assessmentResults.upsert(resultId, {
31589
+ sourcedId: resultId,
31590
+ status: ONEROSTER_STATUS.active,
31591
+ assessmentLineItem: { sourcedId: lineItemId },
31592
+ student: { sourcedId: studentId },
31593
+ score: 0,
31594
+ scoreDate: new Date().toISOString(),
31595
+ scoreStatus: SCORE_STATUS.notSubmitted,
31596
+ inProgress: "true",
31597
+ metadata: {
31598
+ isMasteryCompletion: true,
31599
+ adminAction: true,
31600
+ appName
31601
+ }
31602
+ });
31603
+ } catch {
31604
+ logger17.debug("No completion entry to revoke", { studentId, courseId });
31605
+ }
31606
+ }
31607
+ }
31608
+ var logger17;
31609
+ var init_timeback_mastery_completion_util = __esm(() => {
31610
+ init_src2();
31611
+ init_constants4();
31612
+ init_utils6();
31613
+ logger17 = log.scope("timeback-mastery-completion");
31614
+ });
31615
+
31513
31616
  // ../api-core/src/utils/timeback.util.ts
31514
31617
  function isRecord2(value) {
31515
31618
  return typeof value === "object" && value !== null;
@@ -31867,7 +31970,7 @@ class TimebackAdminService {
31867
31970
  }
31868
31971
  requireClient() {
31869
31972
  if (!this.deps.timeback) {
31870
- logger17.error("Timeback client not available in context");
31973
+ logger18.error("Timeback client not available in context");
31871
31974
  throw new ValidationError("Timeback integration not available in this environment");
31872
31975
  }
31873
31976
  return this.deps.timeback;
@@ -32087,7 +32190,7 @@ class TimebackAdminService {
32087
32190
  });
32088
32191
  return [enrollmentId, this.summarizeAnalyticsFacts(analytics.facts)];
32089
32192
  } catch (error) {
32090
- logger17.warn("Failed to load enrollment analytics summary", {
32193
+ logger18.warn("Failed to load enrollment analytics summary", {
32091
32194
  enrollmentId,
32092
32195
  error: error instanceof Error ? error.message : String(error)
32093
32196
  });
@@ -32117,7 +32220,7 @@ class TimebackAdminService {
32117
32220
  const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
32118
32221
  return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
32119
32222
  } catch (error) {
32120
- logger17.warn("Failed to load recent Caliper activity", {
32223
+ logger18.warn("Failed to load recent Caliper activity", {
32121
32224
  studentId,
32122
32225
  gameId: source.gameId,
32123
32226
  sourceMode: source.sourceMode,
@@ -32296,7 +32399,7 @@ class TimebackAdminService {
32296
32399
  }) : undefined;
32297
32400
  return {
32298
32401
  courseId: integration.courseId,
32299
- title: enrollment?.course.title || `${integration.subject} Grade ${integration.grade}`,
32402
+ title: enrollment?.course.title || `${integration.subject} ${formatGradeLabel(integration.grade)}`,
32300
32403
  grade: integration.grade,
32301
32404
  subject: integration.subject,
32302
32405
  enrollmentId: enrollment?.id || null,
@@ -32443,60 +32546,44 @@ class TimebackAdminService {
32443
32546
  const wasMastered = currentMastered >= masterableUnits;
32444
32547
  const willBeMastered = currentMastered + data.units >= masterableUnits;
32445
32548
  if (wasMastered !== willBeMastered) {
32446
- const ids = deriveSourcedIds(data.courseId);
32447
- const lineItemId = `${ids.course}-mastery-completion-assessment`;
32448
- const resultId = `${lineItemId}:${data.studentId}:completion`;
32449
- if (willBeMastered) {
32450
- await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
32451
- sourcedId: lineItemId,
32452
- title: "Mastery Completion",
32453
- status: ONEROSTER_STATUS.active,
32454
- course: { sourcedId: ids.course },
32455
- ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
32456
- });
32457
- await client.oneroster.assessmentResults.upsert(resultId, {
32458
- sourcedId: resultId,
32459
- status: ONEROSTER_STATUS.active,
32460
- assessmentLineItem: { sourcedId: lineItemId },
32461
- student: { sourcedId: data.studentId },
32462
- score: 100,
32463
- scoreDate: new Date().toISOString(),
32464
- scoreStatus: SCORE_STATUS.fullyGraded,
32465
- inProgress: "false",
32466
- metadata: {
32467
- isMasteryCompletion: true,
32468
- adminAction: true,
32469
- appName
32470
- }
32471
- });
32472
- } else {
32473
- try {
32474
- await client.oneroster.assessmentResults.upsert(resultId, {
32475
- sourcedId: resultId,
32476
- status: ONEROSTER_STATUS.active,
32477
- assessmentLineItem: { sourcedId: lineItemId },
32478
- student: { sourcedId: data.studentId },
32479
- score: 0,
32480
- scoreDate: new Date().toISOString(),
32481
- scoreStatus: SCORE_STATUS.notSubmitted,
32482
- inProgress: "true",
32483
- metadata: {
32484
- isMasteryCompletion: true,
32485
- adminAction: true,
32486
- appName
32487
- }
32488
- });
32489
- } catch {
32490
- logger17.debug("No completion entry to revoke", {
32491
- studentId: data.studentId,
32492
- courseId: data.courseId
32493
- });
32494
- }
32495
- }
32549
+ await upsertMasteryCompletionEntry({
32550
+ client,
32551
+ courseId: data.courseId,
32552
+ studentId: data.studentId,
32553
+ appName,
32554
+ action: willBeMastered ? "complete" : "revoke"
32555
+ });
32496
32556
  }
32497
32557
  }
32498
32558
  return { status: "ok" };
32499
32559
  }
32560
+ async reconcileMasteryForConfigChange(gameId, courseId, user, context) {
32561
+ const { client, appName } = await this.resolveAdminMutationContext(gameId, courseId, user);
32562
+ const action = context.newMasterableUnits < context.oldMasterableUnits ? "complete" : "revoke";
32563
+ const failed = [];
32564
+ let processed = 0;
32565
+ await TimebackAdminService.runWithConcurrency(context.affectedStudentIds, 8, async (studentId) => {
32566
+ try {
32567
+ await upsertMasteryCompletionEntry({
32568
+ client,
32569
+ courseId,
32570
+ studentId,
32571
+ appName,
32572
+ action
32573
+ });
32574
+ processed++;
32575
+ } catch (error) {
32576
+ logger18.warn("Failed to reconcile mastery completion for student", {
32577
+ studentId,
32578
+ courseId,
32579
+ action,
32580
+ error: error instanceof Error ? error.message : String(error)
32581
+ });
32582
+ failed.push(studentId);
32583
+ }
32584
+ });
32585
+ return { processed, failed };
32586
+ }
32500
32587
  async searchStudentsForEnrollment(gameId, courseId, query, user) {
32501
32588
  const client = this.requireClient();
32502
32589
  await this.deps.validateGameManagementAccess(user, gameId);
@@ -32523,7 +32610,7 @@ class TimebackAdminService {
32523
32610
  const response = await client["request"](endpoint, "GET");
32524
32611
  allUsers = response.users || [];
32525
32612
  } catch (error) {
32526
- logger17.warn("Failed to search OneRoster users", {
32613
+ logger18.warn("Failed to search OneRoster users", {
32527
32614
  query: trimmedQuery,
32528
32615
  error: error instanceof Error ? error.message : String(error)
32529
32616
  });
@@ -32668,7 +32755,7 @@ class TimebackAdminService {
32668
32755
  return results;
32669
32756
  }
32670
32757
  }
32671
- var logger17;
32758
+ var logger18;
32672
32759
  var init_timeback_admin_service = __esm(() => {
32673
32760
  init_drizzle_orm();
32674
32761
  init_src();
@@ -32681,8 +32768,9 @@ var init_timeback_admin_service = __esm(() => {
32681
32768
  init_errors();
32682
32769
  init_timeback_admin_metrics_util();
32683
32770
  init_timeback_admin_util();
32771
+ init_timeback_mastery_completion_util();
32684
32772
  init_timeback_util();
32685
- logger17 = log.scope("TimebackAdminService");
32773
+ logger18 = log.scope("TimebackAdminService");
32686
32774
  });
32687
32775
 
32688
32776
  // ../timeback/dist/errors.js
@@ -32929,7 +33017,7 @@ class TimebackAssessmentsService {
32929
33017
  isActive: row.bankActive
32930
33018
  };
32931
33019
  } catch {
32932
- logger18.warn("Failed to fetch QTI test metadata", {
33020
+ logger19.warn("Failed to fetch QTI test metadata", {
32933
33021
  identifier: row.qtiTestIdentifier
32934
33022
  });
32935
33023
  return {
@@ -32983,7 +33071,7 @@ class TimebackAssessmentsService {
32983
33071
  });
32984
33072
  } catch (error) {
32985
33073
  if (error instanceof TimebackApiError && error.status === 409) {
32986
- logger18.info("QTI test already exists (idempotent retry)", {
33074
+ logger19.info("QTI test already exists (idempotent retry)", {
32987
33075
  qtiTestIdentifier: input.qtiTestIdentifier
32988
33076
  });
32989
33077
  } else {
@@ -32996,7 +33084,7 @@ class TimebackAssessmentsService {
32996
33084
  qtiTestIdentifier: input.qtiTestIdentifier,
32997
33085
  sortOrder: maxSortOrder + 1
32998
33086
  }).returning();
32999
- logger18.info("Assessment created", {
33087
+ logger19.info("Assessment created", {
33000
33088
  integrationId,
33001
33089
  qtiTestIdentifier: input.qtiTestIdentifier
33002
33090
  });
@@ -33018,13 +33106,13 @@ class TimebackAssessmentsService {
33018
33106
  }
33019
33107
  await client.qti.tests.delete(qtiTestIdentifier);
33020
33108
  } catch (error) {
33021
- logger18.warn("Partial QTI cleanup during assessment deletion", {
33109
+ logger19.warn("Partial QTI cleanup during assessment deletion", {
33022
33110
  qtiTestIdentifier,
33023
33111
  error: error instanceof Error ? error.message : String(error)
33024
33112
  });
33025
33113
  }
33026
33114
  await this.deps.db.delete(gameTimebackAssessmentTests).where(eq(gameTimebackAssessmentTests.id, row.id));
33027
- logger18.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33115
+ logger19.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33028
33116
  }
33029
33117
  async reorderAssessments(integrationId, identifiers) {
33030
33118
  await this.requireIntegration(integrationId);
@@ -33069,7 +33157,7 @@ class TimebackAssessmentsService {
33069
33157
  return client.qti.tests.reorderItems(qtiTestIdentifier, partId, sectionId, items2);
33070
33158
  }
33071
33159
  async activateAssessment(integrationId, qtiTestIdentifier) {
33072
- logger18.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33160
+ logger19.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33073
33161
  const client = this.requireClient();
33074
33162
  const integration = await this.requireIntegration(integrationId);
33075
33163
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33107,7 +33195,7 @@ class TimebackAssessmentsService {
33107
33195
  });
33108
33196
  }
33109
33197
  } catch {
33110
- logger18.warn("Failed to reactivate existing child resource, will create new", {
33198
+ logger19.warn("Failed to reactivate existing child resource, will create new", {
33111
33199
  childResourceId
33112
33200
  });
33113
33201
  childResourceId = null;
@@ -33120,7 +33208,7 @@ class TimebackAssessmentsService {
33120
33208
  const resourceUrl = resource.metadata?.url;
33121
33209
  if (resourceUrl === qtiTestUrl) {
33122
33210
  childResourceId = resourceId;
33123
- logger18.info("Found existing child Resource for QTI test (idempotent retry)", {
33211
+ logger19.info("Found existing child Resource for QTI test (idempotent retry)", {
33124
33212
  qtiTestIdentifier,
33125
33213
  childResourceId
33126
33214
  });
@@ -33129,7 +33217,7 @@ class TimebackAssessmentsService {
33129
33217
  } catch {}
33130
33218
  }
33131
33219
  if (!childResourceId) {
33132
- logger18.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33220
+ logger19.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33133
33221
  const childResult = await client.oneroster.resources.create({
33134
33222
  resource: {
33135
33223
  status: "active",
@@ -33157,10 +33245,10 @@ class TimebackAssessmentsService {
33157
33245
  }
33158
33246
  }
33159
33247
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: childResourceId, bankActive: true }).where(eq(gameTimebackAssessmentTests.id, row.id));
33160
- logger18.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
33248
+ logger19.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
33161
33249
  }
33162
33250
  async deactivateAssessment(integrationId, qtiTestIdentifier) {
33163
- logger18.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33251
+ logger19.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33164
33252
  const client = this.requireClient();
33165
33253
  const integration = await this.requireIntegration(integrationId);
33166
33254
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33173,7 +33261,7 @@ class TimebackAssessmentsService {
33173
33261
  const childResourceId = row.bankResourceId;
33174
33262
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33175
33263
  try {
33176
- logger18.debug("Reading parent resource for deactivation", {
33264
+ logger19.debug("Reading parent resource for deactivation", {
33177
33265
  resourceId: bankIds.resource
33178
33266
  });
33179
33267
  const parentResource = await client.oneroster.resources.get(bankIds.resource);
@@ -33192,22 +33280,22 @@ class TimebackAssessmentsService {
33192
33280
  });
33193
33281
  }
33194
33282
  } catch (error) {
33195
- logger18.warn("Failed to update parent resource during deactivation", {
33283
+ logger19.warn("Failed to update parent resource during deactivation", {
33196
33284
  bankResourceId: bankIds.resource,
33197
33285
  error: error instanceof Error ? error.message : String(error)
33198
33286
  });
33199
33287
  }
33200
33288
  try {
33201
- logger18.debug("Deleting child resource", { childResourceId });
33289
+ logger19.debug("Deleting child resource", { childResourceId });
33202
33290
  await client.oneroster.resources.delete(childResourceId);
33203
33291
  } catch (error) {
33204
- logger18.warn("Failed to delete child resource (may already be deleted)", {
33292
+ logger19.warn("Failed to delete child resource (may already be deleted)", {
33205
33293
  childResourceId,
33206
33294
  error: error instanceof Error ? error.message : String(error)
33207
33295
  });
33208
33296
  }
33209
33297
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankActive: false }).where(eq(gameTimebackAssessmentTests.id, row.id));
33210
- logger18.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33298
+ logger19.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33211
33299
  }
33212
33300
  isAssessmentActive(row) {
33213
33301
  return row.bankActive;
@@ -33238,14 +33326,14 @@ class TimebackAssessmentsService {
33238
33326
  status: "tobedeleted"
33239
33327
  });
33240
33328
  } catch (error) {
33241
- logger18.warn("Failed to delete component resource", { error });
33329
+ logger19.warn("Failed to delete component resource", { error });
33242
33330
  }
33243
33331
  for (const row of activeRows) {
33244
33332
  if (row.bankResourceId) {
33245
33333
  try {
33246
33334
  await client.oneroster.resources.delete(row.bankResourceId);
33247
33335
  } catch (error) {
33248
- logger18.warn("Failed to delete child resource", {
33336
+ logger19.warn("Failed to delete child resource", {
33249
33337
  childResourceId: row.bankResourceId,
33250
33338
  error
33251
33339
  });
@@ -33255,17 +33343,17 @@ class TimebackAssessmentsService {
33255
33343
  try {
33256
33344
  await client.oneroster.resources.delete(bankIds.resource);
33257
33345
  } catch (error) {
33258
- logger18.warn("Failed to delete parent resource", { error });
33346
+ logger19.warn("Failed to delete parent resource", { error });
33259
33347
  }
33260
33348
  try {
33261
33349
  await client.oneroster.courseComponents.update(bankIds.component, {
33262
33350
  status: "tobedeleted"
33263
33351
  });
33264
33352
  } catch (error) {
33265
- logger18.warn("Failed to delete course component", { error });
33353
+ logger19.warn("Failed to delete course component", { error });
33266
33354
  }
33267
33355
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: null, bankActive: false }).where(eq(gameTimebackAssessmentTests.integrationId, integrationId));
33268
- logger18.info("Bank destroyed", { integrationId });
33356
+ logger19.info("Bank destroyed", { integrationId });
33269
33357
  }
33270
33358
  requireClient() {
33271
33359
  if (!this.deps.timeback) {
@@ -33294,7 +33382,7 @@ class TimebackAssessmentsService {
33294
33382
  async ensureBank(integration) {
33295
33383
  const client = this.requireClient();
33296
33384
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33297
- logger18.debug("Ensuring assessment bank hierarchy", {
33385
+ logger19.debug("Ensuring assessment bank hierarchy", {
33298
33386
  courseId: integration.courseId,
33299
33387
  bankIds
33300
33388
  });
@@ -33311,7 +33399,7 @@ class TimebackAssessmentsService {
33311
33399
  });
33312
33400
  } catch (error) {
33313
33401
  if (error instanceof TimebackApiError && error.status === 409) {
33314
- logger18.debug("Course component already exists", { sourcedId: bankIds.component });
33402
+ logger19.debug("Course component already exists", { sourcedId: bankIds.component });
33315
33403
  } else {
33316
33404
  throw error;
33317
33405
  }
@@ -33335,7 +33423,7 @@ class TimebackAssessmentsService {
33335
33423
  });
33336
33424
  } catch (error) {
33337
33425
  if (error instanceof TimebackApiError && error.status === 409) {
33338
- logger18.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33426
+ logger19.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33339
33427
  } else {
33340
33428
  throw error;
33341
33429
  }
@@ -33355,14 +33443,14 @@ class TimebackAssessmentsService {
33355
33443
  });
33356
33444
  } catch (error) {
33357
33445
  if (error instanceof TimebackApiError && error.status === 409) {
33358
- logger18.debug("Component resource already exists", {
33446
+ logger19.debug("Component resource already exists", {
33359
33447
  sourcedId: bankIds.componentResource
33360
33448
  });
33361
33449
  } else {
33362
33450
  throw error;
33363
33451
  }
33364
33452
  }
33365
- logger18.info("Assessment bank hierarchy created", {
33453
+ logger19.info("Assessment bank hierarchy created", {
33366
33454
  courseId: integration.courseId
33367
33455
  });
33368
33456
  }
@@ -33377,7 +33465,7 @@ class TimebackAssessmentsService {
33377
33465
  return Math.max(...rows.map((r) => r.sortOrder));
33378
33466
  }
33379
33467
  }
33380
- var logger18;
33468
+ var logger19;
33381
33469
  var init_timeback_assessments_service = __esm(() => {
33382
33470
  init_drizzle_orm();
33383
33471
  init_tables_index();
@@ -33386,7 +33474,7 @@ var init_timeback_assessments_service = __esm(() => {
33386
33474
  init_errors4();
33387
33475
  init_utils6();
33388
33476
  init_errors();
33389
- logger18 = log.scope("TimebackAssessmentsService");
33477
+ logger19 = log.scope("TimebackAssessmentsService");
33390
33478
  });
33391
33479
 
33392
33480
  // ../api-core/src/utils/timeback-promotion.util.ts
@@ -33402,7 +33490,7 @@ async function promoteCompletedCourse({
33402
33490
  });
33403
33491
  const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
33404
33492
  if (!nextIntegration) {
33405
- logger19.debug("Skipping promotion because no next course is configured", {
33493
+ logger20.debug("Skipping promotion because no next course is configured", {
33406
33494
  gameId: currentIntegration.gameId,
33407
33495
  studentId,
33408
33496
  grade: currentIntegration.grade,
@@ -33419,7 +33507,7 @@ async function promoteCompletedCourse({
33419
33507
  const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
33420
33508
  if (!currentEnrollment) {
33421
33509
  if (nextEnrollment) {
33422
- logger19.debug("Skipping promotion because student is already on the next course", {
33510
+ logger20.debug("Skipping promotion because student is already on the next course", {
33423
33511
  gameId: currentIntegration.gameId,
33424
33512
  studentId,
33425
33513
  grade: currentIntegration.grade,
@@ -33433,7 +33521,7 @@ async function promoteCompletedCourse({
33433
33521
  nextCourseId: nextIntegration.courseId
33434
33522
  };
33435
33523
  }
33436
- logger19.debug("Skipping promotion because student is not enrolled in the current course", {
33524
+ logger20.debug("Skipping promotion because student is not enrolled in the current course", {
33437
33525
  gameId: currentIntegration.gameId,
33438
33526
  studentId,
33439
33527
  grade: currentIntegration.grade,
@@ -33464,7 +33552,7 @@ async function promoteCompletedCourse({
33464
33552
  client.invalidateEnrollments(studentId);
33465
33553
  }
33466
33554
  }
33467
- logger19.info("Promoted student to next course", {
33555
+ logger20.info("Promoted student to next course", {
33468
33556
  gameId: currentIntegration.gameId,
33469
33557
  studentId,
33470
33558
  subject: currentIntegration.subject,
@@ -33479,16 +33567,16 @@ async function promoteCompletedCourse({
33479
33567
  nextCourseId: nextIntegration.courseId
33480
33568
  };
33481
33569
  }
33482
- var logger19;
33570
+ var logger20;
33483
33571
  var init_timeback_promotion_util = __esm(() => {
33484
33572
  init_drizzle_orm();
33485
33573
  init_tables_index();
33486
33574
  init_src2();
33487
- logger19 = log.scope("TimebackPromotion");
33575
+ logger20 = log.scope("TimebackPromotion");
33488
33576
  });
33489
33577
 
33490
33578
  // ../api-core/src/services/timeback.service.ts
33491
- var logger20, TimebackService;
33579
+ var logger21, TimebackService;
33492
33580
  var init_timeback_service = __esm(() => {
33493
33581
  init_drizzle_orm();
33494
33582
  init_src();
@@ -33499,7 +33587,7 @@ var init_timeback_service = __esm(() => {
33499
33587
  init_errors();
33500
33588
  init_timeback_promotion_util();
33501
33589
  init_timeback_util();
33502
- logger20 = log.scope("TimebackService");
33590
+ logger21 = log.scope("TimebackService");
33503
33591
  TimebackService = class TimebackService {
33504
33592
  static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
33505
33593
  static processedHeartbeatWindows = new Map;
@@ -33545,7 +33633,7 @@ var init_timeback_service = __esm(() => {
33545
33633
  }
33546
33634
  requireClient() {
33547
33635
  if (!this.deps.timeback) {
33548
- logger20.error("Timeback client not available in context");
33636
+ logger21.error("Timeback client not available in context");
33549
33637
  throw new ValidationError("Timeback integration not available in this environment");
33550
33638
  }
33551
33639
  return this.deps.timeback;
@@ -33598,7 +33686,7 @@ var init_timeback_service = __esm(() => {
33598
33686
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
33599
33687
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
33600
33688
  if (!result) {
33601
- logger20.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33689
+ logger21.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33602
33690
  throw new InternalError("Failed to update daily XP record");
33603
33691
  }
33604
33692
  return { xp: result.xp, date: result.date.toISOString() };
@@ -33629,7 +33717,7 @@ var init_timeback_service = __esm(() => {
33629
33717
  columns: { id: true, timebackId: true }
33630
33718
  });
33631
33719
  if (dbUser?.timebackId) {
33632
- logger20.info("Student already onboarded", { userId: user.id });
33720
+ logger21.info("Student already onboarded", { userId: user.id });
33633
33721
  return { status: "already_populated" };
33634
33722
  }
33635
33723
  let timebackId;
@@ -33638,7 +33726,7 @@ var init_timeback_service = __esm(() => {
33638
33726
  const existingUser = await client.oneroster.users.findByEmail(user.email);
33639
33727
  timebackId = existingUser.sourcedId;
33640
33728
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
33641
- logger20.info("Found existing student in OneRoster", {
33729
+ logger21.info("Found existing student in OneRoster", {
33642
33730
  userId: user.id,
33643
33731
  timebackId
33644
33732
  });
@@ -33667,7 +33755,7 @@ var init_timeback_service = __esm(() => {
33667
33755
  }
33668
33756
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
33669
33757
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
33670
- logger20.info("Created student in OneRoster", { userId: user.id, timebackId });
33758
+ logger21.info("Created student in OneRoster", { userId: user.id, timebackId });
33671
33759
  }
33672
33760
  const assessments = await this.fetchAssessments(timebackId);
33673
33761
  await db2.transaction(async (tx) => {
@@ -33701,7 +33789,7 @@ var init_timeback_service = __esm(() => {
33701
33789
  }
33702
33790
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
33703
33791
  if (!updated) {
33704
- logger20.error("User Timeback ID update returned no rows", {
33792
+ logger21.error("User Timeback ID update returned no rows", {
33705
33793
  userId: user.id,
33706
33794
  timebackId
33707
33795
  });
@@ -33725,13 +33813,13 @@ var init_timeback_service = __esm(() => {
33725
33813
  }
33726
33814
  offset += limit;
33727
33815
  }
33728
- logger20.debug("Fetched assessments", {
33816
+ logger21.debug("Fetched assessments", {
33729
33817
  studentSourcedId,
33730
33818
  totalCount: allAssessments.length
33731
33819
  });
33732
33820
  return allAssessments;
33733
33821
  } catch (error) {
33734
- logger20.warn("Failed to fetch assessments", { studentSourcedId, error });
33822
+ logger21.warn("Failed to fetch assessments", { studentSourcedId, error });
33735
33823
  return [];
33736
33824
  }
33737
33825
  }
@@ -33844,7 +33932,7 @@ var init_timeback_service = __esm(() => {
33844
33932
  masterableUnits: derivedMasterableUnits
33845
33933
  } = courseConfig;
33846
33934
  if (!isTimebackSubject(subjectInput)) {
33847
- logger20.warn("Invalid Timeback subject in course config", {
33935
+ logger21.warn("Invalid Timeback subject in course config", {
33848
33936
  subject: subjectInput,
33849
33937
  courseCode,
33850
33938
  title
@@ -33852,7 +33940,7 @@ var init_timeback_service = __esm(() => {
33852
33940
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
33853
33941
  }
33854
33942
  if (!isTimebackGrade(grade)) {
33855
- logger20.warn("Invalid Timeback grade in course config", {
33943
+ logger21.warn("Invalid Timeback grade in course config", {
33856
33944
  grade,
33857
33945
  courseCode,
33858
33946
  title
@@ -33864,7 +33952,7 @@ var init_timeback_service = __esm(() => {
33864
33952
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
33865
33953
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
33866
33954
  if (typeof totalXp !== "number") {
33867
- logger20.warn("Course missing totalXp in Timeback config", {
33955
+ logger21.warn("Course missing totalXp in Timeback config", {
33868
33956
  courseCode,
33869
33957
  title
33870
33958
  });
@@ -33880,7 +33968,9 @@ var init_timeback_service = __esm(() => {
33880
33968
  courseCode,
33881
33969
  level,
33882
33970
  gradingScheme: "STANDARD",
33883
- metadata: metadata2
33971
+ metadata: TimebackService.patchCourseMetadata(metadata2, totalXp, {
33972
+ masterableUnits: masterableUnits ?? null
33973
+ })
33884
33974
  },
33885
33975
  component: {
33886
33976
  ...baseConfig.component,
@@ -33905,7 +33995,7 @@ var init_timeback_service = __esm(() => {
33905
33995
  const existingIntegration = existing.find((i2) => i2.grade === grade && i2.subject === subject);
33906
33996
  if (existingIntegration) {
33907
33997
  await client.update(existingIntegration.courseId, fullConfig);
33908
- const [updated] = await db2.update(gameTimebackIntegrations).set({ totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
33998
+ const [updated] = await db2.update(gameTimebackIntegrations).set({ subject, totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
33909
33999
  if (updated) {
33910
34000
  integrations.push(this.toGameTimebackIntegration(updated));
33911
34001
  }
@@ -33930,6 +34020,60 @@ var init_timeback_service = __esm(() => {
33930
34020
  });
33931
34021
  return rows.map((row) => this.toGameTimebackIntegration(row));
33932
34022
  }
34023
+ async getIntegrationConfig(gameId, courseId, user) {
34024
+ const client = this.requireClient();
34025
+ await this.deps.validateGameManagementAccess(user, gameId);
34026
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34027
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
34028
+ });
34029
+ if (!integration) {
34030
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34031
+ }
34032
+ const config2 = await client.getConfig(courseId);
34033
+ return this.toGameTimebackIntegrationConfig(integration, config2);
34034
+ }
34035
+ async updateIntegration(gameId, courseId, user, patch) {
34036
+ const client = this.requireClient();
34037
+ await this.deps.validateDeveloperAccess(user, gameId);
34038
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34039
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
34040
+ });
34041
+ if (!integration) {
34042
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34043
+ }
34044
+ const timebackConfig = await client.getConfig(courseId);
34045
+ const liveSubject = timebackConfig.course.subjects[0];
34046
+ const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
34047
+ if (!isTimebackSubject(subject)) {
34048
+ throw new ValidationError(`Invalid subject "${subject}"`);
34049
+ }
34050
+ if (subject !== integration.subject) {
34051
+ const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34052
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
34053
+ });
34054
+ if (subjectConflict && subjectConflict.id !== integration.id) {
34055
+ throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
34056
+ }
34057
+ }
34058
+ const totalXp = "totalXp" in patch ? patch.totalXp ?? null : TimebackService.getTotalXpFromConfig(timebackConfig) ?? integration.totalXp ?? null;
34059
+ const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : TimebackService.getMasterableUnitsFromConfig(timebackConfig);
34060
+ await client.update(courseId, TimebackService.patchTimebackConfig(timebackConfig, {
34061
+ ...patch,
34062
+ subject,
34063
+ totalXp,
34064
+ masterableUnits,
34065
+ grade: integration.grade
34066
+ }));
34067
+ const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
34068
+ subject,
34069
+ totalXp,
34070
+ updatedAt: new Date
34071
+ }).where(eq(gameTimebackIntegrations.id, integration.id)).returning();
34072
+ if (!updated) {
34073
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34074
+ }
34075
+ return this.toGameTimebackIntegration(updated);
34076
+ }
33933
34077
  async verifyIntegration(gameId, user) {
33934
34078
  const client = this.requireClient();
33935
34079
  const db2 = this.deps.db;
@@ -33987,6 +34131,146 @@ var init_timeback_service = __esm(() => {
33987
34131
  }
33988
34132
  await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
33989
34133
  }
34134
+ static getMasterableUnitsFromConfig(config2) {
34135
+ const playcademyMetadata = config2.resource.metadata?.playcademy;
34136
+ if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
34137
+ return null;
34138
+ }
34139
+ return playcademyMetadata?.mastery?.masterableUnits ?? null;
34140
+ }
34141
+ static getTotalXpFromConfig(config2) {
34142
+ const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
34143
+ if (typeof courseMetadata?.metrics?.totalXp === "number") {
34144
+ return courseMetadata.metrics.totalXp;
34145
+ }
34146
+ const resourceMetadata = config2.resource.metadata;
34147
+ if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
34148
+ return resourceMetadata.xp;
34149
+ }
34150
+ return null;
34151
+ }
34152
+ static patchCourseMetadata(metadata2, totalXp, options) {
34153
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34154
+ const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
34155
+ const metrics = { ...currentMetrics };
34156
+ if (totalXp === null) {
34157
+ delete metrics.totalXp;
34158
+ } else {
34159
+ metrics.totalXp = totalXp;
34160
+ }
34161
+ if (options?.masterableUnits !== undefined) {
34162
+ if (options.masterableUnits === null) {
34163
+ delete metrics.totalLessons;
34164
+ } else {
34165
+ metrics.totalLessons = options.masterableUnits;
34166
+ }
34167
+ }
34168
+ metrics.totalGrades = 1;
34169
+ if (Object.keys(metrics).length > 0) {
34170
+ nextMetadata.metrics = metrics;
34171
+ } else {
34172
+ delete nextMetadata.metrics;
34173
+ }
34174
+ const goals = options?.goals;
34175
+ if (goals !== undefined) {
34176
+ if (goals === null) {
34177
+ delete nextMetadata.goals;
34178
+ } else {
34179
+ const currentGoals = isRecord2(nextMetadata.goals) ? nextMetadata.goals : {};
34180
+ const nextGoals = { ...currentGoals };
34181
+ for (const [key, value] of Object.entries(goals)) {
34182
+ if (value === null) {
34183
+ delete nextGoals[key];
34184
+ } else if (value !== undefined) {
34185
+ nextGoals[key] = value;
34186
+ }
34187
+ }
34188
+ if (Object.keys(nextGoals).length > 0) {
34189
+ nextMetadata.goals = nextGoals;
34190
+ } else {
34191
+ delete nextMetadata.goals;
34192
+ }
34193
+ }
34194
+ }
34195
+ if (options?.publishStatus !== undefined) {
34196
+ if (options.publishStatus === null) {
34197
+ delete nextMetadata.publishStatus;
34198
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34199
+ delete alphaLearn.publishStatus;
34200
+ if (Object.keys(alphaLearn).length > 0) {
34201
+ nextMetadata.AlphaLearn = alphaLearn;
34202
+ } else {
34203
+ delete nextMetadata.AlphaLearn;
34204
+ }
34205
+ } else {
34206
+ nextMetadata.publishStatus = options.publishStatus;
34207
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34208
+ alphaLearn.publishStatus = options.publishStatus === "published" ? "active" : options.publishStatus;
34209
+ nextMetadata.AlphaLearn = alphaLearn;
34210
+ }
34211
+ }
34212
+ if (options?.isSupplemental !== undefined) {
34213
+ nextMetadata.isSupplemental = options.isSupplemental;
34214
+ }
34215
+ if (options?.timebackVisible !== undefined) {
34216
+ if (options.timebackVisible === null) {
34217
+ delete nextMetadata.timebackVisible;
34218
+ } else {
34219
+ nextMetadata.timebackVisible = options.timebackVisible;
34220
+ }
34221
+ }
34222
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
34223
+ }
34224
+ static patchResourceMetadata(metadata2, options) {
34225
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34226
+ const playcademyMetadata = isRecord2(nextMetadata.playcademy) ? { ...nextMetadata.playcademy } : {};
34227
+ const masteryMetadata = isRecord2(playcademyMetadata.mastery) ? { ...playcademyMetadata.mastery } : {};
34228
+ nextMetadata.subject = options.subject;
34229
+ nextMetadata.grades = [options.grade];
34230
+ if (options.totalXp === null) {
34231
+ delete nextMetadata.xp;
34232
+ } else {
34233
+ nextMetadata.xp = options.totalXp;
34234
+ }
34235
+ if (options.masterableUnits === null) {
34236
+ delete masteryMetadata.masterableUnits;
34237
+ } else {
34238
+ masteryMetadata.masterableUnits = options.masterableUnits;
34239
+ }
34240
+ if (Object.keys(masteryMetadata).length > 0) {
34241
+ playcademyMetadata.mastery = masteryMetadata;
34242
+ } else {
34243
+ delete playcademyMetadata.mastery;
34244
+ }
34245
+ if (Object.keys(playcademyMetadata).length > 0) {
34246
+ nextMetadata.playcademy = playcademyMetadata;
34247
+ } else {
34248
+ delete nextMetadata.playcademy;
34249
+ }
34250
+ return nextMetadata;
34251
+ }
34252
+ static patchTimebackConfig(config2, patch) {
34253
+ return {
34254
+ ...config2,
34255
+ course: {
34256
+ ...config2.course,
34257
+ title: patch.title ?? config2.course.title,
34258
+ courseCode: patch.courseCode ?? config2.course.courseCode,
34259
+ subjects: [patch.subject],
34260
+ metadata: TimebackService.patchCourseMetadata(config2.course.metadata, patch.totalXp, {
34261
+ masterableUnits: patch.masterableUnits,
34262
+ goals: patch.goals,
34263
+ publishStatus: patch.publishStatus,
34264
+ isSupplemental: patch.isSupplemental,
34265
+ timebackVisible: patch.timebackVisible
34266
+ })
34267
+ },
34268
+ resource: {
34269
+ ...config2.resource,
34270
+ metadata: TimebackService.patchResourceMetadata(config2.resource.metadata, patch)
34271
+ }
34272
+ };
34273
+ }
33990
34274
  toGameTimebackIntegration(integration) {
33991
34275
  return {
33992
34276
  id: integration.id,
@@ -34000,6 +34284,21 @@ var init_timeback_service = __esm(() => {
34000
34284
  lastVerifiedAt: integration.lastVerifiedAt ?? null
34001
34285
  };
34002
34286
  }
34287
+ toGameTimebackIntegrationConfig(integration, config2) {
34288
+ const subject = config2.course.subjects[0] ?? integration.subject;
34289
+ if (!isTimebackSubject(subject)) {
34290
+ throw new ValidationError(`Invalid subject "${subject}"`);
34291
+ }
34292
+ return {
34293
+ integration: this.toGameTimebackIntegration(integration),
34294
+ title: config2.course.title,
34295
+ courseCode: config2.course.courseCode,
34296
+ subject,
34297
+ totalXp: TimebackService.getTotalXpFromConfig(config2) ?? integration.totalXp ?? null,
34298
+ masterableUnits: TimebackService.getMasterableUnitsFromConfig(config2),
34299
+ metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
34300
+ };
34301
+ }
34003
34302
  async endActivity({
34004
34303
  gameId,
34005
34304
  studentId,
@@ -34065,7 +34364,7 @@ var init_timeback_service = __esm(() => {
34065
34364
  ...runId ? { runId } : {}
34066
34365
  });
34067
34366
  }
34068
- logger20.info("Recorded activity completion", {
34367
+ logger21.info("Recorded activity completion", {
34069
34368
  gameId,
34070
34369
  courseId: integration.courseId,
34071
34370
  studentId,
@@ -34119,7 +34418,7 @@ var init_timeback_service = __esm(() => {
34119
34418
  masteredUnits: masteryStatus.masteredUnits,
34120
34419
  masterableUnits: masteryStatus.masterableUnits
34121
34420
  };
34122
- logger20.debug("Skipping course advancement because mastery is incomplete", {
34421
+ logger21.debug("Skipping course advancement because mastery is incomplete", {
34123
34422
  gameId,
34124
34423
  studentId,
34125
34424
  subject: currentIntegration.subject,
@@ -34137,7 +34436,7 @@ var init_timeback_service = __esm(() => {
34137
34436
  studentId,
34138
34437
  enrollments
34139
34438
  });
34140
- logger20.info("Manually advanced student", {
34439
+ logger21.info("Manually advanced student", {
34141
34440
  gameId,
34142
34441
  studentId,
34143
34442
  subject: currentIntegration.subject,
@@ -34170,7 +34469,7 @@ var init_timeback_service = __esm(() => {
34170
34469
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
34171
34470
  const effectiveResumeId = resumeId ?? runId;
34172
34471
  if (TimebackService.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
34173
- logger20.debug("Skipping duplicate heartbeat window", {
34472
+ logger21.debug("Skipping duplicate heartbeat window", {
34174
34473
  gameId,
34175
34474
  studentId,
34176
34475
  runId,
@@ -34183,7 +34482,7 @@ var init_timeback_service = __esm(() => {
34183
34482
  await this.deps.validateDeveloperAccess(user, gameId);
34184
34483
  const inFlightHeartbeat = TimebackService.getInFlightHeartbeatWindow(heartbeatWindowKey);
34185
34484
  if (inFlightHeartbeat) {
34186
- logger20.debug("Joining in-flight heartbeat window", {
34485
+ logger21.debug("Joining in-flight heartbeat window", {
34187
34486
  gameId,
34188
34487
  studentId,
34189
34488
  runId,
@@ -34220,7 +34519,7 @@ var init_timeback_service = __esm(() => {
34220
34519
  });
34221
34520
  }
34222
34521
  TimebackService.markHeartbeatWindowProcessed(heartbeatWindowKey);
34223
- logger20.debug("Recorded heartbeat", {
34522
+ logger21.debug("Recorded heartbeat", {
34224
34523
  gameId,
34225
34524
  courseId: integration.courseId,
34226
34525
  studentId,
@@ -34255,7 +34554,7 @@ var init_timeback_service = __esm(() => {
34255
34554
  });
34256
34555
  courseIds = integrations.map((i2) => i2.courseId);
34257
34556
  if (courseIds.length === 0) {
34258
- logger20.debug("No integrations found for game, returning 0 XP", {
34557
+ logger21.debug("No integrations found for game, returning 0 XP", {
34259
34558
  timebackId,
34260
34559
  gameId: options.gameId,
34261
34560
  grade: options.grade,
@@ -34272,7 +34571,7 @@ var init_timeback_service = __esm(() => {
34272
34571
  courseIds: courseIds.length > 0 ? courseIds : undefined,
34273
34572
  include: options?.include
34274
34573
  });
34275
- logger20.debug("Retrieved student XP", {
34574
+ logger21.debug("Retrieved student XP", {
34276
34575
  timebackId,
34277
34576
  gameId: options?.gameId,
34278
34577
  grade: options?.grade,
@@ -34301,15 +34600,15 @@ class UploadService {
34301
34600
  const { fileName, gameId } = request;
34302
34601
  const bucketName = this.deps.uploadBucket;
34303
34602
  if (!bucketName) {
34304
- logger21.error("Upload bucket not configured in environment");
34603
+ logger22.error("Upload bucket not configured in environment");
34305
34604
  throw new ValidationError("Upload bucket not configured");
34306
34605
  }
34307
34606
  await this.deps.validateDeveloperAccess(user, gameId);
34308
34607
  const version2 = ulid();
34309
34608
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
34310
- logger21.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34609
+ logger22.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34311
34610
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
34312
- logger21.info("Presigned URL generated", {
34611
+ logger22.info("Presigned URL generated", {
34313
34612
  userId: user.id,
34314
34613
  gameId,
34315
34614
  version: version2
@@ -34322,12 +34621,12 @@ class UploadService {
34322
34621
  };
34323
34622
  }
34324
34623
  }
34325
- var logger21;
34624
+ var logger22;
34326
34625
  var init_upload_service = __esm(() => {
34327
34626
  init_node();
34328
34627
  init_src2();
34329
34628
  init_errors();
34330
- logger21 = log.scope("UploadService");
34629
+ logger22 = log.scope("UploadService");
34331
34630
  });
34332
34631
 
34333
34632
  // ../api-core/src/services/factory/platform.ts
@@ -34641,7 +34940,7 @@ class AchievementService {
34641
34940
  results.push(result);
34642
34941
  }
34643
34942
  }
34644
- logger22.debug("Listed current achievements", { userId: user.id, count: results.length });
34943
+ logger23.debug("Listed current achievements", { userId: user.id, count: results.length });
34645
34944
  return results;
34646
34945
  }
34647
34946
  async listHistory(user, limit) {
@@ -34659,14 +34958,14 @@ class AchievementService {
34659
34958
  createdAt: c.createdAt,
34660
34959
  scopeKey: c.scopeKey
34661
34960
  }));
34662
- logger22.debug("Listed achievement history", { userId: user.id, count: results.length });
34961
+ logger23.debug("Listed achievement history", { userId: user.id, count: results.length });
34663
34962
  return results;
34664
34963
  }
34665
34964
  async submitProgress(achievementId, user) {
34666
34965
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
34667
34966
  broadcast: false
34668
34967
  });
34669
- logger22.debug("Submitted progress", {
34968
+ logger23.debug("Submitted progress", {
34670
34969
  userId: user.id,
34671
34970
  achievementId,
34672
34971
  wasNewClaim
@@ -34698,7 +34997,7 @@ class AchievementService {
34698
34997
  rewardCredits
34699
34998
  }).returning();
34700
34999
  if (!newClaim) {
34701
- logger22.error("Achievement claim insert returned no rows", {
35000
+ logger23.error("Achievement claim insert returned no rows", {
34702
35001
  userId,
34703
35002
  achievementId,
34704
35003
  scopeKey
@@ -34707,7 +35006,7 @@ class AchievementService {
34707
35006
  }
34708
35007
  await this.deps.addCredits(userId, rewardCredits);
34709
35008
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
34710
- logger22.info("Awarded achievement", {
35009
+ logger23.info("Awarded achievement", {
34711
35010
  userId,
34712
35011
  achievementId,
34713
35012
  scopeKey,
@@ -34744,7 +35043,7 @@ class AchievementService {
34744
35043
  return { title, body: body2 };
34745
35044
  }
34746
35045
  }
34747
- var logger22;
35046
+ var logger23;
34748
35047
  var init_achievement_service = __esm(() => {
34749
35048
  init_drizzle_orm();
34750
35049
  init_tables_index();
@@ -34753,7 +35052,7 @@ var init_achievement_service = __esm(() => {
34753
35052
  init_errors();
34754
35053
  init_leaderboard_util();
34755
35054
  init_scope_util();
34756
- logger22 = log.scope("AchievementService");
35055
+ logger23 = log.scope("AchievementService");
34757
35056
  });
34758
35057
 
34759
35058
  // ../api-core/src/services/inventory.service.ts
@@ -34781,7 +35080,7 @@ class InventoryService {
34781
35080
  },
34782
35081
  updatedAt: inventoryItems.updatedAt
34783
35082
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
34784
- logger23.debug("Listed inventory", { userId: user.id, count: inventory.length });
35083
+ logger24.debug("Listed inventory", { userId: user.id, count: inventory.length });
34785
35084
  return inventory;
34786
35085
  }
34787
35086
  async addItem(itemId, quantity, user) {
@@ -34798,7 +35097,7 @@ class InventoryService {
34798
35097
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
34799
35098
  return inserted?.quantity ?? 0;
34800
35099
  });
34801
- logger23.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
35100
+ logger24.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
34802
35101
  return { newTotal };
34803
35102
  }
34804
35103
  async removeItem(itemId, quantity, user) {
@@ -34809,7 +35108,7 @@ class InventoryService {
34809
35108
  }
34810
35109
  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);
34811
35110
  if (!currentItem) {
34812
- logger23.warn("Insufficient inventory for removal", {
35111
+ logger24.warn("Insufficient inventory for removal", {
34813
35112
  userId: user.id,
34814
35113
  itemId,
34815
35114
  requestedQuantity: quantity
@@ -34819,13 +35118,13 @@ class InventoryService {
34819
35118
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
34820
35119
  return updated?.quantity ?? 0;
34821
35120
  });
34822
- logger23.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
35121
+ logger24.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
34823
35122
  return { newTotal };
34824
35123
  }
34825
35124
  async addCredits(userId, amount) {
34826
35125
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
34827
35126
  if (!creditsItem) {
34828
- logger23.error("Primary currency not found", {
35127
+ logger24.error("Primary currency not found", {
34829
35128
  userId,
34830
35129
  amount
34831
35130
  });
@@ -34838,17 +35137,17 @@ class InventoryService {
34838
35137
  updatedAt: new Date
34839
35138
  }
34840
35139
  });
34841
- logger23.debug("Added credits", { userId, amount });
35140
+ logger24.debug("Added credits", { userId, amount });
34842
35141
  }
34843
35142
  }
34844
- var logger23;
35143
+ var logger24;
34845
35144
  var init_inventory_service = __esm(() => {
34846
35145
  init_drizzle_orm();
34847
35146
  init_src();
34848
35147
  init_tables_index();
34849
35148
  init_src2();
34850
35149
  init_errors();
34851
- logger23 = log.scope("InventoryService");
35150
+ logger24 = log.scope("InventoryService");
34852
35151
  });
34853
35152
 
34854
35153
  // ../api-core/src/services/leaderboard.service.ts
@@ -34880,7 +35179,7 @@ class LeaderboardService {
34880
35179
  sessionId
34881
35180
  }).returning();
34882
35181
  if (!newScore) {
34883
- logger24.error("Score insert returned no rows", { userId, gameId, score: input.score });
35182
+ logger25.error("Score insert returned no rows", { userId, gameId, score: input.score });
34884
35183
  throw new InternalError("Failed to insert score");
34885
35184
  }
34886
35185
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -34908,7 +35207,7 @@ class LeaderboardService {
34908
35207
  movedUpWithinTop3
34909
35208
  });
34910
35209
  }
34911
- logger24.info("Score submitted", {
35210
+ logger25.info("Score submitted", {
34912
35211
  gameId,
34913
35212
  userId,
34914
35213
  isAnonymousUser,
@@ -34985,7 +35284,7 @@ class LeaderboardService {
34985
35284
  });
34986
35285
  }
34987
35286
  } catch (error) {
34988
- logger24.warn("Failed to publish notification", { error });
35287
+ logger25.warn("Failed to publish notification", { error });
34989
35288
  }
34990
35289
  }
34991
35290
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -35104,7 +35403,7 @@ class LeaderboardService {
35104
35403
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
35105
35404
  }
35106
35405
  }
35107
- var logger24;
35406
+ var logger25;
35108
35407
  var init_leaderboard_service = __esm(() => {
35109
35408
  init_drizzle_orm();
35110
35409
  init_src();
@@ -35114,7 +35413,7 @@ var init_leaderboard_service = __esm(() => {
35114
35413
  init_notification();
35115
35414
  init_errors();
35116
35415
  init_leaderboard_util();
35117
- logger24 = log.scope("LeaderboardService");
35416
+ logger25 = log.scope("LeaderboardService");
35118
35417
  });
35119
35418
 
35120
35419
  // ../api-core/src/services/level.service.ts
@@ -35130,9 +35429,9 @@ class LevelService {
35130
35429
  for (const config2 of configs) {
35131
35430
  levelConfigCache.set(config2.level, config2);
35132
35431
  }
35133
- logger25.info("Cache pre-warmed", { count: configs.length });
35432
+ logger26.info("Cache pre-warmed", { count: configs.length });
35134
35433
  } catch (error) {
35135
- logger25.error("Cache pre-warm failed", { error });
35434
+ logger26.error("Cache pre-warm failed", { error });
35136
35435
  }
35137
35436
  }
35138
35437
  async getConfig(level) {
@@ -35166,7 +35465,7 @@ class LevelService {
35166
35465
  totalXP
35167
35466
  }).returning();
35168
35467
  if (!newUserLevel) {
35169
- logger25.error("User level insert returned no rows", { userId: user.id });
35468
+ logger26.error("User level insert returned no rows", { userId: user.id });
35170
35469
  throw new InternalError("Failed to create user level cache record");
35171
35470
  }
35172
35471
  userLevel = newUserLevel;
@@ -35181,7 +35480,7 @@ class LevelService {
35181
35480
  userLevel = updatedUserLevel;
35182
35481
  }
35183
35482
  }
35184
- logger25.debug("Retrieved user level", {
35483
+ logger26.debug("Retrieved user level", {
35185
35484
  userId: user.id,
35186
35485
  totalXP,
35187
35486
  currentLevel,
@@ -35192,7 +35491,7 @@ class LevelService {
35192
35491
  async getProgress(user) {
35193
35492
  const userLevel = await this.getByUser(user);
35194
35493
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
35195
- logger25.debug("Retrieved progress", { userId: user.id });
35494
+ logger26.debug("Retrieved progress", { userId: user.id });
35196
35495
  return {
35197
35496
  level: userLevel.currentLevel,
35198
35497
  currentXp: userLevel.currentXp,
@@ -35226,7 +35525,7 @@ class LevelService {
35226
35525
  if (leveledUp && previousUserLevel) {
35227
35526
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
35228
35527
  }
35229
- logger25.info("Synced from Timeback", {
35528
+ logger26.info("Synced from Timeback", {
35230
35529
  userId,
35231
35530
  totalXP,
35232
35531
  currentLevel,
@@ -35267,7 +35566,7 @@ class LevelService {
35267
35566
  }
35268
35567
  if (totalCredits > 0) {
35269
35568
  await this.deps.addCredits(userId, totalCredits);
35270
- logger25.info("Awarded level-up credits", {
35569
+ logger26.info("Awarded level-up credits", {
35271
35570
  userId,
35272
35571
  fromLevel,
35273
35572
  toLevel,
@@ -35305,14 +35604,14 @@ class LevelService {
35305
35604
  };
35306
35605
  }
35307
35606
  }
35308
- var logger25, levelConfigCache = null;
35607
+ var logger26, levelConfigCache = null;
35309
35608
  var init_level_service = __esm(() => {
35310
35609
  init_drizzle_orm();
35311
35610
  init_src();
35312
35611
  init_tables_index();
35313
35612
  init_src2();
35314
35613
  init_errors();
35315
- logger25 = log.scope("LevelService");
35614
+ logger26 = log.scope("LevelService");
35316
35615
  });
35317
35616
 
35318
35617
  // ../realtime/src/server/domain/events.ts
@@ -35333,13 +35632,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
35333
35632
  });
35334
35633
  if (!res.ok) {
35335
35634
  const text3 = await res.text().catch(() => "");
35336
- logger26.warn("Failed to publish to user", {
35635
+ logger27.warn("Failed to publish to user", {
35337
35636
  status: res.status,
35338
35637
  body: text3
35339
35638
  });
35340
35639
  }
35341
35640
  } catch (error) {
35342
- logger26.error("Publish to user error", { error });
35641
+ logger27.error("Publish to user error", { error });
35343
35642
  }
35344
35643
  }
35345
35644
 
@@ -35361,7 +35660,7 @@ class NotificationService {
35361
35660
  conditions2.push(eq(notifications.type, type));
35362
35661
  }
35363
35662
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
35364
- logger26.debug("Listed notifications", { userId: user.id, count: results.length });
35663
+ logger27.debug("Listed notifications", { userId: user.id, count: results.length });
35365
35664
  return results;
35366
35665
  }
35367
35666
  async updateStatus(notificationId, status, method) {
@@ -35380,7 +35679,7 @@ class NotificationService {
35380
35679
  if (!updated) {
35381
35680
  throw new NotFoundError("Notification", notificationId);
35382
35681
  }
35383
- logger26.debug("Updated status", { notificationId, status });
35682
+ logger27.debug("Updated status", { notificationId, status });
35384
35683
  return updated;
35385
35684
  }
35386
35685
  async getStats(user, options) {
@@ -35406,7 +35705,7 @@ class NotificationService {
35406
35705
  const clicked = statsMap.clicked || 0;
35407
35706
  const dismissed = statsMap.dismissed || 0;
35408
35707
  const expired = statsMap.expired || 0;
35409
- logger26.debug("Retrieved stats", { userId: user.id, total });
35708
+ logger27.debug("Retrieved stats", { userId: user.id, total });
35410
35709
  return {
35411
35710
  total,
35412
35711
  delivered,
@@ -35455,7 +35754,7 @@ class NotificationService {
35455
35754
  options: { data, clickUrl, metadata: metadata2 }
35456
35755
  });
35457
35756
  }
35458
- logger26.debug("Created notification", {
35757
+ logger27.debug("Created notification", {
35459
35758
  userId,
35460
35759
  type,
35461
35760
  id: notificationId,
@@ -35463,7 +35762,7 @@ class NotificationService {
35463
35762
  });
35464
35763
  return notificationId;
35465
35764
  } catch (error) {
35466
- logger26.error("Failed to create notification", { userId, type, error });
35765
+ logger27.error("Failed to create notification", { userId, type, error });
35467
35766
  return null;
35468
35767
  }
35469
35768
  }
@@ -35477,7 +35776,7 @@ class NotificationService {
35477
35776
  }) {
35478
35777
  const realtimeConfig = this.deps.realtime;
35479
35778
  if (!realtimeConfig) {
35480
- logger26.warn("No realtime config for publish");
35779
+ logger27.warn("No realtime config for publish");
35481
35780
  return;
35482
35781
  }
35483
35782
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35519,13 +35818,13 @@ class NotificationService {
35519
35818
  metadata: data.metadata || {}
35520
35819
  }).returning();
35521
35820
  if (!notification) {
35522
- logger26.error("Notification insert returned no rows", {
35821
+ logger27.error("Notification insert returned no rows", {
35523
35822
  userId: data.userId,
35524
35823
  type: data.type
35525
35824
  });
35526
35825
  throw new InternalError("Failed to create notification");
35527
35826
  }
35528
- logger26.info("Inserted notification", {
35827
+ logger27.info("Inserted notification", {
35529
35828
  notificationId: notification.id,
35530
35829
  userId: notification.userId,
35531
35830
  type: notification.type
@@ -35535,7 +35834,7 @@ class NotificationService {
35535
35834
  async deliverPending(userId) {
35536
35835
  const realtimeConfig = this.deps.realtime;
35537
35836
  if (!realtimeConfig) {
35538
- logger26.warn("No realtime config for delivery");
35837
+ logger27.warn("No realtime config for delivery");
35539
35838
  return;
35540
35839
  }
35541
35840
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35562,13 +35861,13 @@ class NotificationService {
35562
35861
  metadata: notification.metadata,
35563
35862
  clickUrl: notification.clickUrl
35564
35863
  });
35565
- logger26.info("Delivered notification", {
35864
+ logger27.info("Delivered notification", {
35566
35865
  notificationId: notification.id,
35567
35866
  userId,
35568
35867
  type: notification.type
35569
35868
  });
35570
35869
  } catch (error) {
35571
- logger26.warn("Failed to deliver", {
35870
+ logger27.warn("Failed to deliver", {
35572
35871
  notificationId: notification.id,
35573
35872
  error
35574
35873
  });
@@ -35576,7 +35875,7 @@ class NotificationService {
35576
35875
  }
35577
35876
  }
35578
35877
  }
35579
- var logger26;
35878
+ var logger27;
35580
35879
  var init_notification_service = __esm(() => {
35581
35880
  init_drizzle_orm();
35582
35881
  init_src();
@@ -35585,7 +35884,7 @@ var init_notification_service = __esm(() => {
35585
35884
  init_events();
35586
35885
  init_notification();
35587
35886
  init_errors();
35588
- logger26 = log.scope("NotificationService");
35887
+ logger27 = log.scope("NotificationService");
35589
35888
  });
35590
35889
 
35591
35890
  // ../api-core/src/services/factory/player.ts
@@ -35709,7 +36008,7 @@ class CharacterService {
35709
36008
  createdAt: characterComponents.createdAt,
35710
36009
  updatedAt: characterComponents.updatedAt
35711
36010
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
35712
- logger27.debug("Listed available components", {
36011
+ logger28.debug("Listed available components", {
35713
36012
  level,
35714
36013
  count: components.length
35715
36014
  });
@@ -35727,7 +36026,7 @@ class CharacterService {
35727
36026
  }
35728
36027
  }
35729
36028
  });
35730
- logger27.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
36029
+ logger28.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
35731
36030
  return pc ?? null;
35732
36031
  }
35733
36032
  async getByUserId(userId) {
@@ -35742,7 +36041,7 @@ class CharacterService {
35742
36041
  }
35743
36042
  }
35744
36043
  });
35745
- logger27.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
36044
+ logger28.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
35746
36045
  return pc ?? null;
35747
36046
  }
35748
36047
  async create(input, user) {
@@ -35757,13 +36056,13 @@ class CharacterService {
35757
36056
  }
35758
36057
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
35759
36058
  if (!characterRow) {
35760
- logger27.error("Character insert returned no rows", { userId: user.id });
36059
+ logger28.error("Character insert returned no rows", { userId: user.id });
35761
36060
  throw new InternalError("Failed to create character in database");
35762
36061
  }
35763
36062
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
35764
36063
  return characterRow;
35765
36064
  });
35766
- logger27.info("Created character", { userId: user.id, characterId: result.id });
36065
+ logger28.info("Created character", { userId: user.id, characterId: result.id });
35767
36066
  return result;
35768
36067
  }
35769
36068
  async update(input, user) {
@@ -35775,7 +36074,7 @@ class CharacterService {
35775
36074
  if (!row) {
35776
36075
  throw new NotFoundError("Player character");
35777
36076
  }
35778
- logger27.info("Updated character", {
36077
+ logger28.info("Updated character", {
35779
36078
  userId: user.id,
35780
36079
  characterId: row.id,
35781
36080
  updatedFields: Object.keys(input)
@@ -35797,7 +36096,7 @@ class CharacterService {
35797
36096
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
35798
36097
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
35799
36098
  if (!validation.isValid) {
35800
- logger27.warn("Accessory validation failed", {
36099
+ logger28.warn("Accessory validation failed", {
35801
36100
  userId: user.id,
35802
36101
  slot,
35803
36102
  accessoryComponentId,
@@ -35813,14 +36112,14 @@ class CharacterService {
35813
36112
  slot
35814
36113
  }).returning();
35815
36114
  if (!result) {
35816
- logger27.error("Accessory insert returned no rows", {
36115
+ logger28.error("Accessory insert returned no rows", {
35817
36116
  userId: user.id,
35818
36117
  slot,
35819
36118
  accessoryComponentId
35820
36119
  });
35821
36120
  throw new InternalError("Failed to equip accessory");
35822
36121
  }
35823
- logger27.info("Equipped accessory", {
36122
+ logger28.info("Equipped accessory", {
35824
36123
  userId: user.id,
35825
36124
  slot,
35826
36125
  accessoryComponentId
@@ -35841,7 +36140,7 @@ class CharacterService {
35841
36140
  const playerLevel = userLevel?.currentLevel ?? 1;
35842
36141
  const validation = validateAccessoryRemoval(slot, playerLevel);
35843
36142
  if (!validation.isValid) {
35844
- logger27.warn("Accessory removal validation failed", {
36143
+ logger28.warn("Accessory removal validation failed", {
35845
36144
  userId: user.id,
35846
36145
  slot,
35847
36146
  playerLevel,
@@ -35850,17 +36149,17 @@ class CharacterService {
35850
36149
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
35851
36150
  }
35852
36151
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
35853
- logger27.info("Removed accessory", { userId: user.id, slot });
36152
+ logger28.info("Removed accessory", { userId: user.id, slot });
35854
36153
  }
35855
36154
  }
35856
- var logger27;
36155
+ var logger28;
35857
36156
  var init_character_service = __esm(() => {
35858
36157
  init_drizzle_orm();
35859
36158
  init_tables_index();
35860
36159
  init_src2();
35861
36160
  init_errors();
35862
36161
  init_accessory_util();
35863
- logger27 = log.scope("CharacterService");
36162
+ logger28 = log.scope("CharacterService");
35864
36163
  });
35865
36164
 
35866
36165
  // ../api-core/src/services/currency.service.ts
@@ -35872,7 +36171,7 @@ class CurrencyService {
35872
36171
  async list() {
35873
36172
  const db2 = this.deps.db;
35874
36173
  const allCurrencies = await db2.query.currencies.findMany();
35875
- logger28.debug("Listed currencies", { count: allCurrencies.length });
36174
+ logger29.debug("Listed currencies", { count: allCurrencies.length });
35876
36175
  return allCurrencies;
35877
36176
  }
35878
36177
  async getById(currencyId) {
@@ -35883,7 +36182,7 @@ class CurrencyService {
35883
36182
  if (!currency) {
35884
36183
  throw new NotFoundError("Currency", currencyId);
35885
36184
  }
35886
- logger28.debug("Retrieved currency", { currencyId });
36185
+ logger29.debug("Retrieved currency", { currencyId });
35887
36186
  return currency;
35888
36187
  }
35889
36188
  async create(data) {
@@ -35891,13 +36190,13 @@ class CurrencyService {
35891
36190
  try {
35892
36191
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
35893
36192
  if (!newCurrency) {
35894
- logger28.error("Currency insert returned no rows", {
36193
+ logger29.error("Currency insert returned no rows", {
35895
36194
  itemId: data.itemId,
35896
36195
  symbol: data.symbol
35897
36196
  });
35898
36197
  throw new InternalError("Failed to create currency");
35899
36198
  }
35900
- logger28.info("Created currency", {
36199
+ logger29.info("Created currency", {
35901
36200
  currencyId: newCurrency.id,
35902
36201
  itemId: newCurrency.itemId,
35903
36202
  symbol: newCurrency.symbol,
@@ -35926,7 +36225,7 @@ class CurrencyService {
35926
36225
  if (!updatedCurrency) {
35927
36226
  throw new NotFoundError("Currency", currencyId);
35928
36227
  }
35929
- logger28.info("Updated currency", {
36228
+ logger29.info("Updated currency", {
35930
36229
  currencyId: updatedCurrency.id,
35931
36230
  updatedFields: Object.keys(data)
35932
36231
  });
@@ -35952,16 +36251,16 @@ class CurrencyService {
35952
36251
  if (result.length === 0) {
35953
36252
  throw new NotFoundError("Currency", currencyId);
35954
36253
  }
35955
- logger28.info("Deleted currency", { currencyId });
36254
+ logger29.info("Deleted currency", { currencyId });
35956
36255
  }
35957
36256
  }
35958
- var logger28;
36257
+ var logger29;
35959
36258
  var init_currency_service = __esm(() => {
35960
36259
  init_drizzle_orm();
35961
36260
  init_tables_index();
35962
36261
  init_src2();
35963
36262
  init_errors();
35964
- logger28 = log.scope("CurrencyService");
36263
+ logger29 = log.scope("CurrencyService");
35965
36264
  });
35966
36265
 
35967
36266
  // ../api-core/src/services/logs.service.ts
@@ -35980,11 +36279,11 @@ class LogsService {
35980
36279
  if (!game) {
35981
36280
  throw new NotFoundError("Game", slug2);
35982
36281
  }
35983
- logger29.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
36282
+ logger30.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
35984
36283
  } else {
35985
36284
  const isApprovedDev = user.developerStatus === "approved";
35986
36285
  if (!isApprovedDev) {
35987
- logger29.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
36286
+ logger30.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
35988
36287
  throw new AccessDeniedError("Must be an approved developer");
35989
36288
  }
35990
36289
  const game = await db2.query.games.findFirst({
@@ -35999,7 +36298,7 @@ class LogsService {
35999
36298
  columns: { id: true }
36000
36299
  });
36001
36300
  if (!membership) {
36002
- logger29.warn("Developer attempted access to unowned game logs", {
36301
+ logger30.warn("Developer attempted access to unowned game logs", {
36003
36302
  userId: user.id,
36004
36303
  slug: slug2
36005
36304
  });
@@ -36008,7 +36307,7 @@ class LogsService {
36008
36307
  }
36009
36308
  const workerId = getDeploymentId(slug2, sstStage);
36010
36309
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
36011
- logger29.debug("Generated log stream token", {
36310
+ logger30.debug("Generated log stream token", {
36012
36311
  userId: user.id,
36013
36312
  slug: slug2,
36014
36313
  workerId
@@ -36016,14 +36315,14 @@ class LogsService {
36016
36315
  return { token, workerId };
36017
36316
  }
36018
36317
  }
36019
- var logger29;
36318
+ var logger30;
36020
36319
  var init_logs_service = __esm(() => {
36021
36320
  init_drizzle_orm();
36022
36321
  init_tables_index();
36023
36322
  init_src2();
36024
36323
  init_errors();
36025
36324
  init_deployment_util();
36026
- logger29 = log.scope("LogsService");
36325
+ logger30 = log.scope("LogsService");
36027
36326
  });
36028
36327
 
36029
36328
  // ../api-core/src/services/lti.service.ts
@@ -36076,7 +36375,7 @@ class MapService {
36076
36375
  if (!mapDetails) {
36077
36376
  throw new NotFoundError("Map", identifier);
36078
36377
  }
36079
- logger30.debug("Retrieved map", { identifier });
36378
+ logger31.debug("Retrieved map", { identifier });
36080
36379
  return mapDetails;
36081
36380
  }
36082
36381
  async getElements(mapId) {
@@ -36092,7 +36391,7 @@ class MapService {
36092
36391
  }
36093
36392
  }
36094
36393
  });
36095
- logger30.debug("Retrieved elements", { mapId, count: elements.length });
36394
+ logger31.debug("Retrieved elements", { mapId, count: elements.length });
36096
36395
  return elements;
36097
36396
  }
36098
36397
  async getObjects(mapId, userId) {
@@ -36113,7 +36412,7 @@ class MapService {
36113
36412
  }
36114
36413
  }
36115
36414
  });
36116
- logger30.debug("Retrieved objects", { mapId, userId, count: objects.length });
36415
+ logger31.debug("Retrieved objects", { mapId, userId, count: objects.length });
36117
36416
  return objects.map((object) => this.formatMapObjectWithItem(object));
36118
36417
  }
36119
36418
  async createObject(mapId, data, user) {
@@ -36140,7 +36439,7 @@ class MapService {
36140
36439
  throw new NotFoundError("Item", data.itemId);
36141
36440
  }
36142
36441
  if (!item.isPlaceable) {
36143
- logger30.warn("Attempted to place non-placeable item", {
36442
+ logger31.warn("Attempted to place non-placeable item", {
36144
36443
  userId: user.id,
36145
36444
  itemId: data.itemId,
36146
36445
  mapId
@@ -36154,7 +36453,7 @@ class MapService {
36154
36453
  };
36155
36454
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
36156
36455
  if (!createdObject) {
36157
- logger30.error("Map object insert returned no rows", {
36456
+ logger31.error("Map object insert returned no rows", {
36158
36457
  userId: user.id,
36159
36458
  mapId,
36160
36459
  itemId: data.itemId
@@ -36178,12 +36477,12 @@ class MapService {
36178
36477
  }
36179
36478
  });
36180
36479
  if (!objectWithItem) {
36181
- logger30.error("Map object query after insert returned no rows", {
36480
+ logger31.error("Map object query after insert returned no rows", {
36182
36481
  objectId: createdObject.id
36183
36482
  });
36184
36483
  throw new InternalError("Failed to retrieve created object");
36185
36484
  }
36186
- logger30.info("Created object", {
36485
+ logger31.info("Created object", {
36187
36486
  userId: user.id,
36188
36487
  mapId,
36189
36488
  objectId: createdObject.id,
@@ -36207,7 +36506,7 @@ class MapService {
36207
36506
  if (result.length === 0) {
36208
36507
  throw new NotFoundError("MapObject", objectId);
36209
36508
  }
36210
- logger30.info("Deleted object", {
36509
+ logger31.info("Deleted object", {
36211
36510
  userId: user.id,
36212
36511
  mapId,
36213
36512
  objectId
@@ -36236,13 +36535,13 @@ class MapService {
36236
36535
  };
36237
36536
  }
36238
36537
  }
36239
- var logger30;
36538
+ var logger31;
36240
36539
  var init_map_service = __esm(() => {
36241
36540
  init_drizzle_orm();
36242
36541
  init_tables_index();
36243
36542
  init_src2();
36244
36543
  init_errors();
36245
- logger30 = log.scope("MapService");
36544
+ logger31 = log.scope("MapService");
36246
36545
  });
36247
36546
 
36248
36547
  // ../api-core/src/services/realtime.service.ts
@@ -36277,20 +36576,20 @@ class RealtimeService {
36277
36576
  }
36278
36577
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
36279
36578
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
36280
- logger31.info("Generated token", {
36579
+ logger32.info("Generated token", {
36281
36580
  userId: user.id,
36282
36581
  gameId: resolvedGameId || "global"
36283
36582
  });
36284
36583
  return { token };
36285
36584
  }
36286
36585
  }
36287
- var logger31;
36586
+ var logger32;
36288
36587
  var init_realtime_service = __esm(() => {
36289
36588
  init_drizzle_orm();
36290
36589
  init_tables_index();
36291
36590
  init_src2();
36292
36591
  init_errors();
36293
- logger31 = log.scope("RealtimeService");
36592
+ logger32 = log.scope("RealtimeService");
36294
36593
  });
36295
36594
 
36296
36595
  // ../api-core/src/services/session.service.ts
@@ -36325,10 +36624,10 @@ class SessionService {
36325
36624
  };
36326
36625
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
36327
36626
  if (!newSession?.sessionId) {
36328
- logger32.error("Game session insert returned no rows", { userId, gameId });
36627
+ logger33.error("Game session insert returned no rows", { userId, gameId });
36329
36628
  throw new InternalError("Failed to create game session");
36330
36629
  }
36331
- logger32.info("Started new session", {
36630
+ logger33.info("Started new session", {
36332
36631
  sessionId: newSession.sessionId,
36333
36632
  gameId,
36334
36633
  userId
@@ -36349,23 +36648,23 @@ class SessionService {
36349
36648
  return { success: true, message: "Session already ended" };
36350
36649
  }
36351
36650
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
36352
- logger32.info("Ended session", { sessionId, gameId, userId });
36651
+ logger33.info("Ended session", { sessionId, gameId, userId });
36353
36652
  return { success: true };
36354
36653
  }
36355
36654
  async mintToken(gameIdOrSlug, userId) {
36356
36655
  const gameId = await this.resolveGameId(gameIdOrSlug);
36357
36656
  const result = await this.deps.mintGameToken(gameId, userId);
36358
- logger32.debug("Minted game token", { gameId, userId });
36657
+ logger33.debug("Minted game token", { gameId, userId });
36359
36658
  return result;
36360
36659
  }
36361
36660
  }
36362
- var logger32;
36661
+ var logger33;
36363
36662
  var init_session_service = __esm(() => {
36364
36663
  init_drizzle_orm();
36365
36664
  init_tables_index();
36366
36665
  init_src2();
36367
36666
  init_errors();
36368
- logger32 = log.scope("SessionService");
36667
+ logger33 = log.scope("SessionService");
36369
36668
  });
36370
36669
 
36371
36670
  // ../api-core/src/services/shop.service.ts
@@ -36411,7 +36710,7 @@ class ShopService {
36411
36710
  const shopItems = [];
36412
36711
  for (const listing of listingsWithRelations) {
36413
36712
  if (!listing.item || !listing.currency) {
36414
- logger33.warn("Listing missing item or currency, skipping", {
36713
+ logger34.warn("Listing missing item or currency, skipping", {
36415
36714
  listingId: listing.id
36416
36715
  });
36417
36716
  } else {
@@ -36428,7 +36727,7 @@ class ShopService {
36428
36727
  });
36429
36728
  }
36430
36729
  }
36431
- logger33.debug("Retrieved shop view", {
36730
+ logger34.debug("Retrieved shop view", {
36432
36731
  userId: user.id,
36433
36732
  itemCount: shopItems.length,
36434
36733
  currencyCount: shopCurrencies.length
@@ -36439,12 +36738,12 @@ class ShopService {
36439
36738
  };
36440
36739
  }
36441
36740
  }
36442
- var logger33;
36741
+ var logger34;
36443
36742
  var init_shop_service = __esm(() => {
36444
36743
  init_drizzle_orm();
36445
36744
  init_tables_index();
36446
36745
  init_src2();
36447
- logger33 = log.scope("ShopService");
36746
+ logger34 = log.scope("ShopService");
36448
36747
  });
36449
36748
 
36450
36749
  // ../api-core/src/services/sprite.service.ts
@@ -36461,17 +36760,17 @@ class SpriteService {
36461
36760
  if (!template) {
36462
36761
  throw new NotFoundError("SpriteTemplate", slug2);
36463
36762
  }
36464
- logger34.debug("Retrieved sprite", { slug: slug2 });
36763
+ logger35.debug("Retrieved sprite", { slug: slug2 });
36465
36764
  return template;
36466
36765
  }
36467
36766
  }
36468
- var logger34;
36767
+ var logger35;
36469
36768
  var init_sprite_service = __esm(() => {
36470
36769
  init_drizzle_orm();
36471
36770
  init_tables_index();
36472
36771
  init_src2();
36473
36772
  init_errors();
36474
- logger34 = log.scope("SpriteService");
36773
+ logger35 = log.scope("SpriteService");
36475
36774
  });
36476
36775
 
36477
36776
  // ../api-core/src/services/user.service.ts
@@ -36486,12 +36785,12 @@ class UserService {
36486
36785
  where: eq(users.id, user.id)
36487
36786
  });
36488
36787
  if (!userData) {
36489
- logger35.error("User not found", { userId: user.id });
36788
+ logger36.error("User not found", { userId: user.id });
36490
36789
  throw new NotFoundError("User", user.id);
36491
36790
  }
36492
36791
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
36493
36792
  if (gameId) {
36494
- logger35.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36793
+ logger36.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36495
36794
  return {
36496
36795
  id: userData.id,
36497
36796
  name: userData.name,
@@ -36504,7 +36803,7 @@ class UserService {
36504
36803
  const timebackAccount = await db2.query.accounts.findFirst({
36505
36804
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
36506
36805
  });
36507
- logger35.debug("Fetched user profile (platform context)", { userId: user.id });
36806
+ logger36.debug("Fetched user profile (platform context)", { userId: user.id });
36508
36807
  return {
36509
36808
  id: userData.id,
36510
36809
  name: userData.name,
@@ -36527,7 +36826,7 @@ class UserService {
36527
36826
  columns: { name: true }
36528
36827
  });
36529
36828
  if (!userData) {
36530
- logger35.error("Demo user not found", { userId });
36829
+ logger36.error("Demo user not found", { userId });
36531
36830
  throw new NotFoundError("User", userId);
36532
36831
  }
36533
36832
  return {
@@ -36541,10 +36840,10 @@ class UserService {
36541
36840
  updatedAt: new Date
36542
36841
  }).where(eq(users.id, userId)).returning({ name: users.name });
36543
36842
  if (!updatedUser) {
36544
- logger35.error("Demo user not found for profile update", { userId });
36843
+ logger36.error("Demo user not found for profile update", { userId });
36545
36844
  throw new NotFoundError("User", userId);
36546
36845
  }
36547
- logger35.debug("Updated demo profile", { userId, displayName });
36846
+ logger36.debug("Updated demo profile", { userId, displayName });
36548
36847
  return {
36549
36848
  displayName: updatedUser.name,
36550
36849
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -36557,7 +36856,7 @@ class UserService {
36557
36856
  ]);
36558
36857
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
36559
36858
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
36560
- logger35.debug("Fetched Timeback data", {
36859
+ logger36.debug("Fetched Timeback data", {
36561
36860
  timebackId,
36562
36861
  role,
36563
36862
  enrollmentCount: enrollments.length,
@@ -36566,9 +36865,9 @@ class UserService {
36566
36865
  return { id: timebackId, role, enrollments, organizations };
36567
36866
  }
36568
36867
  async fetchStudentProfile(timebackId) {
36569
- logger35.debug("Fetching student profile", { timebackId });
36868
+ logger36.debug("Fetching student profile", { timebackId });
36570
36869
  if (!this.deps.timeback) {
36571
- logger35.warn("Timeback client not available");
36870
+ logger36.warn("Timeback client not available");
36572
36871
  return { role: "student", organizations: [] };
36573
36872
  }
36574
36873
  try {
@@ -36596,14 +36895,14 @@ class UserService {
36596
36895
  }
36597
36896
  return { role, organizations: [...orgMap.values()] };
36598
36897
  } catch (error) {
36599
- logger35.warn("Failed to fetch student profile", { error, timebackId });
36898
+ logger36.warn("Failed to fetch student profile", { error, timebackId });
36600
36899
  return { role: "student", organizations: [] };
36601
36900
  }
36602
36901
  }
36603
36902
  async fetchEnrollments(timebackId) {
36604
- logger35.debug("Fetching enrollments", { timebackId });
36903
+ logger36.debug("Fetching enrollments", { timebackId });
36605
36904
  if (!this.deps.timeback) {
36606
- logger35.warn("Timeback client not available");
36905
+ logger36.warn("Timeback client not available");
36607
36906
  return [];
36608
36907
  }
36609
36908
  try {
@@ -36624,7 +36923,7 @@ class UserService {
36624
36923
  orgId: courseToSchool.get(i2.courseId)
36625
36924
  }));
36626
36925
  } catch (error) {
36627
- logger35.warn("Failed to fetch enrollments", { error, timebackId });
36926
+ logger36.warn("Failed to fetch enrollments", { error, timebackId });
36628
36927
  return [];
36629
36928
  }
36630
36929
  }
@@ -36639,14 +36938,14 @@ class UserService {
36639
36938
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
36640
36939
  }
36641
36940
  }
36642
- var logger35;
36941
+ var logger36;
36643
36942
  var init_user_service = __esm(() => {
36644
36943
  init_drizzle_orm();
36645
36944
  init_src();
36646
36945
  init_tables_index();
36647
36946
  init_src2();
36648
36947
  init_errors();
36649
- logger35 = log.scope("UserService");
36948
+ logger36 = log.scope("UserService");
36650
36949
  });
36651
36950
 
36652
36951
  // ../api-core/src/services/verify.service.ts
@@ -36656,16 +36955,16 @@ class VerifyService {
36656
36955
  this.deps = deps;
36657
36956
  }
36658
36957
  async verifyGameToken(token) {
36659
- logger36.debug("Verifying game token");
36958
+ logger37.debug("Verifying game token");
36660
36959
  const payload = await this.deps.validateGameToken(token);
36661
36960
  if (!payload) {
36662
- logger36.warn("Invalid or expired game token presented");
36961
+ logger37.warn("Invalid or expired game token presented");
36663
36962
  throw new ValidationError("Invalid or expired token");
36664
36963
  }
36665
36964
  const gameId = payload.sub;
36666
36965
  const userId = payload.uid;
36667
36966
  if (typeof gameId !== "string" || typeof userId !== "string") {
36668
- logger36.warn("Game token missing required claims", {
36967
+ logger37.warn("Game token missing required claims", {
36669
36968
  hasGameId: typeof gameId === "string",
36670
36969
  hasUserId: typeof userId === "string"
36671
36970
  });
@@ -36676,7 +36975,7 @@ class VerifyService {
36676
36975
  where: eq(users.id, userId)
36677
36976
  });
36678
36977
  if (!userData) {
36679
- logger36.error("User not found for valid token", {
36978
+ logger37.error("User not found for valid token", {
36680
36979
  userId
36681
36980
  });
36682
36981
  throw new NotFoundError("User", userId);
@@ -36690,7 +36989,7 @@ class VerifyService {
36690
36989
  family_name: undefined,
36691
36990
  timeback_id: userData.timebackId || undefined
36692
36991
  };
36693
- logger36.info("Token verified", { gameId, userId });
36992
+ logger37.info("Token verified", { gameId, userId });
36694
36993
  return {
36695
36994
  claims: payload,
36696
36995
  gameId,
@@ -36698,13 +36997,13 @@ class VerifyService {
36698
36997
  };
36699
36998
  }
36700
36999
  }
36701
- var logger36;
37000
+ var logger37;
36702
37001
  var init_verify_service = __esm(() => {
36703
37002
  init_drizzle_orm();
36704
37003
  init_tables_index();
36705
37004
  init_src2();
36706
37005
  init_errors();
36707
- logger36 = log.scope("VerifyService");
37006
+ logger37 = log.scope("VerifyService");
36708
37007
  });
36709
37008
 
36710
37009
  // ../api-core/src/services/factory/standalone.ts
@@ -41822,7 +42121,7 @@ var humanize = (times) => {
41822
42121
  }
41823
42122
  }
41824
42123
  return `${status}`;
41825
- }, logger37 = (fn = console.log) => {
42124
+ }, logger38 = (fn = console.log) => {
41826
42125
  return async function logger2(c, next) {
41827
42126
  const { method, url: url2 } = c.req;
41828
42127
  const path = url2.slice(url2.indexOf("/", 8));
@@ -42003,7 +42302,7 @@ function createApp(db2, options) {
42003
42302
  const app = new Hono2;
42004
42303
  app.use("*", cors({ origin: "*", credentials: true }));
42005
42304
  if (options.verbose && !options.quiet) {
42006
- app.use("*", logger37());
42305
+ app.use("*", logger38());
42007
42306
  }
42008
42307
  app.use("/api/*", async (c, next) => {
42009
42308
  c.set("db", db2);
@@ -48436,12 +48735,12 @@ var init_session2 = __esm(() => {
48436
48735
  init_utils();
48437
48736
  init_dist5();
48438
48737
  PglitePreparedQuery = class PglitePreparedQuery extends PgPreparedQuery {
48439
- constructor(client, queryString, params, logger38, fields, name3, _isResponseInArrayMode, customResultMapper) {
48738
+ constructor(client, queryString, params, logger39, fields, name3, _isResponseInArrayMode, customResultMapper) {
48440
48739
  super({ sql: queryString, params });
48441
48740
  this.client = client;
48442
48741
  this.queryString = queryString;
48443
48742
  this.params = params;
48444
- this.logger = logger38;
48743
+ this.logger = logger39;
48445
48744
  this.fields = fields;
48446
48745
  this._isResponseInArrayMode = _isResponseInArrayMode;
48447
48746
  this.customResultMapper = customResultMapper;
@@ -48545,11 +48844,11 @@ var init_session2 = __esm(() => {
48545
48844
  // ../../node_modules/.bun/drizzle-orm@0.42.0+f8aef3f54a4d48e2/node_modules/drizzle-orm/pglite/driver.js
48546
48845
  function construct(client, config2 = {}) {
48547
48846
  const dialect2 = new PgDialect({ casing: config2.casing });
48548
- let logger38;
48847
+ let logger39;
48549
48848
  if (config2.logger === true) {
48550
- logger38 = new DefaultLogger;
48849
+ logger39 = new DefaultLogger;
48551
48850
  } else if (config2.logger !== false) {
48552
- logger38 = config2.logger;
48851
+ logger39 = config2.logger;
48553
48852
  }
48554
48853
  let schema2;
48555
48854
  if (config2.schema) {
@@ -48560,7 +48859,7 @@ function construct(client, config2 = {}) {
48560
48859
  tableNamesMap: tablesConfig.tableNamesMap
48561
48860
  };
48562
48861
  }
48563
- const driver = new PgliteDriver(client, dialect2, { logger: logger38 });
48862
+ const driver = new PgliteDriver(client, dialect2, { logger: logger39 });
48564
48863
  const session2 = driver.createSession(schema2);
48565
48864
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
48566
48865
  db2.$client = client;
@@ -94774,8 +95073,8 @@ var init_currencies = __esm(() => {
94774
95073
  });
94775
95074
 
94776
95075
  // src/lib/logging/adapter.ts
94777
- function setLogger(logger38) {
94778
- customLogger = logger38;
95076
+ function setLogger(logger39) {
95077
+ customLogger = logger39;
94779
95078
  }
94780
95079
  function getLogger() {
94781
95080
  if (customLogger) {
@@ -94787,10 +95086,10 @@ function getLogger() {
94787
95086
  error: (msg) => console.error(msg)
94788
95087
  };
94789
95088
  }
94790
- var customLogger, logger38;
95089
+ var customLogger, logger39;
94791
95090
  var init_adapter = __esm(() => {
94792
95091
  init_config();
94793
- logger38 = {
95092
+ logger39 = {
94794
95093
  info: (msg) => {
94795
95094
  if (customLogger || !config.embedded) {
94796
95095
  getLogger().info(msg);
@@ -94880,7 +95179,7 @@ async function seedCoreGames(db2) {
94880
95179
  role: "owner"
94881
95180
  }).onConflictDoNothing();
94882
95181
  } catch (error2) {
94883
- logger38.error(`Error seeding core game '${gameData.slug}': ${error2}`);
95182
+ logger39.error(`Error seeding core game '${gameData.slug}': ${error2}`);
94884
95183
  }
94885
95184
  }
94886
95185
  }
@@ -94930,7 +95229,7 @@ async function seedCurrentProjectGame(db2, project) {
94930
95229
  }
94931
95230
  return newGame;
94932
95231
  } catch (error2) {
94933
- logger38.error(`❌ Error seeding project game: ${error2}`);
95232
+ logger39.error(`❌ Error seeding project game: ${error2}`);
94934
95233
  throw error2;
94935
95234
  }
94936
95235
  }
@@ -95723,7 +96022,7 @@ async function provisionLtiUser(db2, claims) {
95723
96022
  where: eq(users.id, existingAccount.userId)
95724
96023
  });
95725
96024
  if (user) {
95726
- logger39.info("Found user by LTI account", {
96025
+ logger40.info("Found user by LTI account", {
95727
96026
  userId: user.id,
95728
96027
  ltiTimebackId
95729
96028
  });
@@ -95752,13 +96051,13 @@ async function provisionLtiUser(db2, claims) {
95752
96051
  updatedAt: new Date
95753
96052
  }).returning({ id: accounts.id });
95754
96053
  if (!account) {
95755
- logger39.error("LTI account link insert returned no rows", {
96054
+ logger40.error("LTI account link insert returned no rows", {
95756
96055
  userId: existingUser.id,
95757
96056
  ltiTimebackId
95758
96057
  });
95759
96058
  throw new InternalError("Failed to link LTI account");
95760
96059
  }
95761
- logger39.info("Linked LTI account to existing user", {
96060
+ logger40.info("Linked LTI account to existing user", {
95762
96061
  userId: existingUser.id,
95763
96062
  ltiTimebackId
95764
96063
  });
@@ -95778,7 +96077,7 @@ async function provisionLtiUser(db2, claims) {
95778
96077
  updatedAt: new Date
95779
96078
  }).returning();
95780
96079
  if (!insertedUser) {
95781
- logger39.error("LTI user insert returned no rows", { email, ltiTimebackId });
96080
+ logger40.error("LTI user insert returned no rows", { email, ltiTimebackId });
95782
96081
  throw new InternalError("Failed to create user");
95783
96082
  }
95784
96083
  await tx.insert(accounts).values({
@@ -95793,7 +96092,7 @@ async function provisionLtiUser(db2, claims) {
95793
96092
  createdAt: new Date,
95794
96093
  updatedAt: new Date
95795
96094
  });
95796
- logger39.info("Provisioned new user from LTI", {
96095
+ logger40.info("Provisioned new user from LTI", {
95797
96096
  userId: insertedUser.id,
95798
96097
  ltiTimebackId
95799
96098
  });
@@ -95801,7 +96100,7 @@ async function provisionLtiUser(db2, claims) {
95801
96100
  });
95802
96101
  return createdUser;
95803
96102
  }
95804
- var logger39;
96103
+ var logger40;
95805
96104
  var init_lti_provisioning = __esm(() => {
95806
96105
  init_drizzle_orm();
95807
96106
  init_src();
@@ -95809,7 +96108,7 @@ var init_lti_provisioning = __esm(() => {
95809
96108
  init_src2();
95810
96109
  init_errors();
95811
96110
  init_lti_util();
95812
- logger39 = log.scope("LtiProvisioning");
96111
+ logger40 = log.scope("LtiProvisioning");
95813
96112
  });
95814
96113
 
95815
96114
  // ../api-core/src/utils/validation.util.ts
@@ -95857,21 +96156,21 @@ var init_utils11 = __esm(() => {
95857
96156
  });
95858
96157
 
95859
96158
  // ../api-core/src/controllers/achievement.controller.ts
95860
- var logger40, listCurrent, listHistory, postProgress, achievements3;
96159
+ var logger41, listCurrent, listHistory, postProgress, achievements3;
95861
96160
  var init_achievement_controller = __esm(() => {
95862
96161
  init_esm();
95863
96162
  init_schemas_index();
95864
96163
  init_src2();
95865
96164
  init_errors();
95866
96165
  init_utils11();
95867
- logger40 = log.scope("AchievementController");
96166
+ logger41 = log.scope("AchievementController");
95868
96167
  listCurrent = requireNonAnonymous(async (ctx) => {
95869
- logger40.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
96168
+ logger41.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
95870
96169
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
95871
96170
  });
95872
96171
  listHistory = requireNonAnonymous(async (ctx) => {
95873
96172
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
95874
- logger40.debug("Listing achievement history", { userId: ctx.user.id, limit });
96173
+ logger41.debug("Listing achievement history", { userId: ctx.user.id, limit });
95875
96174
  return ctx.services.achievement.listHistory(ctx.user, limit);
95876
96175
  });
95877
96176
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -95882,12 +96181,12 @@ var init_achievement_controller = __esm(() => {
95882
96181
  } catch (error2) {
95883
96182
  if (error2 instanceof exports_external.ZodError) {
95884
96183
  const details = formatZodError(error2);
95885
- logger40.warn("Submit achievement progress validation failed", { details });
96184
+ logger41.warn("Submit achievement progress validation failed", { details });
95886
96185
  throw ApiError.unprocessableEntity("Invalid request body", details);
95887
96186
  }
95888
96187
  throw ApiError.badRequest("Invalid JSON body");
95889
96188
  }
95890
- logger40.debug("Submitting progress", {
96189
+ logger41.debug("Submitting progress", {
95891
96190
  userId: ctx.user.id,
95892
96191
  achievementId: body2.achievementId
95893
96192
  });
@@ -95901,14 +96200,14 @@ var init_achievement_controller = __esm(() => {
95901
96200
  });
95902
96201
 
95903
96202
  // ../api-core/src/controllers/admin.controller.ts
95904
- var logger41, getAllowedOrigins;
96203
+ var logger42, getAllowedOrigins;
95905
96204
  var init_admin_controller = __esm(() => {
95906
96205
  init_src2();
95907
96206
  init_utils11();
95908
- logger41 = log.scope("AdminController");
96207
+ logger42 = log.scope("AdminController");
95909
96208
  getAllowedOrigins = requireAdmin(async (ctx) => {
95910
96209
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
95911
- logger41.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
96210
+ logger42.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
95912
96211
  if (shouldRefresh) {
95913
96212
  await ctx.providers.cache.refreshGameOrigins();
95914
96213
  }
@@ -95923,14 +96222,14 @@ var init_admin_controller = __esm(() => {
95923
96222
  });
95924
96223
 
95925
96224
  // ../api-core/src/controllers/bucket.controller.ts
95926
- var logger42, listFiles, getFile, putFile, deleteFile, initiateUpload;
96225
+ var logger43, listFiles, getFile, putFile, deleteFile, initiateUpload;
95927
96226
  var init_bucket_controller = __esm(() => {
95928
96227
  init_esm();
95929
96228
  init_schemas_index();
95930
96229
  init_src2();
95931
96230
  init_errors();
95932
96231
  init_utils11();
95933
- logger42 = log.scope("BucketController");
96232
+ logger43 = log.scope("BucketController");
95934
96233
  listFiles = requireDeveloper(async (ctx) => {
95935
96234
  const slug2 = ctx.params.slug;
95936
96235
  if (!slug2) {
@@ -95938,7 +96237,7 @@ var init_bucket_controller = __esm(() => {
95938
96237
  }
95939
96238
  const url2 = ctx.url;
95940
96239
  const prefix2 = url2.searchParams.get("prefix") || undefined;
95941
- logger42.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96240
+ logger43.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
95942
96241
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
95943
96242
  return { files };
95944
96243
  });
@@ -95948,7 +96247,7 @@ var init_bucket_controller = __esm(() => {
95948
96247
  if (!slug2 || !key) {
95949
96248
  throw ApiError.badRequest("Missing game slug or file key");
95950
96249
  }
95951
- logger42.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
96250
+ logger43.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
95952
96251
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
95953
96252
  return new Response(Buffer.from(object.body), {
95954
96253
  status: 200,
@@ -95967,7 +96266,7 @@ var init_bucket_controller = __esm(() => {
95967
96266
  const arrayBuffer = await ctx.request.arrayBuffer();
95968
96267
  const body2 = new Uint8Array(arrayBuffer);
95969
96268
  const contentType = ctx.request.headers.get("content-type") || undefined;
95970
- logger42.debug("Uploading file", {
96269
+ logger43.debug("Uploading file", {
95971
96270
  userId: ctx.user.id,
95972
96271
  slug: slug2,
95973
96272
  key,
@@ -95983,7 +96282,7 @@ var init_bucket_controller = __esm(() => {
95983
96282
  if (!slug2 || !key) {
95984
96283
  throw ApiError.badRequest("Missing game slug or file key");
95985
96284
  }
95986
- logger42.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
96285
+ logger43.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
95987
96286
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
95988
96287
  return { success: true, key };
95989
96288
  });
@@ -95995,12 +96294,12 @@ var init_bucket_controller = __esm(() => {
95995
96294
  } catch (error2) {
95996
96295
  if (error2 instanceof exports_external.ZodError) {
95997
96296
  const details = formatZodError(error2);
95998
- logger42.warn("Initiate upload validation failed", { details });
96297
+ logger43.warn("Initiate upload validation failed", { details });
95999
96298
  throw ApiError.unprocessableEntity("Validation failed", details);
96000
96299
  }
96001
96300
  throw ApiError.badRequest("Invalid JSON body");
96002
96301
  }
96003
- logger42.debug("Initiating multipart upload", {
96302
+ logger43.debug("Initiating multipart upload", {
96004
96303
  userId: ctx.user.id,
96005
96304
  gameId: body2.gameId,
96006
96305
  fileName: body2.fileName
@@ -96017,19 +96316,19 @@ async function listComponents(ctx) {
96017
96316
  if (!isNaN(parsed) && isFinite(parsed)) {
96018
96317
  level = Math.floor(Math.max(0, parsed));
96019
96318
  }
96020
- logger43.debug("Listing components", { level });
96319
+ logger44.debug("Listing components", { level });
96021
96320
  return ctx.services.character.listAvailableComponents(level);
96022
96321
  }
96023
- var logger43, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96322
+ var logger44, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96024
96323
  var init_character_controller = __esm(() => {
96025
96324
  init_esm();
96026
96325
  init_schemas_index();
96027
96326
  init_src2();
96028
96327
  init_errors();
96029
96328
  init_utils11();
96030
- logger43 = log.scope("CharacterController");
96329
+ logger44 = log.scope("CharacterController");
96031
96330
  get = requireNonAnonymous(async (ctx) => {
96032
- logger43.debug("Getting character", { userId: ctx.user.id });
96331
+ logger44.debug("Getting character", { userId: ctx.user.id });
96033
96332
  return ctx.services.character.getByUser(ctx.user);
96034
96333
  });
96035
96334
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -96037,7 +96336,7 @@ var init_character_controller = __esm(() => {
96037
96336
  if (!userId) {
96038
96337
  throw ApiError.badRequest("User ID is required in the URL path");
96039
96338
  }
96040
- logger43.debug("Getting character by user ID", { requestedUserId: userId });
96339
+ logger44.debug("Getting character by user ID", { requestedUserId: userId });
96041
96340
  return ctx.services.character.getByUserId(userId);
96042
96341
  });
96043
96342
  create = requireNonAnonymous(async (ctx) => {
@@ -96048,12 +96347,12 @@ var init_character_controller = __esm(() => {
96048
96347
  } catch (error2) {
96049
96348
  if (error2 instanceof exports_external.ZodError) {
96050
96349
  const details = formatZodError(error2);
96051
- logger43.warn("Create character validation failed", { details });
96350
+ logger44.warn("Create character validation failed", { details });
96052
96351
  throw ApiError.unprocessableEntity("Invalid request body", details);
96053
96352
  }
96054
96353
  throw ApiError.badRequest("Invalid JSON body");
96055
96354
  }
96056
- logger43.debug("Creating character", {
96355
+ logger44.debug("Creating character", {
96057
96356
  userId: ctx.user.id,
96058
96357
  bodyComponentId: body2.bodyComponentId,
96059
96358
  hairstyleComponentId: body2.hairstyleComponentId
@@ -96068,12 +96367,12 @@ var init_character_controller = __esm(() => {
96068
96367
  } catch (error2) {
96069
96368
  if (error2 instanceof exports_external.ZodError) {
96070
96369
  const details = formatZodError(error2);
96071
- logger43.warn("Update character validation failed", { details });
96370
+ logger44.warn("Update character validation failed", { details });
96072
96371
  throw ApiError.unprocessableEntity("Invalid request body", details);
96073
96372
  }
96074
96373
  throw ApiError.badRequest("Invalid JSON body");
96075
96374
  }
96076
- logger43.debug("Updating character", {
96375
+ logger44.debug("Updating character", {
96077
96376
  userId: ctx.user.id,
96078
96377
  bodyComponentId: body2.bodyComponentId,
96079
96378
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -96089,12 +96388,12 @@ var init_character_controller = __esm(() => {
96089
96388
  } catch (error2) {
96090
96389
  if (error2 instanceof exports_external.ZodError) {
96091
96390
  const details = formatZodError(error2);
96092
- logger43.warn("Equip accessory validation failed", { details });
96391
+ logger44.warn("Equip accessory validation failed", { details });
96093
96392
  throw ApiError.unprocessableEntity("Invalid request body", details);
96094
96393
  }
96095
96394
  throw ApiError.badRequest("Invalid JSON body");
96096
96395
  }
96097
- logger43.debug("Equipping accessory", {
96396
+ logger44.debug("Equipping accessory", {
96098
96397
  userId: ctx.user.id,
96099
96398
  slot: body2.slot,
96100
96399
  accessoryComponentId: body2.accessoryComponentId
@@ -96106,7 +96405,7 @@ var init_character_controller = __esm(() => {
96106
96405
  if (!slot) {
96107
96406
  throw ApiError.badRequest("Slot is required in the URL path");
96108
96407
  }
96109
- logger43.debug("Removing accessory", { userId: ctx.user.id, slot });
96408
+ logger44.debug("Removing accessory", { userId: ctx.user.id, slot });
96110
96409
  await ctx.services.character.removeAccessory(slot, ctx.user);
96111
96410
  return { success: true };
96112
96411
  });
@@ -96122,7 +96421,7 @@ var init_character_controller = __esm(() => {
96122
96421
  });
96123
96422
 
96124
96423
  // ../api-core/src/controllers/currency.controller.ts
96125
- var logger44, list, getById, create2, update3, remove, currencyController;
96424
+ var logger45, list, getById, create2, update3, remove, currencyController;
96126
96425
  var init_currency_controller = __esm(() => {
96127
96426
  init_esm();
96128
96427
  init_schemas_index();
@@ -96130,9 +96429,9 @@ var init_currency_controller = __esm(() => {
96130
96429
  init_src4();
96131
96430
  init_errors();
96132
96431
  init_utils11();
96133
- logger44 = log.scope("CurrencyController");
96432
+ logger45 = log.scope("CurrencyController");
96134
96433
  list = requireNonAnonymous(async (ctx) => {
96135
- logger44.debug("Listing currencies", { userId: ctx.user.id });
96434
+ logger45.debug("Listing currencies", { userId: ctx.user.id });
96136
96435
  return ctx.services.currency.list();
96137
96436
  });
96138
96437
  getById = requireNonAnonymous(async (ctx) => {
@@ -96143,7 +96442,7 @@ var init_currency_controller = __esm(() => {
96143
96442
  if (!isValidUUID(currencyId)) {
96144
96443
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96145
96444
  }
96146
- logger44.debug("Getting currency", { userId: ctx.user.id, currencyId });
96445
+ logger45.debug("Getting currency", { userId: ctx.user.id, currencyId });
96147
96446
  return ctx.services.currency.getById(currencyId);
96148
96447
  });
96149
96448
  create2 = requireAdmin(async (ctx) => {
@@ -96154,12 +96453,12 @@ var init_currency_controller = __esm(() => {
96154
96453
  } catch (error2) {
96155
96454
  if (error2 instanceof exports_external.ZodError) {
96156
96455
  const details = formatZodError(error2);
96157
- logger44.warn("Create currency validation failed", { details });
96456
+ logger45.warn("Create currency validation failed", { details });
96158
96457
  throw ApiError.unprocessableEntity("Validation failed", details);
96159
96458
  }
96160
96459
  throw ApiError.badRequest("Invalid JSON body");
96161
96460
  }
96162
- logger44.debug("Creating currency", {
96461
+ logger45.debug("Creating currency", {
96163
96462
  userId: ctx.user.id,
96164
96463
  symbol: body2.symbol,
96165
96464
  itemId: body2.itemId,
@@ -96182,12 +96481,12 @@ var init_currency_controller = __esm(() => {
96182
96481
  } catch (error2) {
96183
96482
  if (error2 instanceof exports_external.ZodError) {
96184
96483
  const details = formatZodError(error2);
96185
- logger44.warn("Update currency validation failed", { details });
96484
+ logger45.warn("Update currency validation failed", { details });
96186
96485
  throw ApiError.unprocessableEntity("Validation failed", details);
96187
96486
  }
96188
96487
  throw ApiError.badRequest("Invalid JSON body");
96189
96488
  }
96190
- logger44.debug("Updating currency", {
96489
+ logger45.debug("Updating currency", {
96191
96490
  userId: ctx.user.id,
96192
96491
  currencyId,
96193
96492
  symbol: body2.symbol,
@@ -96204,7 +96503,7 @@ var init_currency_controller = __esm(() => {
96204
96503
  if (!isValidUUID(currencyId)) {
96205
96504
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96206
96505
  }
96207
- logger44.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96506
+ logger45.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96208
96507
  await ctx.services.currency.delete(currencyId);
96209
96508
  });
96210
96509
  currencyController = {
@@ -96217,14 +96516,14 @@ var init_currency_controller = __esm(() => {
96217
96516
  });
96218
96517
 
96219
96518
  // ../api-core/src/controllers/database.controller.ts
96220
- var logger45, reset;
96519
+ var logger46, reset;
96221
96520
  var init_database_controller = __esm(() => {
96222
96521
  init_esm();
96223
96522
  init_schemas_index();
96224
96523
  init_src2();
96225
96524
  init_errors();
96226
96525
  init_utils11();
96227
- logger45 = log.scope("DatabaseController");
96526
+ logger46 = log.scope("DatabaseController");
96228
96527
  reset = requireDeveloper(async (ctx) => {
96229
96528
  const slug2 = ctx.params.slug;
96230
96529
  if (!slug2) {
@@ -96237,11 +96536,11 @@ var init_database_controller = __esm(() => {
96237
96536
  } catch (error2) {
96238
96537
  if (error2 instanceof exports_external.ZodError) {
96239
96538
  const details = formatZodError(error2);
96240
- logger45.warn("Database reset validation failed", { details });
96539
+ logger46.warn("Database reset validation failed", { details });
96241
96540
  throw ApiError.unprocessableEntity("Validation failed", details);
96242
96541
  }
96243
96542
  }
96244
- logger45.debug("Resetting database", {
96543
+ logger46.debug("Resetting database", {
96245
96544
  userId: ctx.user.id,
96246
96545
  slug: slug2,
96247
96546
  hasSchema: Boolean(body2.schema)
@@ -96259,7 +96558,7 @@ async function createJob(ctx) {
96259
96558
  let body2;
96260
96559
  try {
96261
96560
  const json4 = await ctx.request.json();
96262
- logger46.debug("Deploy request body", {
96561
+ logger47.debug("Deploy request body", {
96263
96562
  keys: Object.keys(json4 || {}),
96264
96563
  hasUploadToken: Boolean(json4?.uploadToken),
96265
96564
  hasCode: Boolean(json4?.code),
@@ -96269,7 +96568,7 @@ async function createJob(ctx) {
96269
96568
  } catch (error2) {
96270
96569
  if (error2 instanceof exports_external.ZodError) {
96271
96570
  const details = formatZodError(error2);
96272
- logger46.warn("Deploy validation failed", { details });
96571
+ logger47.warn("Deploy validation failed", { details });
96273
96572
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
96274
96573
  }
96275
96574
  throw ApiError.badRequest("Invalid JSON body");
@@ -96290,14 +96589,14 @@ async function getJob(ctx) {
96290
96589
  }
96291
96590
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
96292
96591
  }
96293
- var logger46, deploy;
96592
+ var logger47, deploy;
96294
96593
  var init_deploy_controller = __esm(() => {
96295
96594
  init_esm();
96296
96595
  init_schemas_index();
96297
96596
  init_src2();
96298
96597
  init_errors();
96299
96598
  init_utils11();
96300
- logger46 = log.scope("DeployController");
96599
+ logger47 = log.scope("DeployController");
96301
96600
  deploy = {
96302
96601
  createJob: requireDeveloper(createJob),
96303
96602
  getJob: requireDeveloper(getJob)
@@ -96305,17 +96604,17 @@ var init_deploy_controller = __esm(() => {
96305
96604
  });
96306
96605
 
96307
96606
  // ../api-core/src/controllers/developer.controller.ts
96308
- var logger47, apply, getStatus, developer;
96607
+ var logger48, apply, getStatus, developer;
96309
96608
  var init_developer_controller = __esm(() => {
96310
96609
  init_src2();
96311
96610
  init_utils11();
96312
- logger47 = log.scope("DeveloperController");
96611
+ logger48 = log.scope("DeveloperController");
96313
96612
  apply = requireNonAnonymous(async (ctx) => {
96314
- logger47.debug("Applying for developer status", { userId: ctx.user.id });
96613
+ logger48.debug("Applying for developer status", { userId: ctx.user.id });
96315
96614
  await ctx.services.developer.apply(ctx.user);
96316
96615
  });
96317
96616
  getStatus = requireNonAnonymous(async (ctx) => {
96318
- logger47.debug("Getting developer status", { userId: ctx.user.id });
96617
+ logger48.debug("Getting developer status", { userId: ctx.user.id });
96319
96618
  const status = await ctx.services.developer.getStatus(ctx.user.id);
96320
96619
  return { status };
96321
96620
  });
@@ -96326,7 +96625,7 @@ var init_developer_controller = __esm(() => {
96326
96625
  });
96327
96626
 
96328
96627
  // ../api-core/src/controllers/domain.controller.ts
96329
- var logger48, add, list2, getStatus2, remove2, domains2;
96628
+ var logger49, add, list2, getStatus2, remove2, domains2;
96330
96629
  var init_domain_controller = __esm(() => {
96331
96630
  init_esm();
96332
96631
  init_schemas_index();
@@ -96334,7 +96633,7 @@ var init_domain_controller = __esm(() => {
96334
96633
  init_config2();
96335
96634
  init_errors();
96336
96635
  init_utils11();
96337
- logger48 = log.scope("DomainController");
96636
+ logger49 = log.scope("DomainController");
96338
96637
  add = requireDeveloper(async (ctx) => {
96339
96638
  const slug2 = ctx.params.slug;
96340
96639
  if (!slug2) {
@@ -96347,13 +96646,13 @@ var init_domain_controller = __esm(() => {
96347
96646
  } catch (error2) {
96348
96647
  if (error2 instanceof exports_external.ZodError) {
96349
96648
  const details = formatZodError(error2);
96350
- logger48.warn("Add domain validation failed", { details });
96649
+ logger49.warn("Add domain validation failed", { details });
96351
96650
  throw ApiError.unprocessableEntity("Validation failed", details);
96352
96651
  }
96353
96652
  throw ApiError.badRequest("Invalid JSON body");
96354
96653
  }
96355
96654
  const environment = getPlatformEnvironment(ctx.config);
96356
- logger48.debug("Adding domain", {
96655
+ logger49.debug("Adding domain", {
96357
96656
  userId: ctx.user.id,
96358
96657
  slug: slug2,
96359
96658
  hostname: body2.hostname,
@@ -96367,7 +96666,7 @@ var init_domain_controller = __esm(() => {
96367
96666
  throw ApiError.badRequest("Missing game slug");
96368
96667
  }
96369
96668
  const environment = getPlatformEnvironment(ctx.config);
96370
- logger48.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96669
+ logger49.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96371
96670
  const domains2 = await ctx.services.domain.list(slug2, environment, ctx.user);
96372
96671
  return { domains: domains2 };
96373
96672
  });
@@ -96382,7 +96681,7 @@ var init_domain_controller = __esm(() => {
96382
96681
  }
96383
96682
  const refresh = ctx.url.searchParams.get("refresh") === "true";
96384
96683
  const environment = getPlatformEnvironment(ctx.config);
96385
- logger48.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96684
+ logger49.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96386
96685
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
96387
96686
  });
96388
96687
  remove2 = requireDeveloper(async (ctx) => {
@@ -96395,7 +96694,7 @@ var init_domain_controller = __esm(() => {
96395
96694
  throw ApiError.badRequest("Missing hostname");
96396
96695
  }
96397
96696
  const environment = getPlatformEnvironment(ctx.config);
96398
- logger48.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96697
+ logger49.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96399
96698
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
96400
96699
  });
96401
96700
  domains2 = {
@@ -96407,7 +96706,7 @@ var init_domain_controller = __esm(() => {
96407
96706
  });
96408
96707
 
96409
96708
  // ../api-core/src/controllers/game.controller.ts
96410
- var logger49, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96709
+ var logger50, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96411
96710
  var init_game_controller = __esm(() => {
96412
96711
  init_esm();
96413
96712
  init_schemas_index();
@@ -96415,17 +96714,17 @@ var init_game_controller = __esm(() => {
96415
96714
  init_src4();
96416
96715
  init_errors();
96417
96716
  init_utils11();
96418
- logger49 = log.scope("GameController");
96717
+ logger50 = log.scope("GameController");
96419
96718
  list3 = requireNonAnonymous(async (ctx) => {
96420
- logger49.debug("Listing games", { userId: ctx.user.id });
96719
+ logger50.debug("Listing games", { userId: ctx.user.id });
96421
96720
  return ctx.services.game.list(ctx.user);
96422
96721
  });
96423
96722
  listAccessible = requireNonAnonymous(async (ctx) => {
96424
- logger49.debug("Listing accessible games", { userId: ctx.user.id });
96723
+ logger50.debug("Listing accessible games", { userId: ctx.user.id });
96425
96724
  return ctx.services.game.listAccessible(ctx.user);
96426
96725
  });
96427
96726
  getSubjects = requireNonAnonymous(async (ctx) => {
96428
- logger49.debug("Getting game subjects", { userId: ctx.user.id });
96727
+ logger50.debug("Getting game subjects", { userId: ctx.user.id });
96429
96728
  return ctx.services.game.getSubjects();
96430
96729
  });
96431
96730
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -96436,7 +96735,7 @@ var init_game_controller = __esm(() => {
96436
96735
  if (!isValidUUID(gameId)) {
96437
96736
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96438
96737
  }
96439
- logger49.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96738
+ logger50.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96440
96739
  return ctx.services.game.getById(gameId, ctx.user);
96441
96740
  });
96442
96741
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -96444,7 +96743,7 @@ var init_game_controller = __esm(() => {
96444
96743
  if (!slug2) {
96445
96744
  throw ApiError.badRequest("Missing game slug");
96446
96745
  }
96447
- logger49.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96746
+ logger50.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96448
96747
  return ctx.services.game.getBySlug(slug2, ctx.user);
96449
96748
  });
96450
96749
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -96455,7 +96754,7 @@ var init_game_controller = __esm(() => {
96455
96754
  if (!isValidUUID(gameId)) {
96456
96755
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96457
96756
  }
96458
- logger49.debug("Getting game manifest by ID", {
96757
+ logger50.debug("Getting game manifest by ID", {
96459
96758
  userId: ctx.user.id,
96460
96759
  gameId,
96461
96760
  launchId: ctx.launchId
@@ -96474,12 +96773,12 @@ var init_game_controller = __esm(() => {
96474
96773
  } catch (error2) {
96475
96774
  if (error2 instanceof exports_external.ZodError) {
96476
96775
  const details = formatZodError(error2);
96477
- logger49.warn("Upsert game validation failed", { details });
96776
+ logger50.warn("Upsert game validation failed", { details });
96478
96777
  throw ApiError.unprocessableEntity("Validation failed", details);
96479
96778
  }
96480
96779
  throw ApiError.badRequest("Invalid JSON body");
96481
96780
  }
96482
- logger49.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96781
+ logger50.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96483
96782
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
96484
96783
  });
96485
96784
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -96490,7 +96789,7 @@ var init_game_controller = __esm(() => {
96490
96789
  if (!isValidUUID(gameId)) {
96491
96790
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96492
96791
  }
96493
- logger49.debug("Deleting game", { userId: ctx.user.id, gameId });
96792
+ logger50.debug("Deleting game", { userId: ctx.user.id, gameId });
96494
96793
  await ctx.services.game.delete(gameId, ctx.user);
96495
96794
  });
96496
96795
  games2 = {
@@ -96506,16 +96805,16 @@ var init_game_controller = __esm(() => {
96506
96805
  });
96507
96806
 
96508
96807
  // ../api-core/src/controllers/inventory.controller.ts
96509
- var logger50, list4, addItem, removeItem, inventory;
96808
+ var logger51, list4, addItem, removeItem, inventory;
96510
96809
  var init_inventory_controller = __esm(() => {
96511
96810
  init_esm();
96512
96811
  init_schemas_index();
96513
96812
  init_src2();
96514
96813
  init_errors();
96515
96814
  init_utils11();
96516
- logger50 = log.scope("InventoryController");
96815
+ logger51 = log.scope("InventoryController");
96517
96816
  list4 = requireNonAnonymous(async (ctx) => {
96518
- logger50.debug("Listing inventory", { userId: ctx.user.id });
96817
+ logger51.debug("Listing inventory", { userId: ctx.user.id });
96519
96818
  return ctx.services.inventory.list(ctx.user);
96520
96819
  });
96521
96820
  addItem = requireNonAnonymous(async (ctx) => {
@@ -96526,12 +96825,12 @@ var init_inventory_controller = __esm(() => {
96526
96825
  } catch (error2) {
96527
96826
  if (error2 instanceof exports_external.ZodError) {
96528
96827
  const details = formatZodError(error2);
96529
- logger50.warn("Add inventory item validation failed", { details });
96828
+ logger51.warn("Add inventory item validation failed", { details });
96530
96829
  throw ApiError.unprocessableEntity("Invalid request body", details);
96531
96830
  }
96532
96831
  throw ApiError.badRequest("Invalid JSON body");
96533
96832
  }
96534
- logger50.debug("Adding item", {
96833
+ logger51.debug("Adding item", {
96535
96834
  userId: ctx.user.id,
96536
96835
  itemId: body2.itemId,
96537
96836
  qty: body2.qty
@@ -96546,12 +96845,12 @@ var init_inventory_controller = __esm(() => {
96546
96845
  } catch (error2) {
96547
96846
  if (error2 instanceof exports_external.ZodError) {
96548
96847
  const details = formatZodError(error2);
96549
- logger50.warn("Remove inventory item validation failed", { details });
96848
+ logger51.warn("Remove inventory item validation failed", { details });
96550
96849
  throw ApiError.unprocessableEntity("Invalid request body", details);
96551
96850
  }
96552
96851
  throw ApiError.badRequest("Invalid JSON body");
96553
96852
  }
96554
- logger50.debug("Removing item", {
96853
+ logger51.debug("Removing item", {
96555
96854
  userId: ctx.user.id,
96556
96855
  itemId: body2.itemId,
96557
96856
  qty: body2.qty
@@ -96566,7 +96865,7 @@ var init_inventory_controller = __esm(() => {
96566
96865
  });
96567
96866
 
96568
96867
  // ../api-core/src/controllers/item.controller.ts
96569
- var logger51, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96868
+ var logger52, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96570
96869
  var init_item_controller = __esm(() => {
96571
96870
  init_esm();
96572
96871
  init_schemas_index();
@@ -96574,10 +96873,10 @@ var init_item_controller = __esm(() => {
96574
96873
  init_src4();
96575
96874
  init_errors();
96576
96875
  init_utils11();
96577
- logger51 = log.scope("ItemController");
96876
+ logger52 = log.scope("ItemController");
96578
96877
  list5 = requireNonAnonymous(async (ctx) => {
96579
96878
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
96580
- logger51.debug("Listing items", { userId: ctx.user.id, gameId });
96879
+ logger52.debug("Listing items", { userId: ctx.user.id, gameId });
96581
96880
  return ctx.services.item.list(gameId);
96582
96881
  });
96583
96882
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -96588,7 +96887,7 @@ var init_item_controller = __esm(() => {
96588
96887
  if (!isValidUUID(itemId)) {
96589
96888
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96590
96889
  }
96591
- logger51.debug("Getting item", { userId: ctx.user.id, itemId });
96890
+ logger52.debug("Getting item", { userId: ctx.user.id, itemId });
96592
96891
  return ctx.services.item.getById(itemId);
96593
96892
  });
96594
96893
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -96600,7 +96899,7 @@ var init_item_controller = __esm(() => {
96600
96899
  if (gameId && !isValidUUID(gameId)) {
96601
96900
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96602
96901
  }
96603
- logger51.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96902
+ logger52.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96604
96903
  return ctx.services.item.resolveBySlug(slug2, gameId);
96605
96904
  });
96606
96905
  create3 = requireRole(["admin"], async (ctx) => {
@@ -96611,12 +96910,12 @@ var init_item_controller = __esm(() => {
96611
96910
  } catch (error2) {
96612
96911
  if (error2 instanceof exports_external.ZodError) {
96613
96912
  const details = formatZodError(error2);
96614
- logger51.warn("Create item validation failed", { details });
96913
+ logger52.warn("Create item validation failed", { details });
96615
96914
  throw ApiError.unprocessableEntity("Validation failed", details);
96616
96915
  }
96617
96916
  throw ApiError.badRequest("Invalid JSON body");
96618
96917
  }
96619
- logger51.debug("Creating item", {
96918
+ logger52.debug("Creating item", {
96620
96919
  userId: ctx.user.id,
96621
96920
  slug: body2.slug,
96622
96921
  displayName: body2.displayName
@@ -96638,7 +96937,7 @@ var init_item_controller = __esm(() => {
96638
96937
  } catch (error2) {
96639
96938
  if (error2 instanceof exports_external.ZodError) {
96640
96939
  const details = formatZodError(error2);
96641
- logger51.warn("Update item validation failed", { details });
96940
+ logger52.warn("Update item validation failed", { details });
96642
96941
  throw ApiError.unprocessableEntity("Validation failed", details);
96643
96942
  }
96644
96943
  throw ApiError.badRequest("Invalid JSON body");
@@ -96646,7 +96945,7 @@ var init_item_controller = __esm(() => {
96646
96945
  if (Object.keys(body2).length === 0) {
96647
96946
  throw ApiError.badRequest("No update data provided");
96648
96947
  }
96649
- logger51.debug("Updating item", {
96948
+ logger52.debug("Updating item", {
96650
96949
  userId: ctx.user.id,
96651
96950
  itemId,
96652
96951
  slug: body2.slug,
@@ -96663,7 +96962,7 @@ var init_item_controller = __esm(() => {
96663
96962
  if (!isValidUUID(itemId)) {
96664
96963
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96665
96964
  }
96666
- logger51.debug("Deleting item", { userId: ctx.user.id, itemId });
96965
+ logger52.debug("Deleting item", { userId: ctx.user.id, itemId });
96667
96966
  await ctx.services.item.delete(itemId);
96668
96967
  });
96669
96968
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -96674,7 +96973,7 @@ var init_item_controller = __esm(() => {
96674
96973
  if (!isValidUUID(gameId)) {
96675
96974
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96676
96975
  }
96677
- logger51.debug("Listing game items", { userId: ctx.user.id, gameId });
96976
+ logger52.debug("Listing game items", { userId: ctx.user.id, gameId });
96678
96977
  return ctx.services.item.listByGame(gameId);
96679
96978
  });
96680
96979
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -96692,12 +96991,12 @@ var init_item_controller = __esm(() => {
96692
96991
  } catch (error2) {
96693
96992
  if (error2 instanceof exports_external.ZodError) {
96694
96993
  const details = formatZodError(error2);
96695
- logger51.warn("Create game item validation failed", { details });
96994
+ logger52.warn("Create game item validation failed", { details });
96696
96995
  throw ApiError.unprocessableEntity("Validation failed", details);
96697
96996
  }
96698
96997
  throw ApiError.badRequest("Invalid JSON body");
96699
96998
  }
96700
- logger51.debug("Creating game item", {
96999
+ logger52.debug("Creating game item", {
96701
97000
  userId: ctx.user.id,
96702
97001
  gameId,
96703
97002
  slug: body2.slug,
@@ -96724,7 +97023,7 @@ var init_item_controller = __esm(() => {
96724
97023
  } catch (error2) {
96725
97024
  if (error2 instanceof exports_external.ZodError) {
96726
97025
  const details = formatZodError(error2);
96727
- logger51.warn("Update game item validation failed", { details });
97026
+ logger52.warn("Update game item validation failed", { details });
96728
97027
  throw ApiError.unprocessableEntity("Validation failed", details);
96729
97028
  }
96730
97029
  throw ApiError.badRequest("Invalid JSON body");
@@ -96732,7 +97031,7 @@ var init_item_controller = __esm(() => {
96732
97031
  if (Object.keys(body2).length === 0) {
96733
97032
  throw ApiError.badRequest("No update data provided");
96734
97033
  }
96735
- logger51.debug("Updating game item", {
97034
+ logger52.debug("Updating game item", {
96736
97035
  userId: ctx.user.id,
96737
97036
  gameId,
96738
97037
  itemId,
@@ -96754,7 +97053,7 @@ var init_item_controller = __esm(() => {
96754
97053
  if (!isValidUUID(itemId)) {
96755
97054
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96756
97055
  }
96757
- logger51.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
97056
+ logger52.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
96758
97057
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
96759
97058
  });
96760
97059
  items2 = {
@@ -96772,14 +97071,14 @@ var init_item_controller = __esm(() => {
96772
97071
  });
96773
97072
 
96774
97073
  // ../api-core/src/controllers/kv.controller.ts
96775
- var logger52, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
97074
+ var logger53, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
96776
97075
  var init_kv_controller = __esm(() => {
96777
97076
  init_esm();
96778
97077
  init_schemas_index();
96779
97078
  init_src2();
96780
97079
  init_errors();
96781
97080
  init_utils11();
96782
- logger52 = log.scope("KVController");
97081
+ logger53 = log.scope("KVController");
96783
97082
  listKeys = requireDeveloper(async (ctx) => {
96784
97083
  const slug2 = ctx.params.slug;
96785
97084
  if (!slug2) {
@@ -96787,7 +97086,7 @@ var init_kv_controller = __esm(() => {
96787
97086
  }
96788
97087
  const url2 = ctx.url;
96789
97088
  const prefix2 = url2.searchParams.get("prefix") || undefined;
96790
- logger52.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
97089
+ logger53.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96791
97090
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
96792
97091
  return { keys };
96793
97092
  });
@@ -96796,7 +97095,7 @@ var init_kv_controller = __esm(() => {
96796
97095
  if (!slug2) {
96797
97096
  throw ApiError.badRequest("Missing game slug");
96798
97097
  }
96799
- logger52.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
97098
+ logger53.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
96800
97099
  return ctx.services.kv.getStats(slug2, ctx.user);
96801
97100
  });
96802
97101
  seed = requireDeveloper(async (ctx) => {
@@ -96811,12 +97110,12 @@ var init_kv_controller = __esm(() => {
96811
97110
  } catch (error2) {
96812
97111
  if (error2 instanceof exports_external.ZodError) {
96813
97112
  const details = formatZodError(error2);
96814
- logger52.warn("Seed validation failed", { details });
97113
+ logger53.warn("Seed validation failed", { details });
96815
97114
  throw ApiError.unprocessableEntity("Validation failed", details);
96816
97115
  }
96817
97116
  throw ApiError.badRequest("Invalid JSON body");
96818
97117
  }
96819
- logger52.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
97118
+ logger53.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
96820
97119
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
96821
97120
  return { success: true, count: body2.entries.length };
96822
97121
  });
@@ -96826,7 +97125,7 @@ var init_kv_controller = __esm(() => {
96826
97125
  if (!slug2 || !key) {
96827
97126
  throw ApiError.badRequest("Missing game slug or key");
96828
97127
  }
96829
- logger52.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
97128
+ logger53.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
96830
97129
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
96831
97130
  return { key, value };
96832
97131
  });
@@ -96840,7 +97139,7 @@ var init_kv_controller = __esm(() => {
96840
97139
  if (!value) {
96841
97140
  throw ApiError.badRequest("Missing value in request body");
96842
97141
  }
96843
- logger52.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
97142
+ logger53.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
96844
97143
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
96845
97144
  return { success: true, key };
96846
97145
  });
@@ -96850,7 +97149,7 @@ var init_kv_controller = __esm(() => {
96850
97149
  if (!slug2 || !key) {
96851
97150
  throw ApiError.badRequest("Missing game slug or key");
96852
97151
  }
96853
- logger52.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
97152
+ logger53.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
96854
97153
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
96855
97154
  return { success: true, key };
96856
97155
  });
@@ -96860,7 +97159,7 @@ var init_kv_controller = __esm(() => {
96860
97159
  if (!slug2 || !key) {
96861
97160
  throw ApiError.badRequest("Missing game slug or key");
96862
97161
  }
96863
- logger52.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
97162
+ logger53.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
96864
97163
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
96865
97164
  return { key, metadata: metadata2 };
96866
97165
  });
@@ -96869,21 +97168,21 @@ var init_kv_controller = __esm(() => {
96869
97168
  if (!slug2) {
96870
97169
  throw ApiError.badRequest("Missing game slug");
96871
97170
  }
96872
- logger52.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
97171
+ logger53.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
96873
97172
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
96874
97173
  return { success: true, deleted };
96875
97174
  });
96876
97175
  });
96877
97176
 
96878
97177
  // ../api-core/src/controllers/leaderboard.controller.ts
96879
- var logger53, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
97178
+ var logger54, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
96880
97179
  var init_leaderboard_controller = __esm(() => {
96881
97180
  init_esm();
96882
97181
  init_schemas_index();
96883
97182
  init_src2();
96884
97183
  init_errors();
96885
97184
  init_utils11();
96886
- logger53 = log.scope("LeaderboardController");
97185
+ logger54 = log.scope("LeaderboardController");
96887
97186
  submitScore = requireAuth(async (ctx) => {
96888
97187
  const gameId = ctx.params.gameId;
96889
97188
  if (!gameId) {
@@ -96896,12 +97195,12 @@ var init_leaderboard_controller = __esm(() => {
96896
97195
  } catch (error2) {
96897
97196
  if (error2 instanceof exports_external.ZodError) {
96898
97197
  const details = formatZodError(error2);
96899
- logger53.warn("Submit score validation failed", { details });
97198
+ logger54.warn("Submit score validation failed", { details });
96900
97199
  throw ApiError.unprocessableEntity("Validation failed", details);
96901
97200
  }
96902
97201
  throw ApiError.badRequest("Invalid JSON body");
96903
97202
  }
96904
- logger53.debug("Submitting score", {
97203
+ logger54.debug("Submitting score", {
96905
97204
  userId: ctx.user.id,
96906
97205
  gameId,
96907
97206
  score: body2.score
@@ -96924,12 +97223,12 @@ var init_leaderboard_controller = __esm(() => {
96924
97223
  } catch (error2) {
96925
97224
  if (error2 instanceof exports_external.ZodError) {
96926
97225
  const details = formatZodError(error2);
96927
- logger53.warn("Get global leaderboard query validation failed", { details });
97226
+ logger54.warn("Get global leaderboard query validation failed", { details });
96928
97227
  throw ApiError.badRequest("Invalid query parameters", details);
96929
97228
  }
96930
97229
  throw ApiError.badRequest("Invalid query parameters");
96931
97230
  }
96932
- logger53.debug("Getting global leaderboard", {
97231
+ logger54.debug("Getting global leaderboard", {
96933
97232
  userId: ctx.user.id,
96934
97233
  gameId,
96935
97234
  ...query
@@ -96952,12 +97251,12 @@ var init_leaderboard_controller = __esm(() => {
96952
97251
  } catch (error2) {
96953
97252
  if (error2 instanceof exports_external.ZodError) {
96954
97253
  const details = formatZodError(error2);
96955
- logger53.warn("Get leaderboard query validation failed", { details });
97254
+ logger54.warn("Get leaderboard query validation failed", { details });
96956
97255
  throw ApiError.badRequest("Invalid query parameters", details);
96957
97256
  }
96958
97257
  throw ApiError.badRequest("Invalid query parameters");
96959
97258
  }
96960
- logger53.debug("Getting leaderboard", {
97259
+ logger54.debug("Getting leaderboard", {
96961
97260
  userId: ctx.user.id,
96962
97261
  gameId,
96963
97262
  ...query
@@ -96969,7 +97268,7 @@ var init_leaderboard_controller = __esm(() => {
96969
97268
  if (!gameId || !userId) {
96970
97269
  throw ApiError.badRequest("Game ID and User ID are required");
96971
97270
  }
96972
- logger53.debug("Getting user rank", {
97271
+ logger54.debug("Getting user rank", {
96973
97272
  requesterId: ctx.user.id,
96974
97273
  gameId,
96975
97274
  targetUserId: userId
@@ -96984,7 +97283,7 @@ var init_leaderboard_controller = __esm(() => {
96984
97283
  const url2 = ctx.url;
96985
97284
  const limit = Math.min(Number(url2.searchParams.get("limit") || "50"), 100);
96986
97285
  const gameId = url2.searchParams.get("gameId") || undefined;
96987
- logger53.debug("Getting user all scores", {
97286
+ logger54.debug("Getting user all scores", {
96988
97287
  requesterId: ctx.user.id,
96989
97288
  targetUserId: userId,
96990
97289
  gameId,
@@ -96999,7 +97298,7 @@ var init_leaderboard_controller = __esm(() => {
96999
97298
  }
97000
97299
  const url2 = ctx.url;
97001
97300
  const limit = Math.min(Number(url2.searchParams.get("limit") || "10"), 100);
97002
- logger53.debug("Getting user scores", {
97301
+ logger54.debug("Getting user scores", {
97003
97302
  requesterId: ctx.user.id,
97004
97303
  gameId,
97005
97304
  targetUserId: userId,
@@ -97019,7 +97318,7 @@ var init_leaderboard_controller = __esm(() => {
97019
97318
 
97020
97319
  // ../api-core/src/controllers/level.controller.ts
97021
97320
  async function listConfigs(ctx) {
97022
- logger54.debug("Listing level configs");
97321
+ logger55.debug("Listing level configs");
97023
97322
  return ctx.services.level.listConfigs();
97024
97323
  }
97025
97324
  async function getConfig(ctx) {
@@ -97031,21 +97330,21 @@ async function getConfig(ctx) {
97031
97330
  if (isNaN(level) || level < 1) {
97032
97331
  throw ApiError.badRequest("Level must be a positive integer");
97033
97332
  }
97034
- logger54.debug("Getting level config", { level });
97333
+ logger55.debug("Getting level config", { level });
97035
97334
  return ctx.services.level.getConfig(level);
97036
97335
  }
97037
- var logger54, getByUser, getProgress, levels;
97336
+ var logger55, getByUser, getProgress, levels;
97038
97337
  var init_level_controller = __esm(() => {
97039
97338
  init_src2();
97040
97339
  init_errors();
97041
97340
  init_utils11();
97042
- logger54 = log.scope("LevelController");
97341
+ logger55 = log.scope("LevelController");
97043
97342
  getByUser = requireNonAnonymous(async (ctx) => {
97044
- logger54.debug("Getting user level", { userId: ctx.user.id });
97343
+ logger55.debug("Getting user level", { userId: ctx.user.id });
97045
97344
  return ctx.services.level.getByUser(ctx.user);
97046
97345
  });
97047
97346
  getProgress = requireNonAnonymous(async (ctx) => {
97048
- logger54.debug("Getting level progress", { userId: ctx.user.id });
97347
+ logger55.debug("Getting level progress", { userId: ctx.user.id });
97049
97348
  return ctx.services.level.getProgress(ctx.user);
97050
97349
  });
97051
97350
  levels = {
@@ -97057,12 +97356,12 @@ var init_level_controller = __esm(() => {
97057
97356
  });
97058
97357
 
97059
97358
  // ../api-core/src/controllers/logs.controller.ts
97060
- var logger55, generateToken, logs;
97359
+ var logger56, generateToken, logs;
97061
97360
  var init_logs_controller = __esm(() => {
97062
97361
  init_src2();
97063
97362
  init_errors();
97064
97363
  init_utils11();
97065
- logger55 = log.scope("LogsController");
97364
+ logger56 = log.scope("LogsController");
97066
97365
  generateToken = requireDeveloper(async (ctx) => {
97067
97366
  const slug2 = ctx.params.slug;
97068
97367
  if (!slug2) {
@@ -97081,7 +97380,7 @@ var init_logs_controller = __esm(() => {
97081
97380
  }
97082
97381
  throw ApiError.badRequest("Invalid JSON body");
97083
97382
  }
97084
- logger55.debug("Generating log stream token", {
97383
+ logger56.debug("Generating log stream token", {
97085
97384
  userId: ctx.user.id,
97086
97385
  slug: slug2,
97087
97386
  environment: body2.environment
@@ -97100,13 +97399,13 @@ var init_logs_controller = __esm(() => {
97100
97399
  });
97101
97400
 
97102
97401
  // ../api-core/src/controllers/lti.controller.ts
97103
- var logger56, getStatus3, lti;
97402
+ var logger57, getStatus3, lti;
97104
97403
  var init_lti_controller = __esm(() => {
97105
97404
  init_src2();
97106
97405
  init_utils11();
97107
- logger56 = log.scope("LtiController");
97406
+ logger57 = log.scope("LtiController");
97108
97407
  getStatus3 = requireNonAnonymous(async (ctx) => {
97109
- logger56.debug("Getting status", { userId: ctx.user.id });
97408
+ logger57.debug("Getting status", { userId: ctx.user.id });
97110
97409
  return ctx.services.lti.getStatus(ctx.user);
97111
97410
  });
97112
97411
  lti = {
@@ -97115,7 +97414,7 @@ var init_lti_controller = __esm(() => {
97115
97414
  });
97116
97415
 
97117
97416
  // ../api-core/src/controllers/map.controller.ts
97118
- var logger57, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97417
+ var logger58, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97119
97418
  var init_map_controller = __esm(() => {
97120
97419
  init_esm();
97121
97420
  init_schemas_index();
@@ -97123,13 +97422,13 @@ var init_map_controller = __esm(() => {
97123
97422
  init_src4();
97124
97423
  init_errors();
97125
97424
  init_utils11();
97126
- logger57 = log.scope("MapController");
97425
+ logger58 = log.scope("MapController");
97127
97426
  getByIdentifier = requireNonAnonymous(async (ctx) => {
97128
97427
  const identifier = ctx.params.identifier;
97129
97428
  if (!identifier) {
97130
97429
  throw ApiError.badRequest("Missing map identifier");
97131
97430
  }
97132
- logger57.debug("Getting map", { userId: ctx.user.id, identifier });
97431
+ logger58.debug("Getting map", { userId: ctx.user.id, identifier });
97133
97432
  return ctx.services.map.getByIdentifier(identifier);
97134
97433
  });
97135
97434
  getElements = requireNonAnonymous(async (ctx) => {
@@ -97140,7 +97439,7 @@ var init_map_controller = __esm(() => {
97140
97439
  if (!isValidUUID(mapId)) {
97141
97440
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97142
97441
  }
97143
- logger57.debug("Getting map elements", { userId: ctx.user.id, mapId });
97442
+ logger58.debug("Getting map elements", { userId: ctx.user.id, mapId });
97144
97443
  return ctx.services.map.getElements(mapId);
97145
97444
  });
97146
97445
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -97151,7 +97450,7 @@ var init_map_controller = __esm(() => {
97151
97450
  if (!isValidUUID(mapId)) {
97152
97451
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97153
97452
  }
97154
- logger57.debug("Getting map objects", { userId: ctx.user.id, mapId });
97453
+ logger58.debug("Getting map objects", { userId: ctx.user.id, mapId });
97155
97454
  return ctx.services.map.getObjects(mapId, ctx.user.id);
97156
97455
  });
97157
97456
  createObject = requireNonAnonymous(async (ctx) => {
@@ -97173,12 +97472,12 @@ var init_map_controller = __esm(() => {
97173
97472
  } catch (error2) {
97174
97473
  if (error2 instanceof exports_external.ZodError) {
97175
97474
  const details = formatZodError(error2);
97176
- logger57.warn("Create map object validation failed", { details });
97475
+ logger58.warn("Create map object validation failed", { details });
97177
97476
  throw ApiError.unprocessableEntity("Validation failed", details);
97178
97477
  }
97179
97478
  throw ApiError.badRequest("Invalid JSON body");
97180
97479
  }
97181
- logger57.debug("Creating map object", {
97480
+ logger58.debug("Creating map object", {
97182
97481
  userId: ctx.user.id,
97183
97482
  mapId,
97184
97483
  itemId: body2.itemId,
@@ -97202,7 +97501,7 @@ var init_map_controller = __esm(() => {
97202
97501
  if (!isValidUUID(objectId)) {
97203
97502
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
97204
97503
  }
97205
- logger57.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97504
+ logger58.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97206
97505
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
97207
97506
  });
97208
97507
  maps2 = {
@@ -97215,14 +97514,14 @@ var init_map_controller = __esm(() => {
97215
97514
  });
97216
97515
 
97217
97516
  // ../api-core/src/controllers/notification.controller.ts
97218
- var logger58, list6, updateStatus, getStats2, create4, deliver, notifications2;
97517
+ var logger59, list6, updateStatus, getStats2, create4, deliver, notifications2;
97219
97518
  var init_notification_controller = __esm(() => {
97220
97519
  init_esm();
97221
97520
  init_schemas_index();
97222
97521
  init_src2();
97223
97522
  init_errors();
97224
97523
  init_utils11();
97225
- logger58 = log.scope("NotificationController");
97524
+ logger59 = log.scope("NotificationController");
97226
97525
  list6 = requireNonAnonymous(async (ctx) => {
97227
97526
  const query = {
97228
97527
  status: ctx.url.searchParams.get("status") || undefined,
@@ -97233,10 +97532,10 @@ var init_notification_controller = __esm(() => {
97233
97532
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
97234
97533
  if (!result.success) {
97235
97534
  const details = formatZodError(result.error);
97236
- logger58.warn("List notifications query validation failed", { details });
97535
+ logger59.warn("List notifications query validation failed", { details });
97237
97536
  throw ApiError.badRequest("Invalid query parameters", details);
97238
97537
  }
97239
- logger58.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97538
+ logger59.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97240
97539
  return ctx.services.notification.list(ctx.user, result.data);
97241
97540
  });
97242
97541
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -97251,12 +97550,12 @@ var init_notification_controller = __esm(() => {
97251
97550
  } catch (error2) {
97252
97551
  if (error2 instanceof exports_external.ZodError) {
97253
97552
  const details = formatZodError(error2);
97254
- logger58.warn("Update notification status validation failed", { details });
97553
+ logger59.warn("Update notification status validation failed", { details });
97255
97554
  throw ApiError.unprocessableEntity("Invalid request body", details);
97256
97555
  }
97257
97556
  throw ApiError.badRequest("Invalid JSON body");
97258
97557
  }
97259
- logger58.debug("Updating status", {
97558
+ logger59.debug("Updating status", {
97260
97559
  userId: ctx.user.id,
97261
97560
  notificationId,
97262
97561
  status: body2.status
@@ -97266,7 +97565,7 @@ var init_notification_controller = __esm(() => {
97266
97565
  getStats2 = requireNonAnonymous(async (ctx) => {
97267
97566
  const startDate = ctx.url.searchParams.get("startDate");
97268
97567
  const endDate = ctx.url.searchParams.get("endDate");
97269
- logger58.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97568
+ logger59.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97270
97569
  return ctx.services.notification.getStats(ctx.user, {
97271
97570
  startDate: startDate ? new Date(startDate) : undefined,
97272
97571
  endDate: endDate ? new Date(endDate) : undefined
@@ -97280,12 +97579,12 @@ var init_notification_controller = __esm(() => {
97280
97579
  } catch (error2) {
97281
97580
  if (error2 instanceof exports_external.ZodError) {
97282
97581
  const details = formatZodError(error2);
97283
- logger58.warn("Create notification validation failed", { details });
97582
+ logger59.warn("Create notification validation failed", { details });
97284
97583
  throw ApiError.unprocessableEntity("Invalid request body", details);
97285
97584
  }
97286
97585
  throw ApiError.badRequest("Invalid JSON body");
97287
97586
  }
97288
- logger58.debug("Creating notification", {
97587
+ logger59.debug("Creating notification", {
97289
97588
  userId: ctx.user.id,
97290
97589
  targetUserId: body2.userId,
97291
97590
  type: body2.type
@@ -97303,12 +97602,12 @@ var init_notification_controller = __esm(() => {
97303
97602
  });
97304
97603
  });
97305
97604
  deliver = requireNonAnonymous(async (ctx) => {
97306
- logger58.debug("Delivering notifications", { userId: ctx.user.id });
97605
+ logger59.debug("Delivering notifications", { userId: ctx.user.id });
97307
97606
  try {
97308
97607
  await ctx.services.notification.deliverPending(ctx.user.id);
97309
97608
  return { success: true };
97310
97609
  } catch (error2) {
97311
- logger58.error("Failed to deliver notifications", { error: error2 });
97610
+ logger59.error("Failed to deliver notifications", { error: error2 });
97312
97611
  throw ApiError.internal("Failed to deliver notifications");
97313
97612
  }
97314
97613
  });
@@ -97322,14 +97621,14 @@ var init_notification_controller = __esm(() => {
97322
97621
  });
97323
97622
 
97324
97623
  // ../api-core/src/controllers/realtime.controller.ts
97325
- var logger59, generateToken2, realtime;
97624
+ var logger60, generateToken2, realtime;
97326
97625
  var init_realtime_controller = __esm(() => {
97327
97626
  init_src2();
97328
97627
  init_utils11();
97329
- logger59 = log.scope("RealtimeController");
97628
+ logger60 = log.scope("RealtimeController");
97330
97629
  generateToken2 = requireNonAnonymous(async (ctx) => {
97331
97630
  const gameIdOrSlug = ctx.params.gameId;
97332
- logger59.debug("Generating token", {
97631
+ logger60.debug("Generating token", {
97333
97632
  userId: ctx.user.id,
97334
97633
  gameId: gameIdOrSlug || "global",
97335
97634
  launchId: ctx.launchId
@@ -97342,20 +97641,20 @@ var init_realtime_controller = __esm(() => {
97342
97641
  });
97343
97642
 
97344
97643
  // ../api-core/src/controllers/secrets.controller.ts
97345
- var logger60, listKeys2, setSecrets, deleteSecret, secrets;
97644
+ var logger61, listKeys2, setSecrets, deleteSecret, secrets;
97346
97645
  var init_secrets_controller = __esm(() => {
97347
97646
  init_esm();
97348
97647
  init_schemas_index();
97349
97648
  init_src2();
97350
97649
  init_errors();
97351
97650
  init_utils11();
97352
- logger60 = log.scope("SecretsController");
97651
+ logger61 = log.scope("SecretsController");
97353
97652
  listKeys2 = requireDeveloper(async (ctx) => {
97354
97653
  const slug2 = ctx.params.slug;
97355
97654
  if (!slug2) {
97356
97655
  throw ApiError.badRequest("Missing game slug");
97357
97656
  }
97358
- logger60.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97657
+ logger61.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97359
97658
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
97360
97659
  return { keys };
97361
97660
  });
@@ -97371,12 +97670,12 @@ var init_secrets_controller = __esm(() => {
97371
97670
  } catch (error2) {
97372
97671
  if (error2 instanceof exports_external.ZodError) {
97373
97672
  const details = formatZodError(error2);
97374
- logger60.warn("Set secrets validation failed", { details });
97673
+ logger61.warn("Set secrets validation failed", { details });
97375
97674
  throw ApiError.unprocessableEntity("Validation failed", details);
97376
97675
  }
97377
97676
  throw ApiError.badRequest("Invalid JSON body");
97378
97677
  }
97379
- logger60.debug("Setting secrets", {
97678
+ logger61.debug("Setting secrets", {
97380
97679
  userId: ctx.user.id,
97381
97680
  slug: slug2,
97382
97681
  keyCount: Object.keys(body2).length
@@ -97393,7 +97692,7 @@ var init_secrets_controller = __esm(() => {
97393
97692
  if (!key) {
97394
97693
  throw ApiError.badRequest("Missing secret key");
97395
97694
  }
97396
- logger60.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97695
+ logger61.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97397
97696
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
97398
97697
  return { success: true };
97399
97698
  });
@@ -97405,14 +97704,14 @@ var init_secrets_controller = __esm(() => {
97405
97704
  });
97406
97705
 
97407
97706
  // ../api-core/src/controllers/seed.controller.ts
97408
- var logger61, seed2;
97707
+ var logger62, seed2;
97409
97708
  var init_seed_controller = __esm(() => {
97410
97709
  init_esm();
97411
97710
  init_schemas_index();
97412
97711
  init_src2();
97413
97712
  init_errors();
97414
97713
  init_utils11();
97415
- logger61 = log.scope("SeedController");
97714
+ logger62 = log.scope("SeedController");
97416
97715
  seed2 = requireDeveloper(async (ctx) => {
97417
97716
  const slug2 = ctx.params.slug;
97418
97717
  if (!slug2) {
@@ -97425,12 +97724,12 @@ var init_seed_controller = __esm(() => {
97425
97724
  } catch (error2) {
97426
97725
  if (error2 instanceof exports_external.ZodError) {
97427
97726
  const details = formatZodError(error2);
97428
- logger61.warn("Seed database validation failed", { details });
97727
+ logger62.warn("Seed database validation failed", { details });
97429
97728
  throw ApiError.unprocessableEntity("Validation failed", details);
97430
97729
  }
97431
97730
  throw ApiError.badRequest("Invalid JSON body");
97432
97731
  }
97433
- logger61.debug("Seeding database", {
97732
+ logger62.debug("Seeding database", {
97434
97733
  userId: ctx.user.id,
97435
97734
  slug: slug2,
97436
97735
  codeLength: body2.code.length,
@@ -97441,19 +97740,19 @@ var init_seed_controller = __esm(() => {
97441
97740
  });
97442
97741
 
97443
97742
  // ../api-core/src/controllers/session.controller.ts
97444
- var logger62, start2, end, mintToken, sessions2;
97743
+ var logger63, start2, end, mintToken, sessions2;
97445
97744
  var init_session_controller = __esm(() => {
97446
97745
  init_src2();
97447
97746
  init_tunnel();
97448
97747
  init_errors();
97449
97748
  init_utils11();
97450
- logger62 = log.scope("SessionController");
97749
+ logger63 = log.scope("SessionController");
97451
97750
  start2 = requireAuth(async (ctx) => {
97452
97751
  const gameIdOrSlug = ctx.params.gameId;
97453
97752
  if (!gameIdOrSlug) {
97454
97753
  throw ApiError.badRequest("Missing game ID or slug");
97455
97754
  }
97456
- logger62.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97755
+ logger63.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97457
97756
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
97458
97757
  });
97459
97758
  end = requireAuth(async (ctx) => {
@@ -97465,7 +97764,7 @@ var init_session_controller = __esm(() => {
97465
97764
  if (!sessionId) {
97466
97765
  throw ApiError.badRequest("Missing session ID");
97467
97766
  }
97468
- logger62.debug("Ending session", {
97767
+ logger63.debug("Ending session", {
97469
97768
  userId: ctx.user.id,
97470
97769
  gameIdOrSlug,
97471
97770
  sessionId,
@@ -97478,7 +97777,7 @@ var init_session_controller = __esm(() => {
97478
97777
  if (!gameIdOrSlug) {
97479
97778
  throw ApiError.badRequest("Missing game ID or slug");
97480
97779
  }
97481
- logger62.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97780
+ logger63.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97482
97781
  const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
97483
97782
  let baseUrl;
97484
97783
  if (ctx.config.isLocal) {
@@ -97496,13 +97795,13 @@ var init_session_controller = __esm(() => {
97496
97795
  });
97497
97796
 
97498
97797
  // ../api-core/src/controllers/shop.controller.ts
97499
- var logger63, getShopView, shop;
97798
+ var logger64, getShopView, shop;
97500
97799
  var init_shop_controller = __esm(() => {
97501
97800
  init_src2();
97502
97801
  init_utils11();
97503
- logger63 = log.scope("ShopController");
97802
+ logger64 = log.scope("ShopController");
97504
97803
  getShopView = requireNonAnonymous(async (ctx) => {
97505
- logger63.debug("Getting shop view", { userId: ctx.user.id });
97804
+ logger64.debug("Getting shop view", { userId: ctx.user.id });
97506
97805
  return ctx.services.shop.getShopView(ctx.user);
97507
97806
  });
97508
97807
  shop = {
@@ -97511,7 +97810,7 @@ var init_shop_controller = __esm(() => {
97511
97810
  });
97512
97811
 
97513
97812
  // ../api-core/src/controllers/shop-listing.controller.ts
97514
- var logger64, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97813
+ var logger65, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97515
97814
  var init_shop_listing_controller = __esm(() => {
97516
97815
  init_esm();
97517
97816
  init_schemas_index();
@@ -97519,9 +97818,9 @@ var init_shop_listing_controller = __esm(() => {
97519
97818
  init_src4();
97520
97819
  init_errors();
97521
97820
  init_utils11();
97522
- logger64 = log.scope("ShopListingController");
97821
+ logger65 = log.scope("ShopListingController");
97523
97822
  list7 = requireAdmin(async (ctx) => {
97524
- logger64.debug("Listing shop listings", { userId: ctx.user.id });
97823
+ logger65.debug("Listing shop listings", { userId: ctx.user.id });
97525
97824
  return ctx.services.shopListing.list();
97526
97825
  });
97527
97826
  getById4 = requireAdmin(async (ctx) => {
@@ -97532,7 +97831,7 @@ var init_shop_listing_controller = __esm(() => {
97532
97831
  if (!isValidUUID(listingId)) {
97533
97832
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97534
97833
  }
97535
- logger64.debug("Getting listing", { userId: ctx.user.id, listingId });
97834
+ logger65.debug("Getting listing", { userId: ctx.user.id, listingId });
97536
97835
  return ctx.services.shopListing.getById(listingId);
97537
97836
  });
97538
97837
  create5 = requireAdmin(async (ctx) => {
@@ -97543,12 +97842,12 @@ var init_shop_listing_controller = __esm(() => {
97543
97842
  } catch (error2) {
97544
97843
  if (error2 instanceof exports_external.ZodError) {
97545
97844
  const details = formatZodError(error2);
97546
- logger64.warn("Create shop listing validation failed", { details });
97845
+ logger65.warn("Create shop listing validation failed", { details });
97547
97846
  throw ApiError.unprocessableEntity("Validation failed", details);
97548
97847
  }
97549
97848
  throw ApiError.badRequest("Invalid JSON body");
97550
97849
  }
97551
- logger64.debug("Creating listing", {
97850
+ logger65.debug("Creating listing", {
97552
97851
  userId: ctx.user.id,
97553
97852
  itemId: body2.itemId,
97554
97853
  currencyId: body2.currencyId,
@@ -97571,12 +97870,12 @@ var init_shop_listing_controller = __esm(() => {
97571
97870
  } catch (error2) {
97572
97871
  if (error2 instanceof exports_external.ZodError) {
97573
97872
  const details = formatZodError(error2);
97574
- logger64.warn("Update shop listing validation failed", { details });
97873
+ logger65.warn("Update shop listing validation failed", { details });
97575
97874
  throw ApiError.unprocessableEntity("Validation failed", details);
97576
97875
  }
97577
97876
  throw ApiError.badRequest("Invalid JSON body");
97578
97877
  }
97579
- logger64.debug("Updating listing", {
97878
+ logger65.debug("Updating listing", {
97580
97879
  userId: ctx.user.id,
97581
97880
  listingId,
97582
97881
  price: body2.price,
@@ -97593,7 +97892,7 @@ var init_shop_listing_controller = __esm(() => {
97593
97892
  if (!isValidUUID(listingId)) {
97594
97893
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97595
97894
  }
97596
- logger64.debug("Deleting listing", { userId: ctx.user.id, listingId });
97895
+ logger65.debug("Deleting listing", { userId: ctx.user.id, listingId });
97597
97896
  await ctx.services.shopListing.delete(listingId);
97598
97897
  });
97599
97898
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -97604,7 +97903,7 @@ var init_shop_listing_controller = __esm(() => {
97604
97903
  if (!isValidUUID(gameId)) {
97605
97904
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
97606
97905
  }
97607
- logger64.debug("Listing game listings", { userId: ctx.user.id, gameId });
97906
+ logger65.debug("Listing game listings", { userId: ctx.user.id, gameId });
97608
97907
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
97609
97908
  });
97610
97909
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -97619,7 +97918,7 @@ var init_shop_listing_controller = __esm(() => {
97619
97918
  if (!isValidUUID(itemId)) {
97620
97919
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97621
97920
  }
97622
- logger64.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97921
+ logger65.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97623
97922
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
97624
97923
  });
97625
97924
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -97641,12 +97940,12 @@ var init_shop_listing_controller = __esm(() => {
97641
97940
  } catch (error2) {
97642
97941
  if (error2 instanceof exports_external.ZodError) {
97643
97942
  const details = formatZodError(error2);
97644
- logger64.warn("Create game item listing validation failed", { details });
97943
+ logger65.warn("Create game item listing validation failed", { details });
97645
97944
  throw ApiError.unprocessableEntity("Validation failed", details);
97646
97945
  }
97647
97946
  throw ApiError.badRequest("Invalid JSON body");
97648
97947
  }
97649
- logger64.debug("Creating game item listing", {
97948
+ logger65.debug("Creating game item listing", {
97650
97949
  userId: ctx.user.id,
97651
97950
  gameId,
97652
97951
  itemId,
@@ -97674,12 +97973,12 @@ var init_shop_listing_controller = __esm(() => {
97674
97973
  } catch (error2) {
97675
97974
  if (error2 instanceof exports_external.ZodError) {
97676
97975
  const details = formatZodError(error2);
97677
- logger64.warn("Update game item listing validation failed", { details });
97976
+ logger65.warn("Update game item listing validation failed", { details });
97678
97977
  throw ApiError.unprocessableEntity("Validation failed", details);
97679
97978
  }
97680
97979
  throw ApiError.badRequest("Invalid JSON body");
97681
97980
  }
97682
- logger64.debug("Updating game item listing", {
97981
+ logger65.debug("Updating game item listing", {
97683
97982
  userId: ctx.user.id,
97684
97983
  gameId,
97685
97984
  itemId,
@@ -97701,7 +98000,7 @@ var init_shop_listing_controller = __esm(() => {
97701
98000
  if (!isValidUUID(itemId)) {
97702
98001
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97703
98002
  }
97704
- logger64.debug("Deleting game item listing", {
98003
+ logger65.debug("Deleting game item listing", {
97705
98004
  userId: ctx.user.id,
97706
98005
  gameId,
97707
98006
  itemId
@@ -97728,21 +98027,21 @@ async function getBySlug2(ctx) {
97728
98027
  if (!slug2) {
97729
98028
  throw ApiError.badRequest("Template slug is required");
97730
98029
  }
97731
- logger65.debug("Getting sprite by slug", { slug: slug2 });
98030
+ logger66.debug("Getting sprite by slug", { slug: slug2 });
97732
98031
  return ctx.services.sprite.getBySlug(slug2);
97733
98032
  }
97734
- var logger65, sprites;
98033
+ var logger66, sprites;
97735
98034
  var init_sprite_controller = __esm(() => {
97736
98035
  init_src2();
97737
98036
  init_errors();
97738
- logger65 = log.scope("SpriteController");
98037
+ logger66 = log.scope("SpriteController");
97739
98038
  sprites = {
97740
98039
  getBySlug: getBySlug2
97741
98040
  };
97742
98041
  });
97743
98042
 
97744
98043
  // ../api-core/src/controllers/timeback.controller.ts
97745
- var logger66, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, deleteAssessment, reorderAssessments, reorderQuestions, activateAssessment, deactivateAssessment, listQuestions, createQuestion, updateQuestion, deleteQuestion, getAssessmentBankStatus, destroyAssessmentBank, timeback2;
98044
+ var logger67, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, updateIntegration, getIntegrationConfig, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, deleteAssessment, reorderAssessments, reorderQuestions, activateAssessment, deactivateAssessment, listQuestions, createQuestion, updateQuestion, deleteQuestion, getAssessmentBankStatus, destroyAssessmentBank, timeback2;
97746
98045
  var init_timeback_controller = __esm(() => {
97747
98046
  init_esm();
97748
98047
  init_schemas_index();
@@ -97750,15 +98049,15 @@ var init_timeback_controller = __esm(() => {
97750
98049
  init_src4();
97751
98050
  init_errors();
97752
98051
  init_utils11();
97753
- logger66 = log.scope("TimebackController");
98052
+ logger67 = log.scope("TimebackController");
97754
98053
  getTodayXp = requireNonAnonymous(async (ctx) => {
97755
98054
  const date4 = ctx.url.searchParams.get("date") || undefined;
97756
98055
  const tz = ctx.url.searchParams.get("tz") || undefined;
97757
- logger66.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
98056
+ logger67.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
97758
98057
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
97759
98058
  });
97760
98059
  getTotalXp = requireNonAnonymous(async (ctx) => {
97761
- logger66.debug("Getting total XP", { userId: ctx.user.id });
98060
+ logger67.debug("Getting total XP", { userId: ctx.user.id });
97762
98061
  return ctx.services.timeback.getTotalXp(ctx.user.id);
97763
98062
  });
97764
98063
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -97769,18 +98068,18 @@ var init_timeback_controller = __esm(() => {
97769
98068
  } catch (error2) {
97770
98069
  if (error2 instanceof exports_external.ZodError) {
97771
98070
  const details = formatZodError(error2);
97772
- logger66.warn("Update today XP validation failed", { details });
98071
+ logger67.warn("Update today XP validation failed", { details });
97773
98072
  throw ApiError.unprocessableEntity("Validation failed", details);
97774
98073
  }
97775
98074
  throw ApiError.badRequest("Invalid JSON body");
97776
98075
  }
97777
- logger66.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
98076
+ logger67.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
97778
98077
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
97779
98078
  });
97780
98079
  getXpHistory = requireNonAnonymous(async (ctx) => {
97781
98080
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
97782
98081
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
97783
- logger66.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
98082
+ logger67.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
97784
98083
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
97785
98084
  });
97786
98085
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -97791,18 +98090,18 @@ var init_timeback_controller = __esm(() => {
97791
98090
  } catch (error2) {
97792
98091
  if (error2 instanceof exports_external.ZodError) {
97793
98092
  const details = formatZodError(error2);
97794
- logger66.warn("Populate student validation failed", { details });
98093
+ logger67.warn("Populate student validation failed", { details });
97795
98094
  throw ApiError.unprocessableEntity("Validation failed", details);
97796
98095
  }
97797
98096
  }
97798
- logger66.debug("Populating student", {
98097
+ logger67.debug("Populating student", {
97799
98098
  userId: ctx.user.id,
97800
98099
  hasProvidedNames: Boolean(providedNames)
97801
98100
  });
97802
98101
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
97803
98102
  });
97804
98103
  getUser = requireNonAnonymous(async (ctx) => {
97805
- logger66.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
98104
+ logger67.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
97806
98105
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
97807
98106
  });
97808
98107
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -97810,7 +98109,7 @@ var init_timeback_controller = __esm(() => {
97810
98109
  if (!timebackId) {
97811
98110
  throw ApiError.badRequest("Missing timebackId parameter");
97812
98111
  }
97813
- logger66.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
98112
+ logger67.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
97814
98113
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
97815
98114
  });
97816
98115
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -97821,12 +98120,12 @@ var init_timeback_controller = __esm(() => {
97821
98120
  } catch (error2) {
97822
98121
  if (error2 instanceof exports_external.ZodError) {
97823
98122
  const details = formatZodError(error2);
97824
- logger66.warn("Setup integration validation failed", { details });
98123
+ logger67.warn("Setup integration validation failed", { details });
97825
98124
  throw ApiError.unprocessableEntity("Validation failed", details);
97826
98125
  }
97827
98126
  throw ApiError.badRequest("Invalid JSON body");
97828
98127
  }
97829
- logger66.debug("Setting up integration", {
98128
+ logger67.debug("Setting up integration", {
97830
98129
  userId: ctx.user.id,
97831
98130
  gameId: body2.gameId
97832
98131
  });
@@ -97840,9 +98139,37 @@ var init_timeback_controller = __esm(() => {
97840
98139
  if (!isValidUUID(gameId)) {
97841
98140
  throw ApiError.unprocessableEntity("Invalid gameId format");
97842
98141
  }
97843
- logger66.debug("Getting integrations", { userId: ctx.user.id, gameId });
98142
+ logger67.debug("Getting integrations", { userId: ctx.user.id, gameId });
97844
98143
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
97845
98144
  });
98145
+ updateIntegration = requireDeveloper(async (ctx) => {
98146
+ const { gameId, courseId } = ctx.params;
98147
+ if (!gameId || !courseId) {
98148
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98149
+ }
98150
+ if (!isValidUUID(gameId)) {
98151
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98152
+ }
98153
+ const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
98154
+ logger67.debug("Updating integration", {
98155
+ userId: ctx.user.id,
98156
+ gameId,
98157
+ courseId,
98158
+ fields: Object.keys(body2)
98159
+ });
98160
+ return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
98161
+ });
98162
+ getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
98163
+ const { gameId, courseId } = ctx.params;
98164
+ if (!gameId || !courseId) {
98165
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98166
+ }
98167
+ if (!isValidUUID(gameId)) {
98168
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98169
+ }
98170
+ logger67.debug("Getting integration config", { userId: ctx.user.id, gameId, courseId });
98171
+ return ctx.services.timeback.getIntegrationConfig(gameId, courseId, ctx.user);
98172
+ });
97846
98173
  verifyIntegration = requireDeveloper(async (ctx) => {
97847
98174
  const gameId = ctx.params.gameId;
97848
98175
  if (!gameId) {
@@ -97851,7 +98178,7 @@ var init_timeback_controller = __esm(() => {
97851
98178
  if (!isValidUUID(gameId)) {
97852
98179
  throw ApiError.unprocessableEntity("Invalid gameId format");
97853
98180
  }
97854
- logger66.debug("Verifying integration", { userId: ctx.user.id, gameId });
98181
+ logger67.debug("Verifying integration", { userId: ctx.user.id, gameId });
97855
98182
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
97856
98183
  });
97857
98184
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -97862,7 +98189,7 @@ var init_timeback_controller = __esm(() => {
97862
98189
  if (!isValidUUID(gameId)) {
97863
98190
  throw ApiError.unprocessableEntity("Invalid gameId format");
97864
98191
  }
97865
- logger66.debug("Getting config", { userId: ctx.user.id, gameId });
98192
+ logger67.debug("Getting config", { userId: ctx.user.id, gameId });
97866
98193
  return ctx.services.timeback.getConfig(gameId, ctx.user);
97867
98194
  });
97868
98195
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -97873,7 +98200,7 @@ var init_timeback_controller = __esm(() => {
97873
98200
  if (!isValidUUID(gameId)) {
97874
98201
  throw ApiError.unprocessableEntity("Invalid gameId format");
97875
98202
  }
97876
- logger66.debug("Deleting integrations", { userId: ctx.user.id, gameId });
98203
+ logger67.debug("Deleting integrations", { userId: ctx.user.id, gameId });
97877
98204
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
97878
98205
  });
97879
98206
  endActivity = requireDeveloper(async (ctx) => {
@@ -97884,7 +98211,7 @@ var init_timeback_controller = __esm(() => {
97884
98211
  } catch (error2) {
97885
98212
  if (error2 instanceof exports_external.ZodError) {
97886
98213
  const details = formatZodError(error2);
97887
- logger66.warn("End activity validation failed", { details });
98214
+ logger67.warn("End activity validation failed", { details });
97888
98215
  throw ApiError.unprocessableEntity("Validation failed", details);
97889
98216
  }
97890
98217
  throw ApiError.badRequest("Invalid JSON body");
@@ -97902,7 +98229,7 @@ var init_timeback_controller = __esm(() => {
97902
98229
  masteredUnits,
97903
98230
  extensions
97904
98231
  } = body2;
97905
- logger66.debug("Ending activity", { userId: ctx.user.id, gameId });
98232
+ logger67.debug("Ending activity", { userId: ctx.user.id, gameId });
97906
98233
  return ctx.services.timeback.endActivity({
97907
98234
  gameId,
97908
98235
  studentId,
@@ -97926,7 +98253,7 @@ var init_timeback_controller = __esm(() => {
97926
98253
  } catch (error2) {
97927
98254
  if (error2 instanceof exports_external.ZodError) {
97928
98255
  const details = formatZodError(error2);
97929
- logger66.warn("Heartbeat validation failed", { details });
98256
+ logger67.warn("Heartbeat validation failed", { details });
97930
98257
  throw ApiError.unprocessableEntity("Validation failed", details);
97931
98258
  }
97932
98259
  throw ApiError.badRequest("Invalid JSON body");
@@ -97942,7 +98269,7 @@ var init_timeback_controller = __esm(() => {
97942
98269
  windowSequence,
97943
98270
  isFinal
97944
98271
  } = body2;
97945
- logger66.debug("Recording heartbeat", {
98272
+ logger67.debug("Recording heartbeat", {
97946
98273
  userId: ctx.user.id,
97947
98274
  gameId,
97948
98275
  runId,
@@ -97967,7 +98294,7 @@ var init_timeback_controller = __esm(() => {
97967
98294
  });
97968
98295
  advanceCourse = requireDeveloper(async (ctx) => {
97969
98296
  const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
97970
- logger66.debug("Advancing student manually", {
98297
+ logger67.debug("Advancing student manually", {
97971
98298
  userId: ctx.user.id,
97972
98299
  gameId: body2.gameId,
97973
98300
  studentId: body2.studentId,
@@ -98008,7 +98335,7 @@ var init_timeback_controller = __esm(() => {
98008
98335
  perCourse: includeOptions.includes("percourse"),
98009
98336
  today: includeOptions.includes("today")
98010
98337
  };
98011
- logger66.debug("Getting student XP", {
98338
+ logger67.debug("Getting student XP", {
98012
98339
  requesterId: ctx.user.id,
98013
98340
  timebackId,
98014
98341
  gameId,
@@ -98030,7 +98357,7 @@ var init_timeback_controller = __esm(() => {
98030
98357
  if (!gameId || !courseId) {
98031
98358
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98032
98359
  }
98033
- logger66.debug("Getting course roster", {
98360
+ logger67.debug("Getting course roster", {
98034
98361
  requesterId: ctx.user.id,
98035
98362
  gameId,
98036
98363
  courseId,
@@ -98047,7 +98374,7 @@ var init_timeback_controller = __esm(() => {
98047
98374
  if (!timebackId || !gameId) {
98048
98375
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
98049
98376
  }
98050
- logger66.debug("Getting student overview", {
98377
+ logger67.debug("Getting student overview", {
98051
98378
  requesterId: ctx.user.id,
98052
98379
  timebackId,
98053
98380
  gameId,
@@ -98061,7 +98388,7 @@ var init_timeback_controller = __esm(() => {
98061
98388
  if (!gameId || !timebackId) {
98062
98389
  throw ApiError.badRequest("Missing gameId or timebackId path parameter");
98063
98390
  }
98064
- logger66.debug("Getting game metrics", {
98391
+ logger67.debug("Getting game metrics", {
98065
98392
  requesterId: ctx.user.id,
98066
98393
  gameId,
98067
98394
  timebackId
@@ -98079,7 +98406,7 @@ var init_timeback_controller = __esm(() => {
98079
98406
  if (!timebackId || !courseId || !gameId) {
98080
98407
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
98081
98408
  }
98082
- logger66.debug("Getting student activity", {
98409
+ logger67.debug("Getting student activity", {
98083
98410
  requesterId: ctx.user.id,
98084
98411
  timebackId,
98085
98412
  courseId,
@@ -98104,7 +98431,7 @@ var init_timeback_controller = __esm(() => {
98104
98431
  if (!timebackId || !courseId || !activityId || !gameId) {
98105
98432
  throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
98106
98433
  }
98107
- logger66.debug("Getting activity detail", {
98434
+ logger67.debug("Getting activity detail", {
98108
98435
  requesterId: ctx.user.id,
98109
98436
  timebackId,
98110
98437
  courseId,
@@ -98122,7 +98449,7 @@ var init_timeback_controller = __esm(() => {
98122
98449
  });
98123
98450
  grantXp = requireDeveloper(async (ctx) => {
98124
98451
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
98125
- logger66.debug("Granting manual XP", {
98452
+ logger67.debug("Granting manual XP", {
98126
98453
  requesterId: ctx.user.id,
98127
98454
  gameId: body2.gameId,
98128
98455
  courseId: body2.courseId,
@@ -98134,7 +98461,7 @@ var init_timeback_controller = __esm(() => {
98134
98461
  });
98135
98462
  adjustTime = requireDeveloper(async (ctx) => {
98136
98463
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
98137
- logger66.debug("Adjusting time spent", {
98464
+ logger67.debug("Adjusting time spent", {
98138
98465
  requesterId: ctx.user.id,
98139
98466
  gameId: body2.gameId,
98140
98467
  courseId: body2.courseId,
@@ -98146,7 +98473,7 @@ var init_timeback_controller = __esm(() => {
98146
98473
  });
98147
98474
  adjustMastery = requireDeveloper(async (ctx) => {
98148
98475
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
98149
- logger66.debug("Adjusting mastered units", {
98476
+ logger67.debug("Adjusting mastered units", {
98150
98477
  requesterId: ctx.user.id,
98151
98478
  gameId: body2.gameId,
98152
98479
  courseId: body2.courseId,
@@ -98156,6 +98483,22 @@ var init_timeback_controller = __esm(() => {
98156
98483
  });
98157
98484
  return ctx.services.timebackAdmin.adjustMasteredUnits(body2, ctx.user);
98158
98485
  });
98486
+ reconcileMasteryForConfigChange = requireDeveloper(async (ctx) => {
98487
+ const body2 = await parseRequestBody(ctx.request, ReconcileMasteryForConfigChangeSchema);
98488
+ logger67.debug("Reconciling mastery completion for config change", {
98489
+ requesterId: ctx.user.id,
98490
+ gameId: body2.gameId,
98491
+ courseId: body2.courseId,
98492
+ oldMasterableUnits: body2.oldMasterableUnits,
98493
+ newMasterableUnits: body2.newMasterableUnits,
98494
+ affectedCount: body2.affectedStudentIds.length
98495
+ });
98496
+ return ctx.services.timebackAdmin.reconcileMasteryForConfigChange(body2.gameId, body2.courseId, ctx.user, {
98497
+ oldMasterableUnits: body2.oldMasterableUnits,
98498
+ newMasterableUnits: body2.newMasterableUnits,
98499
+ affectedStudentIds: body2.affectedStudentIds
98500
+ });
98501
+ });
98159
98502
  searchStudents = requireGameManagementAccess(async (ctx) => {
98160
98503
  const gameId = ctx.params.gameId;
98161
98504
  const courseId = ctx.params.courseId;
@@ -98163,7 +98506,7 @@ var init_timeback_controller = __esm(() => {
98163
98506
  if (!gameId || !courseId) {
98164
98507
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98165
98508
  }
98166
- logger66.debug("Searching students for enrollment", {
98509
+ logger67.debug("Searching students for enrollment", {
98167
98510
  requesterId: ctx.user.id,
98168
98511
  gameId,
98169
98512
  courseId,
@@ -98173,7 +98516,7 @@ var init_timeback_controller = __esm(() => {
98173
98516
  });
98174
98517
  enrollStudent = requireGameManagementAccess(async (ctx) => {
98175
98518
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
98176
- logger66.debug("Enrolling student", {
98519
+ logger67.debug("Enrolling student", {
98177
98520
  requesterId: ctx.user.id,
98178
98521
  gameId: body2.gameId,
98179
98522
  courseId: body2.courseId,
@@ -98183,7 +98526,7 @@ var init_timeback_controller = __esm(() => {
98183
98526
  });
98184
98527
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
98185
98528
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
98186
- logger66.debug("Unenrolling student", {
98529
+ logger67.debug("Unenrolling student", {
98187
98530
  requesterId: ctx.user.id,
98188
98531
  gameId: body2.gameId,
98189
98532
  courseId: body2.courseId,
@@ -98193,7 +98536,7 @@ var init_timeback_controller = __esm(() => {
98193
98536
  });
98194
98537
  reactivateEnrollment = requireGameManagementAccess(async (ctx) => {
98195
98538
  const body2 = await parseRequestBody(ctx.request, ReactivateEnrollmentRequestSchema);
98196
- logger66.debug("Reactivating enrollment", {
98539
+ logger67.debug("Reactivating enrollment", {
98197
98540
  requesterId: ctx.user.id,
98198
98541
  gameId: body2.gameId,
98199
98542
  courseId: body2.courseId,
@@ -98333,6 +98676,8 @@ var init_timeback_controller = __esm(() => {
98333
98676
  getUserById,
98334
98677
  setupIntegration,
98335
98678
  getIntegrations,
98679
+ updateIntegration,
98680
+ getIntegrationConfig,
98336
98681
  verifyIntegration,
98337
98682
  getConfig: getConfig2,
98338
98683
  deleteIntegrations,
@@ -98348,6 +98693,7 @@ var init_timeback_controller = __esm(() => {
98348
98693
  grantXp,
98349
98694
  adjustTime,
98350
98695
  adjustMastery,
98696
+ reconcileMasteryForConfigChange,
98351
98697
  searchStudents,
98352
98698
  enrollStudent,
98353
98699
  unenrollStudent,
@@ -98369,14 +98715,14 @@ var init_timeback_controller = __esm(() => {
98369
98715
  });
98370
98716
 
98371
98717
  // ../api-core/src/controllers/upload.controller.ts
98372
- var logger67, initiate;
98718
+ var logger68, initiate;
98373
98719
  var init_upload_controller = __esm(() => {
98374
98720
  init_esm();
98375
98721
  init_schemas_index();
98376
98722
  init_src2();
98377
98723
  init_errors();
98378
98724
  init_utils11();
98379
- logger67 = log.scope("UploadController");
98725
+ logger68 = log.scope("UploadController");
98380
98726
  initiate = requireDeveloper(async (ctx) => {
98381
98727
  let body2;
98382
98728
  try {
@@ -98385,34 +98731,34 @@ var init_upload_controller = __esm(() => {
98385
98731
  } catch (error2) {
98386
98732
  if (error2 instanceof exports_external.ZodError) {
98387
98733
  const details = formatZodError(error2);
98388
- logger67.warn("Initiate upload validation failed", { details });
98734
+ logger68.warn("Initiate upload validation failed", { details });
98389
98735
  throw ApiError.unprocessableEntity("Validation failed", details);
98390
98736
  }
98391
98737
  throw ApiError.badRequest("Invalid JSON body");
98392
98738
  }
98393
- logger67.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98739
+ logger68.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98394
98740
  return ctx.services.upload.initiate(body2, ctx.user);
98395
98741
  });
98396
98742
  });
98397
98743
 
98398
98744
  // ../api-core/src/controllers/user.controller.ts
98399
- var logger68, getMe, getDemoProfile, updateDemoProfile, users2;
98745
+ var logger69, getMe, getDemoProfile, updateDemoProfile, users2;
98400
98746
  var init_user_controller = __esm(() => {
98401
98747
  init_schemas_index();
98402
98748
  init_src2();
98403
98749
  init_utils11();
98404
- logger68 = log.scope("UserController");
98750
+ logger69 = log.scope("UserController");
98405
98751
  getMe = requireNonAnonymous(async (ctx) => {
98406
- logger68.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98752
+ logger69.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98407
98753
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
98408
98754
  });
98409
98755
  getDemoProfile = requireAnonymous(async (ctx) => {
98410
- logger68.debug("Getting demo profile", { userId: ctx.user.id });
98756
+ logger69.debug("Getting demo profile", { userId: ctx.user.id });
98411
98757
  return ctx.services.user.getDemoProfile(ctx.user.id);
98412
98758
  });
98413
98759
  updateDemoProfile = requireAnonymous(async (ctx) => {
98414
98760
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
98415
- logger68.debug("Updating demo profile", {
98761
+ logger69.debug("Updating demo profile", {
98416
98762
  userId: ctx.user.id,
98417
98763
  displayName: body2.displayName
98418
98764
  });
@@ -98426,13 +98772,13 @@ var init_user_controller = __esm(() => {
98426
98772
  });
98427
98773
 
98428
98774
  // ../api-core/src/controllers/verify.controller.ts
98429
- var logger69;
98775
+ var logger70;
98430
98776
  var init_verify_controller = __esm(() => {
98431
98777
  init_schemas_index();
98432
98778
  init_src2();
98433
98779
  init_errors();
98434
98780
  init_utils11();
98435
- logger69 = log.scope("VerifyController");
98781
+ logger70 = log.scope("VerifyController");
98436
98782
  });
98437
98783
 
98438
98784
  // ../api-core/src/controllers/index.ts
@@ -98746,7 +99092,7 @@ var init_uploads = __esm(() => {
98746
99092
  });
98747
99093
 
98748
99094
  // src/routes/platform/games/deploy.ts
98749
- var logger70, gameDeployRouter;
99095
+ var logger71, gameDeployRouter;
98750
99096
  var init_deploy = __esm(() => {
98751
99097
  init_drizzle_orm();
98752
99098
  init_dist4();
@@ -98756,7 +99102,7 @@ var init_deploy = __esm(() => {
98756
99102
  init_src2();
98757
99103
  init_api();
98758
99104
  init_uploads();
98759
- logger70 = log.scope("SandboxDeploy");
99105
+ logger71 = log.scope("SandboxDeploy");
98760
99106
  gameDeployRouter = new Hono2;
98761
99107
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
98762
99108
  const user = c2.get("user");
@@ -98882,7 +99228,7 @@ var init_deploy = __esm(() => {
98882
99228
  completedAt: now2
98883
99229
  };
98884
99230
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
98885
- logger70.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
99231
+ logger71.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
98886
99232
  return c2.json({
98887
99233
  id: insertedJob.id,
98888
99234
  status: "succeeded",
@@ -99500,7 +99846,7 @@ var init_timeback6 = __esm(() => {
99500
99846
  return {
99501
99847
  grade: e.grade,
99502
99848
  subject: e.subject,
99503
- title: `${e.subject} Grade ${e.grade}`,
99849
+ title: `${e.subject} ${formatGradeLabel(e.grade)}`,
99504
99850
  totalXp: totalXp2,
99505
99851
  ...includeToday && { todayXp: todayXp2 }
99506
99852
  };
@@ -99529,7 +99875,7 @@ function verifyMockToken(idToken) {
99529
99875
  throw new Error("Invalid LTI token format");
99530
99876
  }
99531
99877
  }
99532
- var logger71, ltiRouter;
99878
+ var logger72, ltiRouter;
99533
99879
  var init_lti = __esm(() => {
99534
99880
  init_drizzle_orm();
99535
99881
  init_dist4();
@@ -99539,7 +99885,7 @@ var init_lti = __esm(() => {
99539
99885
  init_src2();
99540
99886
  init_constants();
99541
99887
  init_api();
99542
- logger71 = log.scope("SandboxLti");
99888
+ logger72 = log.scope("SandboxLti");
99543
99889
  ltiRouter = new Hono2;
99544
99890
  ltiRouter.post("/launch", async (c2) => {
99545
99891
  const db2 = c2.get("db");
@@ -99557,7 +99903,7 @@ var init_lti = __esm(() => {
99557
99903
  claims = verifyMockToken(idToken);
99558
99904
  } catch (error2) {
99559
99905
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99560
- logger71.error("LTI token verification failed", { error: errorMessage });
99906
+ logger72.error("LTI token verification failed", { error: errorMessage });
99561
99907
  return c2.json({
99562
99908
  error: "invalid_token",
99563
99909
  message: errorMessage
@@ -99565,7 +99911,7 @@ var init_lti = __esm(() => {
99565
99911
  }
99566
99912
  const validationError = validateLtiClaims(claims);
99567
99913
  if (validationError) {
99568
- logger71.warn("LTI claims validation failed", {
99914
+ logger72.warn("LTI claims validation failed", {
99569
99915
  error: validationError,
99570
99916
  sub: claims.sub
99571
99917
  });
@@ -99585,7 +99931,7 @@ var init_lti = __esm(() => {
99585
99931
  createdAt: new Date,
99586
99932
  updatedAt: new Date
99587
99933
  });
99588
- logger71.info("LTI launch successful", { userId: user.id });
99934
+ logger72.info("LTI launch successful", { userId: user.id });
99589
99935
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
99590
99936
  const currentHost = new URL(c2.req.url).hostname;
99591
99937
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -99593,7 +99939,7 @@ var init_lti = __esm(() => {
99593
99939
  return c2.redirect(redirectPath);
99594
99940
  } catch (error2) {
99595
99941
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99596
- logger71.error("Unexpected error during LTI launch", { error: errorMessage });
99942
+ logger72.error("Unexpected error during LTI launch", { error: errorMessage });
99597
99943
  return c2.json({
99598
99944
  error: "unexpected_error",
99599
99945
  message: "An unexpected error occurred during LTI launch"