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