@playcademy/sandbox 0.3.18 → 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 +826 -499
  2. package/dist/server.js +826 -499
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1316,7 +1316,7 @@ var package_default;
1316
1316
  var init_package = __esm(() => {
1317
1317
  package_default = {
1318
1318
  name: "@playcademy/sandbox",
1319
- version: "0.3.18",
1319
+ version: "0.3.19-beta.1",
1320
1320
  description: "Local development server for Playcademy game development",
1321
1321
  type: "module",
1322
1322
  exports: {
@@ -31180,7 +31180,7 @@ function isValidAdminAttributionDate(value) {
31180
31180
  const date3 = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
31181
31181
  return date3.getUTCFullYear() === year && date3.getUTCMonth() + 1 === month && date3.getUTCDate() === day;
31182
31182
  }
31183
- var TIMEBACK_GRADES, TIMEBACK_SUBJECTS4, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, 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, EnrollStudentRequestSchema, UnenrollStudentRequestSchema, ReactivateEnrollmentRequestSchema, InsertAssessmentTestSchema, CreateAssessmentRequestSchema, ReorderAssessmentsRequestSchema, ReorderQuestionsRequestSchema;
31183
+ var TIMEBACK_GRADES, TIMEBACK_SUBJECTS4, TimebackGradeSchema, TimebackSubjectSchema, UpdateTimebackXpRequestSchema, CourseGoalsSchema, UpdateGameTimebackIntegrationRequestSchema, TimebackActivityDataSchema, EndActivityRequestSchema, GameActivityMetricsSchema, GameCourseMetricsSchema, GameMetricsResponseSchema, AdvanceCourseRequestSchema, HeartbeatRequestSchema, PopulateStudentRequestSchema, DerivedPlatformCourseConfigSchema, TimebackBaseConfigSchema, PlatformTimebackSetupRequestSchema, AdminTimebackMutationBaseSchema, AdminAttributionDateSchema, ADMIN_GRANT_XP_MIN = -1e5, ADMIN_GRANT_XP_MAX = 1e5, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE, GrantTimebackXpRequestSchema, AdjustTimebackTimeRequestSchema, AdjustTimebackMasteryRequestSchema, ReconcileMasteryForConfigChangeSchema, EnrollStudentRequestSchema, UnenrollStudentRequestSchema, ReactivateEnrollmentRequestSchema, InsertAssessmentTestSchema, CreateAssessmentRequestSchema, ReorderAssessmentsRequestSchema, ReorderQuestionsRequestSchema;
31184
31184
  var init_schemas11 = __esm(() => {
31185
31185
  init_drizzle_zod();
31186
31186
  init_esm();
@@ -31205,6 +31205,24 @@ var init_schemas11 = __esm(() => {
31205
31205
  xp: exports_external.number().min(0, "XP must be a non-negative number"),
31206
31206
  userTimestamp: exports_external.string().datetime().optional()
31207
31207
  });
31208
+ CourseGoalsSchema = exports_external.object({
31209
+ dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
31210
+ dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
31211
+ dailyActiveMinutes: exports_external.number().int().nonnegative().nullable().optional(),
31212
+ dailyAccuracy: exports_external.number().int().min(0).max(100).nullable().optional(),
31213
+ dailyMasteredUnits: exports_external.number().int().nonnegative().nullable().optional()
31214
+ });
31215
+ UpdateGameTimebackIntegrationRequestSchema = exports_external.object({
31216
+ title: exports_external.string().trim().min(1).optional(),
31217
+ courseCode: exports_external.string().trim().min(1).optional(),
31218
+ subject: TimebackSubjectSchema.optional(),
31219
+ totalXp: exports_external.number().int().nonnegative().nullable().optional(),
31220
+ masterableUnits: exports_external.number().int().nonnegative().nullable().optional(),
31221
+ goals: CourseGoalsSchema.optional(),
31222
+ publishStatus: exports_external.enum(["draft", "testing", "published", "deactivated"]).nullable().optional(),
31223
+ isSupplemental: exports_external.boolean().optional(),
31224
+ timebackVisible: exports_external.boolean().nullable().optional()
31225
+ });
31208
31226
  TimebackActivityDataSchema = exports_external.object({
31209
31227
  activityId: exports_external.string().min(1),
31210
31228
  activityName: exports_external.string().optional(),
@@ -31374,6 +31392,13 @@ var init_schemas11 = __esm(() => {
31374
31392
  date: AdminAttributionDateSchema.optional(),
31375
31393
  useCurrentTime: exports_external.boolean().optional()
31376
31394
  });
31395
+ ReconcileMasteryForConfigChangeSchema = exports_external.object({
31396
+ gameId: exports_external.string().uuid(),
31397
+ courseId: exports_external.string().min(1),
31398
+ oldMasterableUnits: exports_external.number().int().positive(),
31399
+ newMasterableUnits: exports_external.number().int().positive(),
31400
+ affectedStudentIds: exports_external.array(exports_external.string().min(1)).min(1).max(500)
31401
+ });
31377
31402
  EnrollStudentRequestSchema = exports_external.object({
31378
31403
  gameId: exports_external.string().uuid(),
31379
31404
  courseId: exports_external.string().min(1),
@@ -31529,6 +31554,65 @@ var init_timeback_admin_util = __esm(() => {
31529
31554
  init_errors();
31530
31555
  });
31531
31556
 
31557
+ // ../api-core/src/utils/timeback-mastery-completion.util.ts
31558
+ async function upsertMasteryCompletionEntry(params) {
31559
+ const { client, courseId, studentId, appName, action } = params;
31560
+ const ids = deriveSourcedIds(courseId);
31561
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
31562
+ const resultId = `${lineItemId}:${studentId}:completion`;
31563
+ if (action === "complete") {
31564
+ await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
31565
+ sourcedId: lineItemId,
31566
+ title: "Mastery Completion",
31567
+ status: ONEROSTER_STATUS.active,
31568
+ course: { sourcedId: ids.course },
31569
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
31570
+ });
31571
+ await client.oneroster.assessmentResults.upsert(resultId, {
31572
+ sourcedId: resultId,
31573
+ status: ONEROSTER_STATUS.active,
31574
+ assessmentLineItem: { sourcedId: lineItemId },
31575
+ student: { sourcedId: studentId },
31576
+ score: 100,
31577
+ scoreDate: new Date().toISOString(),
31578
+ scoreStatus: SCORE_STATUS.fullyGraded,
31579
+ inProgress: "false",
31580
+ metadata: {
31581
+ isMasteryCompletion: true,
31582
+ adminAction: true,
31583
+ appName
31584
+ }
31585
+ });
31586
+ } else {
31587
+ try {
31588
+ await client.oneroster.assessmentResults.upsert(resultId, {
31589
+ sourcedId: resultId,
31590
+ status: ONEROSTER_STATUS.active,
31591
+ assessmentLineItem: { sourcedId: lineItemId },
31592
+ student: { sourcedId: studentId },
31593
+ score: 0,
31594
+ scoreDate: new Date().toISOString(),
31595
+ scoreStatus: SCORE_STATUS.notSubmitted,
31596
+ inProgress: "true",
31597
+ metadata: {
31598
+ isMasteryCompletion: true,
31599
+ adminAction: true,
31600
+ appName
31601
+ }
31602
+ });
31603
+ } catch {
31604
+ logger17.debug("No completion entry to revoke", { studentId, courseId });
31605
+ }
31606
+ }
31607
+ }
31608
+ var logger17;
31609
+ var init_timeback_mastery_completion_util = __esm(() => {
31610
+ init_src2();
31611
+ init_constants4();
31612
+ init_utils6();
31613
+ logger17 = log.scope("timeback-mastery-completion");
31614
+ });
31615
+
31532
31616
  // ../api-core/src/utils/timeback.util.ts
31533
31617
  function isRecord2(value) {
31534
31618
  return typeof value === "object" && value !== null;
@@ -31886,7 +31970,7 @@ class TimebackAdminService {
31886
31970
  }
31887
31971
  requireClient() {
31888
31972
  if (!this.deps.timeback) {
31889
- logger17.error("Timeback client not available in context");
31973
+ logger18.error("Timeback client not available in context");
31890
31974
  throw new ValidationError("Timeback integration not available in this environment");
31891
31975
  }
31892
31976
  return this.deps.timeback;
@@ -32106,7 +32190,7 @@ class TimebackAdminService {
32106
32190
  });
32107
32191
  return [enrollmentId, this.summarizeAnalyticsFacts(analytics.facts)];
32108
32192
  } catch (error) {
32109
- logger17.warn("Failed to load enrollment analytics summary", {
32193
+ logger18.warn("Failed to load enrollment analytics summary", {
32110
32194
  enrollmentId,
32111
32195
  error: error instanceof Error ? error.message : String(error)
32112
32196
  });
@@ -32136,7 +32220,7 @@ class TimebackAdminService {
32136
32220
  const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
32137
32221
  return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
32138
32222
  } catch (error) {
32139
- logger17.warn("Failed to load recent Caliper activity", {
32223
+ logger18.warn("Failed to load recent Caliper activity", {
32140
32224
  studentId,
32141
32225
  gameId: source.gameId,
32142
32226
  sourceMode: source.sourceMode,
@@ -32462,60 +32546,44 @@ class TimebackAdminService {
32462
32546
  const wasMastered = currentMastered >= masterableUnits;
32463
32547
  const willBeMastered = currentMastered + data.units >= masterableUnits;
32464
32548
  if (wasMastered !== willBeMastered) {
32465
- const ids = deriveSourcedIds(data.courseId);
32466
- const lineItemId = `${ids.course}-mastery-completion-assessment`;
32467
- const resultId = `${lineItemId}:${data.studentId}:completion`;
32468
- if (willBeMastered) {
32469
- await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
32470
- sourcedId: lineItemId,
32471
- title: "Mastery Completion",
32472
- status: ONEROSTER_STATUS.active,
32473
- course: { sourcedId: ids.course },
32474
- ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
32475
- });
32476
- await client.oneroster.assessmentResults.upsert(resultId, {
32477
- sourcedId: resultId,
32478
- status: ONEROSTER_STATUS.active,
32479
- assessmentLineItem: { sourcedId: lineItemId },
32480
- student: { sourcedId: data.studentId },
32481
- score: 100,
32482
- scoreDate: new Date().toISOString(),
32483
- scoreStatus: SCORE_STATUS.fullyGraded,
32484
- inProgress: "false",
32485
- metadata: {
32486
- isMasteryCompletion: true,
32487
- adminAction: true,
32488
- appName
32489
- }
32490
- });
32491
- } else {
32492
- try {
32493
- await client.oneroster.assessmentResults.upsert(resultId, {
32494
- sourcedId: resultId,
32495
- status: ONEROSTER_STATUS.active,
32496
- assessmentLineItem: { sourcedId: lineItemId },
32497
- student: { sourcedId: data.studentId },
32498
- score: 0,
32499
- scoreDate: new Date().toISOString(),
32500
- scoreStatus: SCORE_STATUS.notSubmitted,
32501
- inProgress: "true",
32502
- metadata: {
32503
- isMasteryCompletion: true,
32504
- adminAction: true,
32505
- appName
32506
- }
32507
- });
32508
- } catch {
32509
- logger17.debug("No completion entry to revoke", {
32510
- studentId: data.studentId,
32511
- courseId: data.courseId
32512
- });
32513
- }
32514
- }
32549
+ await upsertMasteryCompletionEntry({
32550
+ client,
32551
+ courseId: data.courseId,
32552
+ studentId: data.studentId,
32553
+ appName,
32554
+ action: willBeMastered ? "complete" : "revoke"
32555
+ });
32515
32556
  }
32516
32557
  }
32517
32558
  return { status: "ok" };
32518
32559
  }
32560
+ async reconcileMasteryForConfigChange(gameId, courseId, user, context) {
32561
+ const { client, appName } = await this.resolveAdminMutationContext(gameId, courseId, user);
32562
+ const action = context.newMasterableUnits < context.oldMasterableUnits ? "complete" : "revoke";
32563
+ const failed = [];
32564
+ let processed = 0;
32565
+ await TimebackAdminService.runWithConcurrency(context.affectedStudentIds, 8, async (studentId) => {
32566
+ try {
32567
+ await upsertMasteryCompletionEntry({
32568
+ client,
32569
+ courseId,
32570
+ studentId,
32571
+ appName,
32572
+ action
32573
+ });
32574
+ processed++;
32575
+ } catch (error) {
32576
+ logger18.warn("Failed to reconcile mastery completion for student", {
32577
+ studentId,
32578
+ courseId,
32579
+ action,
32580
+ error: error instanceof Error ? error.message : String(error)
32581
+ });
32582
+ failed.push(studentId);
32583
+ }
32584
+ });
32585
+ return { processed, failed };
32586
+ }
32519
32587
  async searchStudentsForEnrollment(gameId, courseId, query, user) {
32520
32588
  const client = this.requireClient();
32521
32589
  await this.deps.validateGameManagementAccess(user, gameId);
@@ -32542,7 +32610,7 @@ class TimebackAdminService {
32542
32610
  const response = await client["request"](endpoint, "GET");
32543
32611
  allUsers = response.users || [];
32544
32612
  } catch (error) {
32545
- logger17.warn("Failed to search OneRoster users", {
32613
+ logger18.warn("Failed to search OneRoster users", {
32546
32614
  query: trimmedQuery,
32547
32615
  error: error instanceof Error ? error.message : String(error)
32548
32616
  });
@@ -32687,7 +32755,7 @@ class TimebackAdminService {
32687
32755
  return results;
32688
32756
  }
32689
32757
  }
32690
- var logger17;
32758
+ var logger18;
32691
32759
  var init_timeback_admin_service = __esm(() => {
32692
32760
  init_drizzle_orm();
32693
32761
  init_src();
@@ -32700,8 +32768,9 @@ var init_timeback_admin_service = __esm(() => {
32700
32768
  init_errors();
32701
32769
  init_timeback_admin_metrics_util();
32702
32770
  init_timeback_admin_util();
32771
+ init_timeback_mastery_completion_util();
32703
32772
  init_timeback_util();
32704
- logger17 = log.scope("TimebackAdminService");
32773
+ logger18 = log.scope("TimebackAdminService");
32705
32774
  });
32706
32775
 
32707
32776
  // ../timeback/dist/errors.js
@@ -32948,7 +33017,7 @@ class TimebackAssessmentsService {
32948
33017
  isActive: row.bankActive
32949
33018
  };
32950
33019
  } catch {
32951
- logger18.warn("Failed to fetch QTI test metadata", {
33020
+ logger19.warn("Failed to fetch QTI test metadata", {
32952
33021
  identifier: row.qtiTestIdentifier
32953
33022
  });
32954
33023
  return {
@@ -33002,7 +33071,7 @@ class TimebackAssessmentsService {
33002
33071
  });
33003
33072
  } catch (error) {
33004
33073
  if (error instanceof TimebackApiError && error.status === 409) {
33005
- logger18.info("QTI test already exists (idempotent retry)", {
33074
+ logger19.info("QTI test already exists (idempotent retry)", {
33006
33075
  qtiTestIdentifier: input.qtiTestIdentifier
33007
33076
  });
33008
33077
  } else {
@@ -33015,7 +33084,7 @@ class TimebackAssessmentsService {
33015
33084
  qtiTestIdentifier: input.qtiTestIdentifier,
33016
33085
  sortOrder: maxSortOrder + 1
33017
33086
  }).returning();
33018
- logger18.info("Assessment created", {
33087
+ logger19.info("Assessment created", {
33019
33088
  integrationId,
33020
33089
  qtiTestIdentifier: input.qtiTestIdentifier
33021
33090
  });
@@ -33037,13 +33106,13 @@ class TimebackAssessmentsService {
33037
33106
  }
33038
33107
  await client.qti.tests.delete(qtiTestIdentifier);
33039
33108
  } catch (error) {
33040
- logger18.warn("Partial QTI cleanup during assessment deletion", {
33109
+ logger19.warn("Partial QTI cleanup during assessment deletion", {
33041
33110
  qtiTestIdentifier,
33042
33111
  error: error instanceof Error ? error.message : String(error)
33043
33112
  });
33044
33113
  }
33045
33114
  await this.deps.db.delete(gameTimebackAssessmentTests).where(eq(gameTimebackAssessmentTests.id, row.id));
33046
- logger18.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33115
+ logger19.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33047
33116
  }
33048
33117
  async reorderAssessments(integrationId, identifiers) {
33049
33118
  await this.requireIntegration(integrationId);
@@ -33088,7 +33157,7 @@ class TimebackAssessmentsService {
33088
33157
  return client.qti.tests.reorderItems(qtiTestIdentifier, partId, sectionId, items2);
33089
33158
  }
33090
33159
  async activateAssessment(integrationId, qtiTestIdentifier) {
33091
- logger18.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33160
+ logger19.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33092
33161
  const client = this.requireClient();
33093
33162
  const integration = await this.requireIntegration(integrationId);
33094
33163
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33126,7 +33195,7 @@ class TimebackAssessmentsService {
33126
33195
  });
33127
33196
  }
33128
33197
  } catch {
33129
- logger18.warn("Failed to reactivate existing child resource, will create new", {
33198
+ logger19.warn("Failed to reactivate existing child resource, will create new", {
33130
33199
  childResourceId
33131
33200
  });
33132
33201
  childResourceId = null;
@@ -33139,7 +33208,7 @@ class TimebackAssessmentsService {
33139
33208
  const resourceUrl = resource.metadata?.url;
33140
33209
  if (resourceUrl === qtiTestUrl) {
33141
33210
  childResourceId = resourceId;
33142
- logger18.info("Found existing child Resource for QTI test (idempotent retry)", {
33211
+ logger19.info("Found existing child Resource for QTI test (idempotent retry)", {
33143
33212
  qtiTestIdentifier,
33144
33213
  childResourceId
33145
33214
  });
@@ -33148,7 +33217,7 @@ class TimebackAssessmentsService {
33148
33217
  } catch {}
33149
33218
  }
33150
33219
  if (!childResourceId) {
33151
- logger18.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33220
+ logger19.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33152
33221
  const childResult = await client.oneroster.resources.create({
33153
33222
  resource: {
33154
33223
  status: "active",
@@ -33176,10 +33245,10 @@ class TimebackAssessmentsService {
33176
33245
  }
33177
33246
  }
33178
33247
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: childResourceId, bankActive: true }).where(eq(gameTimebackAssessmentTests.id, row.id));
33179
- logger18.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
33248
+ logger19.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
33180
33249
  }
33181
33250
  async deactivateAssessment(integrationId, qtiTestIdentifier) {
33182
- logger18.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33251
+ logger19.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33183
33252
  const client = this.requireClient();
33184
33253
  const integration = await this.requireIntegration(integrationId);
33185
33254
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33192,7 +33261,7 @@ class TimebackAssessmentsService {
33192
33261
  const childResourceId = row.bankResourceId;
33193
33262
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33194
33263
  try {
33195
- logger18.debug("Reading parent resource for deactivation", {
33264
+ logger19.debug("Reading parent resource for deactivation", {
33196
33265
  resourceId: bankIds.resource
33197
33266
  });
33198
33267
  const parentResource = await client.oneroster.resources.get(bankIds.resource);
@@ -33211,22 +33280,22 @@ class TimebackAssessmentsService {
33211
33280
  });
33212
33281
  }
33213
33282
  } catch (error) {
33214
- logger18.warn("Failed to update parent resource during deactivation", {
33283
+ logger19.warn("Failed to update parent resource during deactivation", {
33215
33284
  bankResourceId: bankIds.resource,
33216
33285
  error: error instanceof Error ? error.message : String(error)
33217
33286
  });
33218
33287
  }
33219
33288
  try {
33220
- logger18.debug("Deleting child resource", { childResourceId });
33289
+ logger19.debug("Deleting child resource", { childResourceId });
33221
33290
  await client.oneroster.resources.delete(childResourceId);
33222
33291
  } catch (error) {
33223
- logger18.warn("Failed to delete child resource (may already be deleted)", {
33292
+ logger19.warn("Failed to delete child resource (may already be deleted)", {
33224
33293
  childResourceId,
33225
33294
  error: error instanceof Error ? error.message : String(error)
33226
33295
  });
33227
33296
  }
33228
33297
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankActive: false }).where(eq(gameTimebackAssessmentTests.id, row.id));
33229
- logger18.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33298
+ logger19.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33230
33299
  }
33231
33300
  isAssessmentActive(row) {
33232
33301
  return row.bankActive;
@@ -33257,14 +33326,14 @@ class TimebackAssessmentsService {
33257
33326
  status: "tobedeleted"
33258
33327
  });
33259
33328
  } catch (error) {
33260
- logger18.warn("Failed to delete component resource", { error });
33329
+ logger19.warn("Failed to delete component resource", { error });
33261
33330
  }
33262
33331
  for (const row of activeRows) {
33263
33332
  if (row.bankResourceId) {
33264
33333
  try {
33265
33334
  await client.oneroster.resources.delete(row.bankResourceId);
33266
33335
  } catch (error) {
33267
- logger18.warn("Failed to delete child resource", {
33336
+ logger19.warn("Failed to delete child resource", {
33268
33337
  childResourceId: row.bankResourceId,
33269
33338
  error
33270
33339
  });
@@ -33274,17 +33343,17 @@ class TimebackAssessmentsService {
33274
33343
  try {
33275
33344
  await client.oneroster.resources.delete(bankIds.resource);
33276
33345
  } catch (error) {
33277
- logger18.warn("Failed to delete parent resource", { error });
33346
+ logger19.warn("Failed to delete parent resource", { error });
33278
33347
  }
33279
33348
  try {
33280
33349
  await client.oneroster.courseComponents.update(bankIds.component, {
33281
33350
  status: "tobedeleted"
33282
33351
  });
33283
33352
  } catch (error) {
33284
- logger18.warn("Failed to delete course component", { error });
33353
+ logger19.warn("Failed to delete course component", { error });
33285
33354
  }
33286
33355
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: null, bankActive: false }).where(eq(gameTimebackAssessmentTests.integrationId, integrationId));
33287
- logger18.info("Bank destroyed", { integrationId });
33356
+ logger19.info("Bank destroyed", { integrationId });
33288
33357
  }
33289
33358
  requireClient() {
33290
33359
  if (!this.deps.timeback) {
@@ -33313,7 +33382,7 @@ class TimebackAssessmentsService {
33313
33382
  async ensureBank(integration) {
33314
33383
  const client = this.requireClient();
33315
33384
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33316
- logger18.debug("Ensuring assessment bank hierarchy", {
33385
+ logger19.debug("Ensuring assessment bank hierarchy", {
33317
33386
  courseId: integration.courseId,
33318
33387
  bankIds
33319
33388
  });
@@ -33330,7 +33399,7 @@ class TimebackAssessmentsService {
33330
33399
  });
33331
33400
  } catch (error) {
33332
33401
  if (error instanceof TimebackApiError && error.status === 409) {
33333
- logger18.debug("Course component already exists", { sourcedId: bankIds.component });
33402
+ logger19.debug("Course component already exists", { sourcedId: bankIds.component });
33334
33403
  } else {
33335
33404
  throw error;
33336
33405
  }
@@ -33354,7 +33423,7 @@ class TimebackAssessmentsService {
33354
33423
  });
33355
33424
  } catch (error) {
33356
33425
  if (error instanceof TimebackApiError && error.status === 409) {
33357
- logger18.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33426
+ logger19.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33358
33427
  } else {
33359
33428
  throw error;
33360
33429
  }
@@ -33374,14 +33443,14 @@ class TimebackAssessmentsService {
33374
33443
  });
33375
33444
  } catch (error) {
33376
33445
  if (error instanceof TimebackApiError && error.status === 409) {
33377
- logger18.debug("Component resource already exists", {
33446
+ logger19.debug("Component resource already exists", {
33378
33447
  sourcedId: bankIds.componentResource
33379
33448
  });
33380
33449
  } else {
33381
33450
  throw error;
33382
33451
  }
33383
33452
  }
33384
- logger18.info("Assessment bank hierarchy created", {
33453
+ logger19.info("Assessment bank hierarchy created", {
33385
33454
  courseId: integration.courseId
33386
33455
  });
33387
33456
  }
@@ -33396,7 +33465,7 @@ class TimebackAssessmentsService {
33396
33465
  return Math.max(...rows.map((r) => r.sortOrder));
33397
33466
  }
33398
33467
  }
33399
- var logger18;
33468
+ var logger19;
33400
33469
  var init_timeback_assessments_service = __esm(() => {
33401
33470
  init_drizzle_orm();
33402
33471
  init_tables_index();
@@ -33405,7 +33474,7 @@ var init_timeback_assessments_service = __esm(() => {
33405
33474
  init_errors4();
33406
33475
  init_utils6();
33407
33476
  init_errors();
33408
- logger18 = log.scope("TimebackAssessmentsService");
33477
+ logger19 = log.scope("TimebackAssessmentsService");
33409
33478
  });
33410
33479
 
33411
33480
  // ../api-core/src/utils/timeback-promotion.util.ts
@@ -33421,7 +33490,7 @@ async function promoteCompletedCourse({
33421
33490
  });
33422
33491
  const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
33423
33492
  if (!nextIntegration) {
33424
- logger19.debug("Skipping promotion because no next course is configured", {
33493
+ logger20.debug("Skipping promotion because no next course is configured", {
33425
33494
  gameId: currentIntegration.gameId,
33426
33495
  studentId,
33427
33496
  grade: currentIntegration.grade,
@@ -33438,7 +33507,7 @@ async function promoteCompletedCourse({
33438
33507
  const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
33439
33508
  if (!currentEnrollment) {
33440
33509
  if (nextEnrollment) {
33441
- logger19.debug("Skipping promotion because student is already on the next course", {
33510
+ logger20.debug("Skipping promotion because student is already on the next course", {
33442
33511
  gameId: currentIntegration.gameId,
33443
33512
  studentId,
33444
33513
  grade: currentIntegration.grade,
@@ -33452,7 +33521,7 @@ async function promoteCompletedCourse({
33452
33521
  nextCourseId: nextIntegration.courseId
33453
33522
  };
33454
33523
  }
33455
- logger19.debug("Skipping promotion because student is not enrolled in the current course", {
33524
+ logger20.debug("Skipping promotion because student is not enrolled in the current course", {
33456
33525
  gameId: currentIntegration.gameId,
33457
33526
  studentId,
33458
33527
  grade: currentIntegration.grade,
@@ -33483,7 +33552,7 @@ async function promoteCompletedCourse({
33483
33552
  client.invalidateEnrollments(studentId);
33484
33553
  }
33485
33554
  }
33486
- logger19.info("Promoted student to next course", {
33555
+ logger20.info("Promoted student to next course", {
33487
33556
  gameId: currentIntegration.gameId,
33488
33557
  studentId,
33489
33558
  subject: currentIntegration.subject,
@@ -33498,16 +33567,16 @@ async function promoteCompletedCourse({
33498
33567
  nextCourseId: nextIntegration.courseId
33499
33568
  };
33500
33569
  }
33501
- var logger19;
33570
+ var logger20;
33502
33571
  var init_timeback_promotion_util = __esm(() => {
33503
33572
  init_drizzle_orm();
33504
33573
  init_tables_index();
33505
33574
  init_src2();
33506
- logger19 = log.scope("TimebackPromotion");
33575
+ logger20 = log.scope("TimebackPromotion");
33507
33576
  });
33508
33577
 
33509
33578
  // ../api-core/src/services/timeback.service.ts
33510
- var logger20, TimebackService;
33579
+ var logger21, TimebackService;
33511
33580
  var init_timeback_service = __esm(() => {
33512
33581
  init_drizzle_orm();
33513
33582
  init_src();
@@ -33518,7 +33587,7 @@ var init_timeback_service = __esm(() => {
33518
33587
  init_errors();
33519
33588
  init_timeback_promotion_util();
33520
33589
  init_timeback_util();
33521
- logger20 = log.scope("TimebackService");
33590
+ logger21 = log.scope("TimebackService");
33522
33591
  TimebackService = class TimebackService {
33523
33592
  static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
33524
33593
  static processedHeartbeatWindows = new Map;
@@ -33564,7 +33633,7 @@ var init_timeback_service = __esm(() => {
33564
33633
  }
33565
33634
  requireClient() {
33566
33635
  if (!this.deps.timeback) {
33567
- logger20.error("Timeback client not available in context");
33636
+ logger21.error("Timeback client not available in context");
33568
33637
  throw new ValidationError("Timeback integration not available in this environment");
33569
33638
  }
33570
33639
  return this.deps.timeback;
@@ -33617,7 +33686,7 @@ var init_timeback_service = __esm(() => {
33617
33686
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
33618
33687
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
33619
33688
  if (!result) {
33620
- logger20.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33689
+ logger21.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33621
33690
  throw new InternalError("Failed to update daily XP record");
33622
33691
  }
33623
33692
  return { xp: result.xp, date: result.date.toISOString() };
@@ -33648,7 +33717,7 @@ var init_timeback_service = __esm(() => {
33648
33717
  columns: { id: true, timebackId: true }
33649
33718
  });
33650
33719
  if (dbUser?.timebackId) {
33651
- logger20.info("Student already onboarded", { userId: user.id });
33720
+ logger21.info("Student already onboarded", { userId: user.id });
33652
33721
  return { status: "already_populated" };
33653
33722
  }
33654
33723
  let timebackId;
@@ -33657,7 +33726,7 @@ var init_timeback_service = __esm(() => {
33657
33726
  const existingUser = await client.oneroster.users.findByEmail(user.email);
33658
33727
  timebackId = existingUser.sourcedId;
33659
33728
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
33660
- logger20.info("Found existing student in OneRoster", {
33729
+ logger21.info("Found existing student in OneRoster", {
33661
33730
  userId: user.id,
33662
33731
  timebackId
33663
33732
  });
@@ -33686,7 +33755,7 @@ var init_timeback_service = __esm(() => {
33686
33755
  }
33687
33756
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
33688
33757
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
33689
- logger20.info("Created student in OneRoster", { userId: user.id, timebackId });
33758
+ logger21.info("Created student in OneRoster", { userId: user.id, timebackId });
33690
33759
  }
33691
33760
  const assessments = await this.fetchAssessments(timebackId);
33692
33761
  await db2.transaction(async (tx) => {
@@ -33720,7 +33789,7 @@ var init_timeback_service = __esm(() => {
33720
33789
  }
33721
33790
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
33722
33791
  if (!updated) {
33723
- logger20.error("User Timeback ID update returned no rows", {
33792
+ logger21.error("User Timeback ID update returned no rows", {
33724
33793
  userId: user.id,
33725
33794
  timebackId
33726
33795
  });
@@ -33744,13 +33813,13 @@ var init_timeback_service = __esm(() => {
33744
33813
  }
33745
33814
  offset += limit;
33746
33815
  }
33747
- logger20.debug("Fetched assessments", {
33816
+ logger21.debug("Fetched assessments", {
33748
33817
  studentSourcedId,
33749
33818
  totalCount: allAssessments.length
33750
33819
  });
33751
33820
  return allAssessments;
33752
33821
  } catch (error) {
33753
- logger20.warn("Failed to fetch assessments", { studentSourcedId, error });
33822
+ logger21.warn("Failed to fetch assessments", { studentSourcedId, error });
33754
33823
  return [];
33755
33824
  }
33756
33825
  }
@@ -33863,7 +33932,7 @@ var init_timeback_service = __esm(() => {
33863
33932
  masterableUnits: derivedMasterableUnits
33864
33933
  } = courseConfig;
33865
33934
  if (!isTimebackSubject(subjectInput)) {
33866
- logger20.warn("Invalid Timeback subject in course config", {
33935
+ logger21.warn("Invalid Timeback subject in course config", {
33867
33936
  subject: subjectInput,
33868
33937
  courseCode,
33869
33938
  title
@@ -33871,7 +33940,7 @@ var init_timeback_service = __esm(() => {
33871
33940
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
33872
33941
  }
33873
33942
  if (!isTimebackGrade(grade)) {
33874
- logger20.warn("Invalid Timeback grade in course config", {
33943
+ logger21.warn("Invalid Timeback grade in course config", {
33875
33944
  grade,
33876
33945
  courseCode,
33877
33946
  title
@@ -33883,7 +33952,7 @@ var init_timeback_service = __esm(() => {
33883
33952
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
33884
33953
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
33885
33954
  if (typeof totalXp !== "number") {
33886
- logger20.warn("Course missing totalXp in Timeback config", {
33955
+ logger21.warn("Course missing totalXp in Timeback config", {
33887
33956
  courseCode,
33888
33957
  title
33889
33958
  });
@@ -33899,7 +33968,9 @@ var init_timeback_service = __esm(() => {
33899
33968
  courseCode,
33900
33969
  level,
33901
33970
  gradingScheme: "STANDARD",
33902
- metadata: metadata2
33971
+ metadata: TimebackService.patchCourseMetadata(metadata2, totalXp, {
33972
+ masterableUnits: masterableUnits ?? null
33973
+ })
33903
33974
  },
33904
33975
  component: {
33905
33976
  ...baseConfig.component,
@@ -33924,7 +33995,7 @@ var init_timeback_service = __esm(() => {
33924
33995
  const existingIntegration = existing.find((i2) => i2.grade === grade && i2.subject === subject);
33925
33996
  if (existingIntegration) {
33926
33997
  await client.update(existingIntegration.courseId, fullConfig);
33927
- const [updated] = await db2.update(gameTimebackIntegrations).set({ totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
33998
+ const [updated] = await db2.update(gameTimebackIntegrations).set({ subject, totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
33928
33999
  if (updated) {
33929
34000
  integrations.push(this.toGameTimebackIntegration(updated));
33930
34001
  }
@@ -33949,6 +34020,60 @@ var init_timeback_service = __esm(() => {
33949
34020
  });
33950
34021
  return rows.map((row) => this.toGameTimebackIntegration(row));
33951
34022
  }
34023
+ async getIntegrationConfig(gameId, courseId, user) {
34024
+ const client = this.requireClient();
34025
+ await this.deps.validateGameManagementAccess(user, gameId);
34026
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34027
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
34028
+ });
34029
+ if (!integration) {
34030
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34031
+ }
34032
+ const config2 = await client.getConfig(courseId);
34033
+ return this.toGameTimebackIntegrationConfig(integration, config2);
34034
+ }
34035
+ async updateIntegration(gameId, courseId, user, patch) {
34036
+ const client = this.requireClient();
34037
+ await this.deps.validateDeveloperAccess(user, gameId);
34038
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34039
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
34040
+ });
34041
+ if (!integration) {
34042
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34043
+ }
34044
+ const timebackConfig = await client.getConfig(courseId);
34045
+ const liveSubject = timebackConfig.course.subjects[0];
34046
+ const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
34047
+ if (!isTimebackSubject(subject)) {
34048
+ throw new ValidationError(`Invalid subject "${subject}"`);
34049
+ }
34050
+ if (subject !== integration.subject) {
34051
+ const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34052
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
34053
+ });
34054
+ if (subjectConflict && subjectConflict.id !== integration.id) {
34055
+ throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
34056
+ }
34057
+ }
34058
+ const totalXp = "totalXp" in patch ? patch.totalXp ?? null : TimebackService.getTotalXpFromConfig(timebackConfig) ?? integration.totalXp ?? null;
34059
+ const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : TimebackService.getMasterableUnitsFromConfig(timebackConfig);
34060
+ await client.update(courseId, TimebackService.patchTimebackConfig(timebackConfig, {
34061
+ ...patch,
34062
+ subject,
34063
+ totalXp,
34064
+ masterableUnits,
34065
+ grade: integration.grade
34066
+ }));
34067
+ const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
34068
+ subject,
34069
+ totalXp,
34070
+ updatedAt: new Date
34071
+ }).where(eq(gameTimebackIntegrations.id, integration.id)).returning();
34072
+ if (!updated) {
34073
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34074
+ }
34075
+ return this.toGameTimebackIntegration(updated);
34076
+ }
33952
34077
  async verifyIntegration(gameId, user) {
33953
34078
  const client = this.requireClient();
33954
34079
  const db2 = this.deps.db;
@@ -34006,6 +34131,146 @@ var init_timeback_service = __esm(() => {
34006
34131
  }
34007
34132
  await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
34008
34133
  }
34134
+ static getMasterableUnitsFromConfig(config2) {
34135
+ const playcademyMetadata = config2.resource.metadata?.playcademy;
34136
+ if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
34137
+ return null;
34138
+ }
34139
+ return playcademyMetadata?.mastery?.masterableUnits ?? null;
34140
+ }
34141
+ static getTotalXpFromConfig(config2) {
34142
+ const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
34143
+ if (typeof courseMetadata?.metrics?.totalXp === "number") {
34144
+ return courseMetadata.metrics.totalXp;
34145
+ }
34146
+ const resourceMetadata = config2.resource.metadata;
34147
+ if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
34148
+ return resourceMetadata.xp;
34149
+ }
34150
+ return null;
34151
+ }
34152
+ static patchCourseMetadata(metadata2, totalXp, options) {
34153
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34154
+ const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
34155
+ const metrics = { ...currentMetrics };
34156
+ if (totalXp === null) {
34157
+ delete metrics.totalXp;
34158
+ } else {
34159
+ metrics.totalXp = totalXp;
34160
+ }
34161
+ if (options?.masterableUnits !== undefined) {
34162
+ if (options.masterableUnits === null) {
34163
+ delete metrics.totalLessons;
34164
+ } else {
34165
+ metrics.totalLessons = options.masterableUnits;
34166
+ }
34167
+ }
34168
+ metrics.totalGrades = 1;
34169
+ if (Object.keys(metrics).length > 0) {
34170
+ nextMetadata.metrics = metrics;
34171
+ } else {
34172
+ delete nextMetadata.metrics;
34173
+ }
34174
+ const goals = options?.goals;
34175
+ if (goals !== undefined) {
34176
+ if (goals === null) {
34177
+ delete nextMetadata.goals;
34178
+ } else {
34179
+ const currentGoals = isRecord2(nextMetadata.goals) ? nextMetadata.goals : {};
34180
+ const nextGoals = { ...currentGoals };
34181
+ for (const [key, value] of Object.entries(goals)) {
34182
+ if (value === null) {
34183
+ delete nextGoals[key];
34184
+ } else if (value !== undefined) {
34185
+ nextGoals[key] = value;
34186
+ }
34187
+ }
34188
+ if (Object.keys(nextGoals).length > 0) {
34189
+ nextMetadata.goals = nextGoals;
34190
+ } else {
34191
+ delete nextMetadata.goals;
34192
+ }
34193
+ }
34194
+ }
34195
+ if (options?.publishStatus !== undefined) {
34196
+ if (options.publishStatus === null) {
34197
+ delete nextMetadata.publishStatus;
34198
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34199
+ delete alphaLearn.publishStatus;
34200
+ if (Object.keys(alphaLearn).length > 0) {
34201
+ nextMetadata.AlphaLearn = alphaLearn;
34202
+ } else {
34203
+ delete nextMetadata.AlphaLearn;
34204
+ }
34205
+ } else {
34206
+ nextMetadata.publishStatus = options.publishStatus;
34207
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34208
+ alphaLearn.publishStatus = options.publishStatus === "published" ? "active" : options.publishStatus;
34209
+ nextMetadata.AlphaLearn = alphaLearn;
34210
+ }
34211
+ }
34212
+ if (options?.isSupplemental !== undefined) {
34213
+ nextMetadata.isSupplemental = options.isSupplemental;
34214
+ }
34215
+ if (options?.timebackVisible !== undefined) {
34216
+ if (options.timebackVisible === null) {
34217
+ delete nextMetadata.timebackVisible;
34218
+ } else {
34219
+ nextMetadata.timebackVisible = options.timebackVisible;
34220
+ }
34221
+ }
34222
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
34223
+ }
34224
+ static patchResourceMetadata(metadata2, options) {
34225
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34226
+ const playcademyMetadata = isRecord2(nextMetadata.playcademy) ? { ...nextMetadata.playcademy } : {};
34227
+ const masteryMetadata = isRecord2(playcademyMetadata.mastery) ? { ...playcademyMetadata.mastery } : {};
34228
+ nextMetadata.subject = options.subject;
34229
+ nextMetadata.grades = [options.grade];
34230
+ if (options.totalXp === null) {
34231
+ delete nextMetadata.xp;
34232
+ } else {
34233
+ nextMetadata.xp = options.totalXp;
34234
+ }
34235
+ if (options.masterableUnits === null) {
34236
+ delete masteryMetadata.masterableUnits;
34237
+ } else {
34238
+ masteryMetadata.masterableUnits = options.masterableUnits;
34239
+ }
34240
+ if (Object.keys(masteryMetadata).length > 0) {
34241
+ playcademyMetadata.mastery = masteryMetadata;
34242
+ } else {
34243
+ delete playcademyMetadata.mastery;
34244
+ }
34245
+ if (Object.keys(playcademyMetadata).length > 0) {
34246
+ nextMetadata.playcademy = playcademyMetadata;
34247
+ } else {
34248
+ delete nextMetadata.playcademy;
34249
+ }
34250
+ return nextMetadata;
34251
+ }
34252
+ static patchTimebackConfig(config2, patch) {
34253
+ return {
34254
+ ...config2,
34255
+ course: {
34256
+ ...config2.course,
34257
+ title: patch.title ?? config2.course.title,
34258
+ courseCode: patch.courseCode ?? config2.course.courseCode,
34259
+ subjects: [patch.subject],
34260
+ metadata: TimebackService.patchCourseMetadata(config2.course.metadata, patch.totalXp, {
34261
+ masterableUnits: patch.masterableUnits,
34262
+ goals: patch.goals,
34263
+ publishStatus: patch.publishStatus,
34264
+ isSupplemental: patch.isSupplemental,
34265
+ timebackVisible: patch.timebackVisible
34266
+ })
34267
+ },
34268
+ resource: {
34269
+ ...config2.resource,
34270
+ metadata: TimebackService.patchResourceMetadata(config2.resource.metadata, patch)
34271
+ }
34272
+ };
34273
+ }
34009
34274
  toGameTimebackIntegration(integration) {
34010
34275
  return {
34011
34276
  id: integration.id,
@@ -34019,6 +34284,21 @@ var init_timeback_service = __esm(() => {
34019
34284
  lastVerifiedAt: integration.lastVerifiedAt ?? null
34020
34285
  };
34021
34286
  }
34287
+ toGameTimebackIntegrationConfig(integration, config2) {
34288
+ const subject = config2.course.subjects[0] ?? integration.subject;
34289
+ if (!isTimebackSubject(subject)) {
34290
+ throw new ValidationError(`Invalid subject "${subject}"`);
34291
+ }
34292
+ return {
34293
+ integration: this.toGameTimebackIntegration(integration),
34294
+ title: config2.course.title,
34295
+ courseCode: config2.course.courseCode,
34296
+ subject,
34297
+ totalXp: TimebackService.getTotalXpFromConfig(config2) ?? integration.totalXp ?? null,
34298
+ masterableUnits: TimebackService.getMasterableUnitsFromConfig(config2),
34299
+ metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
34300
+ };
34301
+ }
34022
34302
  async endActivity({
34023
34303
  gameId,
34024
34304
  studentId,
@@ -34084,7 +34364,7 @@ var init_timeback_service = __esm(() => {
34084
34364
  ...runId ? { runId } : {}
34085
34365
  });
34086
34366
  }
34087
- logger20.info("Recorded activity completion", {
34367
+ logger21.info("Recorded activity completion", {
34088
34368
  gameId,
34089
34369
  courseId: integration.courseId,
34090
34370
  studentId,
@@ -34138,7 +34418,7 @@ var init_timeback_service = __esm(() => {
34138
34418
  masteredUnits: masteryStatus.masteredUnits,
34139
34419
  masterableUnits: masteryStatus.masterableUnits
34140
34420
  };
34141
- logger20.debug("Skipping course advancement because mastery is incomplete", {
34421
+ logger21.debug("Skipping course advancement because mastery is incomplete", {
34142
34422
  gameId,
34143
34423
  studentId,
34144
34424
  subject: currentIntegration.subject,
@@ -34156,7 +34436,7 @@ var init_timeback_service = __esm(() => {
34156
34436
  studentId,
34157
34437
  enrollments
34158
34438
  });
34159
- logger20.info("Manually advanced student", {
34439
+ logger21.info("Manually advanced student", {
34160
34440
  gameId,
34161
34441
  studentId,
34162
34442
  subject: currentIntegration.subject,
@@ -34189,7 +34469,7 @@ var init_timeback_service = __esm(() => {
34189
34469
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
34190
34470
  const effectiveResumeId = resumeId ?? runId;
34191
34471
  if (TimebackService.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
34192
- logger20.debug("Skipping duplicate heartbeat window", {
34472
+ logger21.debug("Skipping duplicate heartbeat window", {
34193
34473
  gameId,
34194
34474
  studentId,
34195
34475
  runId,
@@ -34202,7 +34482,7 @@ var init_timeback_service = __esm(() => {
34202
34482
  await this.deps.validateDeveloperAccess(user, gameId);
34203
34483
  const inFlightHeartbeat = TimebackService.getInFlightHeartbeatWindow(heartbeatWindowKey);
34204
34484
  if (inFlightHeartbeat) {
34205
- logger20.debug("Joining in-flight heartbeat window", {
34485
+ logger21.debug("Joining in-flight heartbeat window", {
34206
34486
  gameId,
34207
34487
  studentId,
34208
34488
  runId,
@@ -34239,7 +34519,7 @@ var init_timeback_service = __esm(() => {
34239
34519
  });
34240
34520
  }
34241
34521
  TimebackService.markHeartbeatWindowProcessed(heartbeatWindowKey);
34242
- logger20.debug("Recorded heartbeat", {
34522
+ logger21.debug("Recorded heartbeat", {
34243
34523
  gameId,
34244
34524
  courseId: integration.courseId,
34245
34525
  studentId,
@@ -34274,7 +34554,7 @@ var init_timeback_service = __esm(() => {
34274
34554
  });
34275
34555
  courseIds = integrations.map((i2) => i2.courseId);
34276
34556
  if (courseIds.length === 0) {
34277
- logger20.debug("No integrations found for game, returning 0 XP", {
34557
+ logger21.debug("No integrations found for game, returning 0 XP", {
34278
34558
  timebackId,
34279
34559
  gameId: options.gameId,
34280
34560
  grade: options.grade,
@@ -34291,7 +34571,7 @@ var init_timeback_service = __esm(() => {
34291
34571
  courseIds: courseIds.length > 0 ? courseIds : undefined,
34292
34572
  include: options?.include
34293
34573
  });
34294
- logger20.debug("Retrieved student XP", {
34574
+ logger21.debug("Retrieved student XP", {
34295
34575
  timebackId,
34296
34576
  gameId: options?.gameId,
34297
34577
  grade: options?.grade,
@@ -34320,15 +34600,15 @@ class UploadService {
34320
34600
  const { fileName, gameId } = request;
34321
34601
  const bucketName = this.deps.uploadBucket;
34322
34602
  if (!bucketName) {
34323
- logger21.error("Upload bucket not configured in environment");
34603
+ logger22.error("Upload bucket not configured in environment");
34324
34604
  throw new ValidationError("Upload bucket not configured");
34325
34605
  }
34326
34606
  await this.deps.validateDeveloperAccess(user, gameId);
34327
34607
  const version2 = ulid();
34328
34608
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
34329
- logger21.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34609
+ logger22.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34330
34610
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
34331
- logger21.info("Presigned URL generated", {
34611
+ logger22.info("Presigned URL generated", {
34332
34612
  userId: user.id,
34333
34613
  gameId,
34334
34614
  version: version2
@@ -34341,12 +34621,12 @@ class UploadService {
34341
34621
  };
34342
34622
  }
34343
34623
  }
34344
- var logger21;
34624
+ var logger22;
34345
34625
  var init_upload_service = __esm(() => {
34346
34626
  init_node();
34347
34627
  init_src2();
34348
34628
  init_errors();
34349
- logger21 = log.scope("UploadService");
34629
+ logger22 = log.scope("UploadService");
34350
34630
  });
34351
34631
 
34352
34632
  // ../api-core/src/services/factory/platform.ts
@@ -34660,7 +34940,7 @@ class AchievementService {
34660
34940
  results.push(result);
34661
34941
  }
34662
34942
  }
34663
- logger22.debug("Listed current achievements", { userId: user.id, count: results.length });
34943
+ logger23.debug("Listed current achievements", { userId: user.id, count: results.length });
34664
34944
  return results;
34665
34945
  }
34666
34946
  async listHistory(user, limit) {
@@ -34678,14 +34958,14 @@ class AchievementService {
34678
34958
  createdAt: c.createdAt,
34679
34959
  scopeKey: c.scopeKey
34680
34960
  }));
34681
- logger22.debug("Listed achievement history", { userId: user.id, count: results.length });
34961
+ logger23.debug("Listed achievement history", { userId: user.id, count: results.length });
34682
34962
  return results;
34683
34963
  }
34684
34964
  async submitProgress(achievementId, user) {
34685
34965
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
34686
34966
  broadcast: false
34687
34967
  });
34688
- logger22.debug("Submitted progress", {
34968
+ logger23.debug("Submitted progress", {
34689
34969
  userId: user.id,
34690
34970
  achievementId,
34691
34971
  wasNewClaim
@@ -34717,7 +34997,7 @@ class AchievementService {
34717
34997
  rewardCredits
34718
34998
  }).returning();
34719
34999
  if (!newClaim) {
34720
- logger22.error("Achievement claim insert returned no rows", {
35000
+ logger23.error("Achievement claim insert returned no rows", {
34721
35001
  userId,
34722
35002
  achievementId,
34723
35003
  scopeKey
@@ -34726,7 +35006,7 @@ class AchievementService {
34726
35006
  }
34727
35007
  await this.deps.addCredits(userId, rewardCredits);
34728
35008
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
34729
- logger22.info("Awarded achievement", {
35009
+ logger23.info("Awarded achievement", {
34730
35010
  userId,
34731
35011
  achievementId,
34732
35012
  scopeKey,
@@ -34763,7 +35043,7 @@ class AchievementService {
34763
35043
  return { title, body: body2 };
34764
35044
  }
34765
35045
  }
34766
- var logger22;
35046
+ var logger23;
34767
35047
  var init_achievement_service = __esm(() => {
34768
35048
  init_drizzle_orm();
34769
35049
  init_tables_index();
@@ -34772,7 +35052,7 @@ var init_achievement_service = __esm(() => {
34772
35052
  init_errors();
34773
35053
  init_leaderboard_util();
34774
35054
  init_scope_util();
34775
- logger22 = log.scope("AchievementService");
35055
+ logger23 = log.scope("AchievementService");
34776
35056
  });
34777
35057
 
34778
35058
  // ../api-core/src/services/inventory.service.ts
@@ -34800,7 +35080,7 @@ class InventoryService {
34800
35080
  },
34801
35081
  updatedAt: inventoryItems.updatedAt
34802
35082
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
34803
- logger23.debug("Listed inventory", { userId: user.id, count: inventory.length });
35083
+ logger24.debug("Listed inventory", { userId: user.id, count: inventory.length });
34804
35084
  return inventory;
34805
35085
  }
34806
35086
  async addItem(itemId, quantity, user) {
@@ -34817,7 +35097,7 @@ class InventoryService {
34817
35097
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
34818
35098
  return inserted?.quantity ?? 0;
34819
35099
  });
34820
- logger23.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
35100
+ logger24.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
34821
35101
  return { newTotal };
34822
35102
  }
34823
35103
  async removeItem(itemId, quantity, user) {
@@ -34828,7 +35108,7 @@ class InventoryService {
34828
35108
  }
34829
35109
  const [currentItem] = await tx.select({ id: inventoryItems.id, quantity: inventoryItems.quantity }).from(inventoryItems).where(and(eq(inventoryItems.userId, user.id), eq(inventoryItems.itemId, itemId), gte(inventoryItems.quantity, quantity))).limit(1);
34830
35110
  if (!currentItem) {
34831
- logger23.warn("Insufficient inventory for removal", {
35111
+ logger24.warn("Insufficient inventory for removal", {
34832
35112
  userId: user.id,
34833
35113
  itemId,
34834
35114
  requestedQuantity: quantity
@@ -34838,13 +35118,13 @@ class InventoryService {
34838
35118
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
34839
35119
  return updated?.quantity ?? 0;
34840
35120
  });
34841
- logger23.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
35121
+ logger24.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
34842
35122
  return { newTotal };
34843
35123
  }
34844
35124
  async addCredits(userId, amount) {
34845
35125
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
34846
35126
  if (!creditsItem) {
34847
- logger23.error("Primary currency not found", {
35127
+ logger24.error("Primary currency not found", {
34848
35128
  userId,
34849
35129
  amount
34850
35130
  });
@@ -34857,17 +35137,17 @@ class InventoryService {
34857
35137
  updatedAt: new Date
34858
35138
  }
34859
35139
  });
34860
- logger23.debug("Added credits", { userId, amount });
35140
+ logger24.debug("Added credits", { userId, amount });
34861
35141
  }
34862
35142
  }
34863
- var logger23;
35143
+ var logger24;
34864
35144
  var init_inventory_service = __esm(() => {
34865
35145
  init_drizzle_orm();
34866
35146
  init_src();
34867
35147
  init_tables_index();
34868
35148
  init_src2();
34869
35149
  init_errors();
34870
- logger23 = log.scope("InventoryService");
35150
+ logger24 = log.scope("InventoryService");
34871
35151
  });
34872
35152
 
34873
35153
  // ../api-core/src/services/leaderboard.service.ts
@@ -34899,7 +35179,7 @@ class LeaderboardService {
34899
35179
  sessionId
34900
35180
  }).returning();
34901
35181
  if (!newScore) {
34902
- logger24.error("Score insert returned no rows", { userId, gameId, score: input.score });
35182
+ logger25.error("Score insert returned no rows", { userId, gameId, score: input.score });
34903
35183
  throw new InternalError("Failed to insert score");
34904
35184
  }
34905
35185
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -34927,7 +35207,7 @@ class LeaderboardService {
34927
35207
  movedUpWithinTop3
34928
35208
  });
34929
35209
  }
34930
- logger24.info("Score submitted", {
35210
+ logger25.info("Score submitted", {
34931
35211
  gameId,
34932
35212
  userId,
34933
35213
  isAnonymousUser,
@@ -35004,7 +35284,7 @@ class LeaderboardService {
35004
35284
  });
35005
35285
  }
35006
35286
  } catch (error) {
35007
- logger24.warn("Failed to publish notification", { error });
35287
+ logger25.warn("Failed to publish notification", { error });
35008
35288
  }
35009
35289
  }
35010
35290
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -35123,7 +35403,7 @@ class LeaderboardService {
35123
35403
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
35124
35404
  }
35125
35405
  }
35126
- var logger24;
35406
+ var logger25;
35127
35407
  var init_leaderboard_service = __esm(() => {
35128
35408
  init_drizzle_orm();
35129
35409
  init_src();
@@ -35133,7 +35413,7 @@ var init_leaderboard_service = __esm(() => {
35133
35413
  init_notification();
35134
35414
  init_errors();
35135
35415
  init_leaderboard_util();
35136
- logger24 = log.scope("LeaderboardService");
35416
+ logger25 = log.scope("LeaderboardService");
35137
35417
  });
35138
35418
 
35139
35419
  // ../api-core/src/services/level.service.ts
@@ -35149,9 +35429,9 @@ class LevelService {
35149
35429
  for (const config2 of configs) {
35150
35430
  levelConfigCache.set(config2.level, config2);
35151
35431
  }
35152
- logger25.info("Cache pre-warmed", { count: configs.length });
35432
+ logger26.info("Cache pre-warmed", { count: configs.length });
35153
35433
  } catch (error) {
35154
- logger25.error("Cache pre-warm failed", { error });
35434
+ logger26.error("Cache pre-warm failed", { error });
35155
35435
  }
35156
35436
  }
35157
35437
  async getConfig(level) {
@@ -35185,7 +35465,7 @@ class LevelService {
35185
35465
  totalXP
35186
35466
  }).returning();
35187
35467
  if (!newUserLevel) {
35188
- logger25.error("User level insert returned no rows", { userId: user.id });
35468
+ logger26.error("User level insert returned no rows", { userId: user.id });
35189
35469
  throw new InternalError("Failed to create user level cache record");
35190
35470
  }
35191
35471
  userLevel = newUserLevel;
@@ -35200,7 +35480,7 @@ class LevelService {
35200
35480
  userLevel = updatedUserLevel;
35201
35481
  }
35202
35482
  }
35203
- logger25.debug("Retrieved user level", {
35483
+ logger26.debug("Retrieved user level", {
35204
35484
  userId: user.id,
35205
35485
  totalXP,
35206
35486
  currentLevel,
@@ -35211,7 +35491,7 @@ class LevelService {
35211
35491
  async getProgress(user) {
35212
35492
  const userLevel = await this.getByUser(user);
35213
35493
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
35214
- logger25.debug("Retrieved progress", { userId: user.id });
35494
+ logger26.debug("Retrieved progress", { userId: user.id });
35215
35495
  return {
35216
35496
  level: userLevel.currentLevel,
35217
35497
  currentXp: userLevel.currentXp,
@@ -35245,7 +35525,7 @@ class LevelService {
35245
35525
  if (leveledUp && previousUserLevel) {
35246
35526
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
35247
35527
  }
35248
- logger25.info("Synced from Timeback", {
35528
+ logger26.info("Synced from Timeback", {
35249
35529
  userId,
35250
35530
  totalXP,
35251
35531
  currentLevel,
@@ -35286,7 +35566,7 @@ class LevelService {
35286
35566
  }
35287
35567
  if (totalCredits > 0) {
35288
35568
  await this.deps.addCredits(userId, totalCredits);
35289
- logger25.info("Awarded level-up credits", {
35569
+ logger26.info("Awarded level-up credits", {
35290
35570
  userId,
35291
35571
  fromLevel,
35292
35572
  toLevel,
@@ -35324,14 +35604,14 @@ class LevelService {
35324
35604
  };
35325
35605
  }
35326
35606
  }
35327
- var logger25, levelConfigCache = null;
35607
+ var logger26, levelConfigCache = null;
35328
35608
  var init_level_service = __esm(() => {
35329
35609
  init_drizzle_orm();
35330
35610
  init_src();
35331
35611
  init_tables_index();
35332
35612
  init_src2();
35333
35613
  init_errors();
35334
- logger25 = log.scope("LevelService");
35614
+ logger26 = log.scope("LevelService");
35335
35615
  });
35336
35616
 
35337
35617
  // ../realtime/src/server/domain/events.ts
@@ -35352,13 +35632,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
35352
35632
  });
35353
35633
  if (!res.ok) {
35354
35634
  const text3 = await res.text().catch(() => "");
35355
- logger26.warn("Failed to publish to user", {
35635
+ logger27.warn("Failed to publish to user", {
35356
35636
  status: res.status,
35357
35637
  body: text3
35358
35638
  });
35359
35639
  }
35360
35640
  } catch (error) {
35361
- logger26.error("Publish to user error", { error });
35641
+ logger27.error("Publish to user error", { error });
35362
35642
  }
35363
35643
  }
35364
35644
 
@@ -35380,7 +35660,7 @@ class NotificationService {
35380
35660
  conditions2.push(eq(notifications.type, type));
35381
35661
  }
35382
35662
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
35383
- logger26.debug("Listed notifications", { userId: user.id, count: results.length });
35663
+ logger27.debug("Listed notifications", { userId: user.id, count: results.length });
35384
35664
  return results;
35385
35665
  }
35386
35666
  async updateStatus(notificationId, status, method) {
@@ -35399,7 +35679,7 @@ class NotificationService {
35399
35679
  if (!updated) {
35400
35680
  throw new NotFoundError("Notification", notificationId);
35401
35681
  }
35402
- logger26.debug("Updated status", { notificationId, status });
35682
+ logger27.debug("Updated status", { notificationId, status });
35403
35683
  return updated;
35404
35684
  }
35405
35685
  async getStats(user, options) {
@@ -35425,7 +35705,7 @@ class NotificationService {
35425
35705
  const clicked = statsMap.clicked || 0;
35426
35706
  const dismissed = statsMap.dismissed || 0;
35427
35707
  const expired = statsMap.expired || 0;
35428
- logger26.debug("Retrieved stats", { userId: user.id, total });
35708
+ logger27.debug("Retrieved stats", { userId: user.id, total });
35429
35709
  return {
35430
35710
  total,
35431
35711
  delivered,
@@ -35474,7 +35754,7 @@ class NotificationService {
35474
35754
  options: { data, clickUrl, metadata: metadata2 }
35475
35755
  });
35476
35756
  }
35477
- logger26.debug("Created notification", {
35757
+ logger27.debug("Created notification", {
35478
35758
  userId,
35479
35759
  type,
35480
35760
  id: notificationId,
@@ -35482,7 +35762,7 @@ class NotificationService {
35482
35762
  });
35483
35763
  return notificationId;
35484
35764
  } catch (error) {
35485
- logger26.error("Failed to create notification", { userId, type, error });
35765
+ logger27.error("Failed to create notification", { userId, type, error });
35486
35766
  return null;
35487
35767
  }
35488
35768
  }
@@ -35496,7 +35776,7 @@ class NotificationService {
35496
35776
  }) {
35497
35777
  const realtimeConfig = this.deps.realtime;
35498
35778
  if (!realtimeConfig) {
35499
- logger26.warn("No realtime config for publish");
35779
+ logger27.warn("No realtime config for publish");
35500
35780
  return;
35501
35781
  }
35502
35782
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35538,13 +35818,13 @@ class NotificationService {
35538
35818
  metadata: data.metadata || {}
35539
35819
  }).returning();
35540
35820
  if (!notification) {
35541
- logger26.error("Notification insert returned no rows", {
35821
+ logger27.error("Notification insert returned no rows", {
35542
35822
  userId: data.userId,
35543
35823
  type: data.type
35544
35824
  });
35545
35825
  throw new InternalError("Failed to create notification");
35546
35826
  }
35547
- logger26.info("Inserted notification", {
35827
+ logger27.info("Inserted notification", {
35548
35828
  notificationId: notification.id,
35549
35829
  userId: notification.userId,
35550
35830
  type: notification.type
@@ -35554,7 +35834,7 @@ class NotificationService {
35554
35834
  async deliverPending(userId) {
35555
35835
  const realtimeConfig = this.deps.realtime;
35556
35836
  if (!realtimeConfig) {
35557
- logger26.warn("No realtime config for delivery");
35837
+ logger27.warn("No realtime config for delivery");
35558
35838
  return;
35559
35839
  }
35560
35840
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35581,13 +35861,13 @@ class NotificationService {
35581
35861
  metadata: notification.metadata,
35582
35862
  clickUrl: notification.clickUrl
35583
35863
  });
35584
- logger26.info("Delivered notification", {
35864
+ logger27.info("Delivered notification", {
35585
35865
  notificationId: notification.id,
35586
35866
  userId,
35587
35867
  type: notification.type
35588
35868
  });
35589
35869
  } catch (error) {
35590
- logger26.warn("Failed to deliver", {
35870
+ logger27.warn("Failed to deliver", {
35591
35871
  notificationId: notification.id,
35592
35872
  error
35593
35873
  });
@@ -35595,7 +35875,7 @@ class NotificationService {
35595
35875
  }
35596
35876
  }
35597
35877
  }
35598
- var logger26;
35878
+ var logger27;
35599
35879
  var init_notification_service = __esm(() => {
35600
35880
  init_drizzle_orm();
35601
35881
  init_src();
@@ -35604,7 +35884,7 @@ var init_notification_service = __esm(() => {
35604
35884
  init_events();
35605
35885
  init_notification();
35606
35886
  init_errors();
35607
- logger26 = log.scope("NotificationService");
35887
+ logger27 = log.scope("NotificationService");
35608
35888
  });
35609
35889
 
35610
35890
  // ../api-core/src/services/factory/player.ts
@@ -35728,7 +36008,7 @@ class CharacterService {
35728
36008
  createdAt: characterComponents.createdAt,
35729
36009
  updatedAt: characterComponents.updatedAt
35730
36010
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
35731
- logger27.debug("Listed available components", {
36011
+ logger28.debug("Listed available components", {
35732
36012
  level,
35733
36013
  count: components.length
35734
36014
  });
@@ -35746,7 +36026,7 @@ class CharacterService {
35746
36026
  }
35747
36027
  }
35748
36028
  });
35749
- logger27.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
36029
+ logger28.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
35750
36030
  return pc ?? null;
35751
36031
  }
35752
36032
  async getByUserId(userId) {
@@ -35761,7 +36041,7 @@ class CharacterService {
35761
36041
  }
35762
36042
  }
35763
36043
  });
35764
- logger27.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
36044
+ logger28.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
35765
36045
  return pc ?? null;
35766
36046
  }
35767
36047
  async create(input, user) {
@@ -35776,13 +36056,13 @@ class CharacterService {
35776
36056
  }
35777
36057
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
35778
36058
  if (!characterRow) {
35779
- logger27.error("Character insert returned no rows", { userId: user.id });
36059
+ logger28.error("Character insert returned no rows", { userId: user.id });
35780
36060
  throw new InternalError("Failed to create character in database");
35781
36061
  }
35782
36062
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
35783
36063
  return characterRow;
35784
36064
  });
35785
- logger27.info("Created character", { userId: user.id, characterId: result.id });
36065
+ logger28.info("Created character", { userId: user.id, characterId: result.id });
35786
36066
  return result;
35787
36067
  }
35788
36068
  async update(input, user) {
@@ -35794,7 +36074,7 @@ class CharacterService {
35794
36074
  if (!row) {
35795
36075
  throw new NotFoundError("Player character");
35796
36076
  }
35797
- logger27.info("Updated character", {
36077
+ logger28.info("Updated character", {
35798
36078
  userId: user.id,
35799
36079
  characterId: row.id,
35800
36080
  updatedFields: Object.keys(input)
@@ -35816,7 +36096,7 @@ class CharacterService {
35816
36096
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
35817
36097
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
35818
36098
  if (!validation.isValid) {
35819
- logger27.warn("Accessory validation failed", {
36099
+ logger28.warn("Accessory validation failed", {
35820
36100
  userId: user.id,
35821
36101
  slot,
35822
36102
  accessoryComponentId,
@@ -35832,14 +36112,14 @@ class CharacterService {
35832
36112
  slot
35833
36113
  }).returning();
35834
36114
  if (!result) {
35835
- logger27.error("Accessory insert returned no rows", {
36115
+ logger28.error("Accessory insert returned no rows", {
35836
36116
  userId: user.id,
35837
36117
  slot,
35838
36118
  accessoryComponentId
35839
36119
  });
35840
36120
  throw new InternalError("Failed to equip accessory");
35841
36121
  }
35842
- logger27.info("Equipped accessory", {
36122
+ logger28.info("Equipped accessory", {
35843
36123
  userId: user.id,
35844
36124
  slot,
35845
36125
  accessoryComponentId
@@ -35860,7 +36140,7 @@ class CharacterService {
35860
36140
  const playerLevel = userLevel?.currentLevel ?? 1;
35861
36141
  const validation = validateAccessoryRemoval(slot, playerLevel);
35862
36142
  if (!validation.isValid) {
35863
- logger27.warn("Accessory removal validation failed", {
36143
+ logger28.warn("Accessory removal validation failed", {
35864
36144
  userId: user.id,
35865
36145
  slot,
35866
36146
  playerLevel,
@@ -35869,17 +36149,17 @@ class CharacterService {
35869
36149
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
35870
36150
  }
35871
36151
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
35872
- logger27.info("Removed accessory", { userId: user.id, slot });
36152
+ logger28.info("Removed accessory", { userId: user.id, slot });
35873
36153
  }
35874
36154
  }
35875
- var logger27;
36155
+ var logger28;
35876
36156
  var init_character_service = __esm(() => {
35877
36157
  init_drizzle_orm();
35878
36158
  init_tables_index();
35879
36159
  init_src2();
35880
36160
  init_errors();
35881
36161
  init_accessory_util();
35882
- logger27 = log.scope("CharacterService");
36162
+ logger28 = log.scope("CharacterService");
35883
36163
  });
35884
36164
 
35885
36165
  // ../api-core/src/services/currency.service.ts
@@ -35891,7 +36171,7 @@ class CurrencyService {
35891
36171
  async list() {
35892
36172
  const db2 = this.deps.db;
35893
36173
  const allCurrencies = await db2.query.currencies.findMany();
35894
- logger28.debug("Listed currencies", { count: allCurrencies.length });
36174
+ logger29.debug("Listed currencies", { count: allCurrencies.length });
35895
36175
  return allCurrencies;
35896
36176
  }
35897
36177
  async getById(currencyId) {
@@ -35902,7 +36182,7 @@ class CurrencyService {
35902
36182
  if (!currency) {
35903
36183
  throw new NotFoundError("Currency", currencyId);
35904
36184
  }
35905
- logger28.debug("Retrieved currency", { currencyId });
36185
+ logger29.debug("Retrieved currency", { currencyId });
35906
36186
  return currency;
35907
36187
  }
35908
36188
  async create(data) {
@@ -35910,13 +36190,13 @@ class CurrencyService {
35910
36190
  try {
35911
36191
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
35912
36192
  if (!newCurrency) {
35913
- logger28.error("Currency insert returned no rows", {
36193
+ logger29.error("Currency insert returned no rows", {
35914
36194
  itemId: data.itemId,
35915
36195
  symbol: data.symbol
35916
36196
  });
35917
36197
  throw new InternalError("Failed to create currency");
35918
36198
  }
35919
- logger28.info("Created currency", {
36199
+ logger29.info("Created currency", {
35920
36200
  currencyId: newCurrency.id,
35921
36201
  itemId: newCurrency.itemId,
35922
36202
  symbol: newCurrency.symbol,
@@ -35945,7 +36225,7 @@ class CurrencyService {
35945
36225
  if (!updatedCurrency) {
35946
36226
  throw new NotFoundError("Currency", currencyId);
35947
36227
  }
35948
- logger28.info("Updated currency", {
36228
+ logger29.info("Updated currency", {
35949
36229
  currencyId: updatedCurrency.id,
35950
36230
  updatedFields: Object.keys(data)
35951
36231
  });
@@ -35971,16 +36251,16 @@ class CurrencyService {
35971
36251
  if (result.length === 0) {
35972
36252
  throw new NotFoundError("Currency", currencyId);
35973
36253
  }
35974
- logger28.info("Deleted currency", { currencyId });
36254
+ logger29.info("Deleted currency", { currencyId });
35975
36255
  }
35976
36256
  }
35977
- var logger28;
36257
+ var logger29;
35978
36258
  var init_currency_service = __esm(() => {
35979
36259
  init_drizzle_orm();
35980
36260
  init_tables_index();
35981
36261
  init_src2();
35982
36262
  init_errors();
35983
- logger28 = log.scope("CurrencyService");
36263
+ logger29 = log.scope("CurrencyService");
35984
36264
  });
35985
36265
 
35986
36266
  // ../api-core/src/services/logs.service.ts
@@ -35999,11 +36279,11 @@ class LogsService {
35999
36279
  if (!game) {
36000
36280
  throw new NotFoundError("Game", slug2);
36001
36281
  }
36002
- logger29.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
36282
+ logger30.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
36003
36283
  } else {
36004
36284
  const isApprovedDev = user.developerStatus === "approved";
36005
36285
  if (!isApprovedDev) {
36006
- logger29.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
36286
+ logger30.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
36007
36287
  throw new AccessDeniedError("Must be an approved developer");
36008
36288
  }
36009
36289
  const game = await db2.query.games.findFirst({
@@ -36018,7 +36298,7 @@ class LogsService {
36018
36298
  columns: { id: true }
36019
36299
  });
36020
36300
  if (!membership) {
36021
- logger29.warn("Developer attempted access to unowned game logs", {
36301
+ logger30.warn("Developer attempted access to unowned game logs", {
36022
36302
  userId: user.id,
36023
36303
  slug: slug2
36024
36304
  });
@@ -36027,7 +36307,7 @@ class LogsService {
36027
36307
  }
36028
36308
  const workerId = getDeploymentId(slug2, sstStage);
36029
36309
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
36030
- logger29.debug("Generated log stream token", {
36310
+ logger30.debug("Generated log stream token", {
36031
36311
  userId: user.id,
36032
36312
  slug: slug2,
36033
36313
  workerId
@@ -36035,14 +36315,14 @@ class LogsService {
36035
36315
  return { token, workerId };
36036
36316
  }
36037
36317
  }
36038
- var logger29;
36318
+ var logger30;
36039
36319
  var init_logs_service = __esm(() => {
36040
36320
  init_drizzle_orm();
36041
36321
  init_tables_index();
36042
36322
  init_src2();
36043
36323
  init_errors();
36044
36324
  init_deployment_util();
36045
- logger29 = log.scope("LogsService");
36325
+ logger30 = log.scope("LogsService");
36046
36326
  });
36047
36327
 
36048
36328
  // ../api-core/src/services/lti.service.ts
@@ -36095,7 +36375,7 @@ class MapService {
36095
36375
  if (!mapDetails) {
36096
36376
  throw new NotFoundError("Map", identifier);
36097
36377
  }
36098
- logger30.debug("Retrieved map", { identifier });
36378
+ logger31.debug("Retrieved map", { identifier });
36099
36379
  return mapDetails;
36100
36380
  }
36101
36381
  async getElements(mapId) {
@@ -36111,7 +36391,7 @@ class MapService {
36111
36391
  }
36112
36392
  }
36113
36393
  });
36114
- logger30.debug("Retrieved elements", { mapId, count: elements.length });
36394
+ logger31.debug("Retrieved elements", { mapId, count: elements.length });
36115
36395
  return elements;
36116
36396
  }
36117
36397
  async getObjects(mapId, userId) {
@@ -36132,7 +36412,7 @@ class MapService {
36132
36412
  }
36133
36413
  }
36134
36414
  });
36135
- logger30.debug("Retrieved objects", { mapId, userId, count: objects.length });
36415
+ logger31.debug("Retrieved objects", { mapId, userId, count: objects.length });
36136
36416
  return objects.map((object) => this.formatMapObjectWithItem(object));
36137
36417
  }
36138
36418
  async createObject(mapId, data, user) {
@@ -36159,7 +36439,7 @@ class MapService {
36159
36439
  throw new NotFoundError("Item", data.itemId);
36160
36440
  }
36161
36441
  if (!item.isPlaceable) {
36162
- logger30.warn("Attempted to place non-placeable item", {
36442
+ logger31.warn("Attempted to place non-placeable item", {
36163
36443
  userId: user.id,
36164
36444
  itemId: data.itemId,
36165
36445
  mapId
@@ -36173,7 +36453,7 @@ class MapService {
36173
36453
  };
36174
36454
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
36175
36455
  if (!createdObject) {
36176
- logger30.error("Map object insert returned no rows", {
36456
+ logger31.error("Map object insert returned no rows", {
36177
36457
  userId: user.id,
36178
36458
  mapId,
36179
36459
  itemId: data.itemId
@@ -36197,12 +36477,12 @@ class MapService {
36197
36477
  }
36198
36478
  });
36199
36479
  if (!objectWithItem) {
36200
- logger30.error("Map object query after insert returned no rows", {
36480
+ logger31.error("Map object query after insert returned no rows", {
36201
36481
  objectId: createdObject.id
36202
36482
  });
36203
36483
  throw new InternalError("Failed to retrieve created object");
36204
36484
  }
36205
- logger30.info("Created object", {
36485
+ logger31.info("Created object", {
36206
36486
  userId: user.id,
36207
36487
  mapId,
36208
36488
  objectId: createdObject.id,
@@ -36226,7 +36506,7 @@ class MapService {
36226
36506
  if (result.length === 0) {
36227
36507
  throw new NotFoundError("MapObject", objectId);
36228
36508
  }
36229
- logger30.info("Deleted object", {
36509
+ logger31.info("Deleted object", {
36230
36510
  userId: user.id,
36231
36511
  mapId,
36232
36512
  objectId
@@ -36255,13 +36535,13 @@ class MapService {
36255
36535
  };
36256
36536
  }
36257
36537
  }
36258
- var logger30;
36538
+ var logger31;
36259
36539
  var init_map_service = __esm(() => {
36260
36540
  init_drizzle_orm();
36261
36541
  init_tables_index();
36262
36542
  init_src2();
36263
36543
  init_errors();
36264
- logger30 = log.scope("MapService");
36544
+ logger31 = log.scope("MapService");
36265
36545
  });
36266
36546
 
36267
36547
  // ../api-core/src/services/realtime.service.ts
@@ -36296,20 +36576,20 @@ class RealtimeService {
36296
36576
  }
36297
36577
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
36298
36578
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
36299
- logger31.info("Generated token", {
36579
+ logger32.info("Generated token", {
36300
36580
  userId: user.id,
36301
36581
  gameId: resolvedGameId || "global"
36302
36582
  });
36303
36583
  return { token };
36304
36584
  }
36305
36585
  }
36306
- var logger31;
36586
+ var logger32;
36307
36587
  var init_realtime_service = __esm(() => {
36308
36588
  init_drizzle_orm();
36309
36589
  init_tables_index();
36310
36590
  init_src2();
36311
36591
  init_errors();
36312
- logger31 = log.scope("RealtimeService");
36592
+ logger32 = log.scope("RealtimeService");
36313
36593
  });
36314
36594
 
36315
36595
  // ../api-core/src/services/session.service.ts
@@ -36344,10 +36624,10 @@ class SessionService {
36344
36624
  };
36345
36625
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
36346
36626
  if (!newSession?.sessionId) {
36347
- logger32.error("Game session insert returned no rows", { userId, gameId });
36627
+ logger33.error("Game session insert returned no rows", { userId, gameId });
36348
36628
  throw new InternalError("Failed to create game session");
36349
36629
  }
36350
- logger32.info("Started new session", {
36630
+ logger33.info("Started new session", {
36351
36631
  sessionId: newSession.sessionId,
36352
36632
  gameId,
36353
36633
  userId
@@ -36368,23 +36648,23 @@ class SessionService {
36368
36648
  return { success: true, message: "Session already ended" };
36369
36649
  }
36370
36650
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
36371
- logger32.info("Ended session", { sessionId, gameId, userId });
36651
+ logger33.info("Ended session", { sessionId, gameId, userId });
36372
36652
  return { success: true };
36373
36653
  }
36374
36654
  async mintToken(gameIdOrSlug, userId) {
36375
36655
  const gameId = await this.resolveGameId(gameIdOrSlug);
36376
36656
  const result = await this.deps.mintGameToken(gameId, userId);
36377
- logger32.debug("Minted game token", { gameId, userId });
36657
+ logger33.debug("Minted game token", { gameId, userId });
36378
36658
  return result;
36379
36659
  }
36380
36660
  }
36381
- var logger32;
36661
+ var logger33;
36382
36662
  var init_session_service = __esm(() => {
36383
36663
  init_drizzle_orm();
36384
36664
  init_tables_index();
36385
36665
  init_src2();
36386
36666
  init_errors();
36387
- logger32 = log.scope("SessionService");
36667
+ logger33 = log.scope("SessionService");
36388
36668
  });
36389
36669
 
36390
36670
  // ../api-core/src/services/shop.service.ts
@@ -36430,7 +36710,7 @@ class ShopService {
36430
36710
  const shopItems = [];
36431
36711
  for (const listing of listingsWithRelations) {
36432
36712
  if (!listing.item || !listing.currency) {
36433
- logger33.warn("Listing missing item or currency, skipping", {
36713
+ logger34.warn("Listing missing item or currency, skipping", {
36434
36714
  listingId: listing.id
36435
36715
  });
36436
36716
  } else {
@@ -36447,7 +36727,7 @@ class ShopService {
36447
36727
  });
36448
36728
  }
36449
36729
  }
36450
- logger33.debug("Retrieved shop view", {
36730
+ logger34.debug("Retrieved shop view", {
36451
36731
  userId: user.id,
36452
36732
  itemCount: shopItems.length,
36453
36733
  currencyCount: shopCurrencies.length
@@ -36458,12 +36738,12 @@ class ShopService {
36458
36738
  };
36459
36739
  }
36460
36740
  }
36461
- var logger33;
36741
+ var logger34;
36462
36742
  var init_shop_service = __esm(() => {
36463
36743
  init_drizzle_orm();
36464
36744
  init_tables_index();
36465
36745
  init_src2();
36466
- logger33 = log.scope("ShopService");
36746
+ logger34 = log.scope("ShopService");
36467
36747
  });
36468
36748
 
36469
36749
  // ../api-core/src/services/sprite.service.ts
@@ -36480,17 +36760,17 @@ class SpriteService {
36480
36760
  if (!template) {
36481
36761
  throw new NotFoundError("SpriteTemplate", slug2);
36482
36762
  }
36483
- logger34.debug("Retrieved sprite", { slug: slug2 });
36763
+ logger35.debug("Retrieved sprite", { slug: slug2 });
36484
36764
  return template;
36485
36765
  }
36486
36766
  }
36487
- var logger34;
36767
+ var logger35;
36488
36768
  var init_sprite_service = __esm(() => {
36489
36769
  init_drizzle_orm();
36490
36770
  init_tables_index();
36491
36771
  init_src2();
36492
36772
  init_errors();
36493
- logger34 = log.scope("SpriteService");
36773
+ logger35 = log.scope("SpriteService");
36494
36774
  });
36495
36775
 
36496
36776
  // ../api-core/src/services/user.service.ts
@@ -36505,12 +36785,12 @@ class UserService {
36505
36785
  where: eq(users.id, user.id)
36506
36786
  });
36507
36787
  if (!userData) {
36508
- logger35.error("User not found", { userId: user.id });
36788
+ logger36.error("User not found", { userId: user.id });
36509
36789
  throw new NotFoundError("User", user.id);
36510
36790
  }
36511
36791
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
36512
36792
  if (gameId) {
36513
- logger35.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36793
+ logger36.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36514
36794
  return {
36515
36795
  id: userData.id,
36516
36796
  name: userData.name,
@@ -36523,7 +36803,7 @@ class UserService {
36523
36803
  const timebackAccount = await db2.query.accounts.findFirst({
36524
36804
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
36525
36805
  });
36526
- logger35.debug("Fetched user profile (platform context)", { userId: user.id });
36806
+ logger36.debug("Fetched user profile (platform context)", { userId: user.id });
36527
36807
  return {
36528
36808
  id: userData.id,
36529
36809
  name: userData.name,
@@ -36546,7 +36826,7 @@ class UserService {
36546
36826
  columns: { name: true }
36547
36827
  });
36548
36828
  if (!userData) {
36549
- logger35.error("Demo user not found", { userId });
36829
+ logger36.error("Demo user not found", { userId });
36550
36830
  throw new NotFoundError("User", userId);
36551
36831
  }
36552
36832
  return {
@@ -36560,10 +36840,10 @@ class UserService {
36560
36840
  updatedAt: new Date
36561
36841
  }).where(eq(users.id, userId)).returning({ name: users.name });
36562
36842
  if (!updatedUser) {
36563
- logger35.error("Demo user not found for profile update", { userId });
36843
+ logger36.error("Demo user not found for profile update", { userId });
36564
36844
  throw new NotFoundError("User", userId);
36565
36845
  }
36566
- logger35.debug("Updated demo profile", { userId, displayName });
36846
+ logger36.debug("Updated demo profile", { userId, displayName });
36567
36847
  return {
36568
36848
  displayName: updatedUser.name,
36569
36849
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -36576,7 +36856,7 @@ class UserService {
36576
36856
  ]);
36577
36857
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
36578
36858
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
36579
- logger35.debug("Fetched Timeback data", {
36859
+ logger36.debug("Fetched Timeback data", {
36580
36860
  timebackId,
36581
36861
  role,
36582
36862
  enrollmentCount: enrollments.length,
@@ -36585,9 +36865,9 @@ class UserService {
36585
36865
  return { id: timebackId, role, enrollments, organizations };
36586
36866
  }
36587
36867
  async fetchStudentProfile(timebackId) {
36588
- logger35.debug("Fetching student profile", { timebackId });
36868
+ logger36.debug("Fetching student profile", { timebackId });
36589
36869
  if (!this.deps.timeback) {
36590
- logger35.warn("Timeback client not available");
36870
+ logger36.warn("Timeback client not available");
36591
36871
  return { role: "student", organizations: [] };
36592
36872
  }
36593
36873
  try {
@@ -36615,14 +36895,14 @@ class UserService {
36615
36895
  }
36616
36896
  return { role, organizations: [...orgMap.values()] };
36617
36897
  } catch (error) {
36618
- logger35.warn("Failed to fetch student profile", { error, timebackId });
36898
+ logger36.warn("Failed to fetch student profile", { error, timebackId });
36619
36899
  return { role: "student", organizations: [] };
36620
36900
  }
36621
36901
  }
36622
36902
  async fetchEnrollments(timebackId) {
36623
- logger35.debug("Fetching enrollments", { timebackId });
36903
+ logger36.debug("Fetching enrollments", { timebackId });
36624
36904
  if (!this.deps.timeback) {
36625
- logger35.warn("Timeback client not available");
36905
+ logger36.warn("Timeback client not available");
36626
36906
  return [];
36627
36907
  }
36628
36908
  try {
@@ -36643,7 +36923,7 @@ class UserService {
36643
36923
  orgId: courseToSchool.get(i2.courseId)
36644
36924
  }));
36645
36925
  } catch (error) {
36646
- logger35.warn("Failed to fetch enrollments", { error, timebackId });
36926
+ logger36.warn("Failed to fetch enrollments", { error, timebackId });
36647
36927
  return [];
36648
36928
  }
36649
36929
  }
@@ -36658,14 +36938,14 @@ class UserService {
36658
36938
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
36659
36939
  }
36660
36940
  }
36661
- var logger35;
36941
+ var logger36;
36662
36942
  var init_user_service = __esm(() => {
36663
36943
  init_drizzle_orm();
36664
36944
  init_src();
36665
36945
  init_tables_index();
36666
36946
  init_src2();
36667
36947
  init_errors();
36668
- logger35 = log.scope("UserService");
36948
+ logger36 = log.scope("UserService");
36669
36949
  });
36670
36950
 
36671
36951
  // ../api-core/src/services/verify.service.ts
@@ -36675,16 +36955,16 @@ class VerifyService {
36675
36955
  this.deps = deps;
36676
36956
  }
36677
36957
  async verifyGameToken(token) {
36678
- logger36.debug("Verifying game token");
36958
+ logger37.debug("Verifying game token");
36679
36959
  const payload = await this.deps.validateGameToken(token);
36680
36960
  if (!payload) {
36681
- logger36.warn("Invalid or expired game token presented");
36961
+ logger37.warn("Invalid or expired game token presented");
36682
36962
  throw new ValidationError("Invalid or expired token");
36683
36963
  }
36684
36964
  const gameId = payload.sub;
36685
36965
  const userId = payload.uid;
36686
36966
  if (typeof gameId !== "string" || typeof userId !== "string") {
36687
- logger36.warn("Game token missing required claims", {
36967
+ logger37.warn("Game token missing required claims", {
36688
36968
  hasGameId: typeof gameId === "string",
36689
36969
  hasUserId: typeof userId === "string"
36690
36970
  });
@@ -36695,7 +36975,7 @@ class VerifyService {
36695
36975
  where: eq(users.id, userId)
36696
36976
  });
36697
36977
  if (!userData) {
36698
- logger36.error("User not found for valid token", {
36978
+ logger37.error("User not found for valid token", {
36699
36979
  userId
36700
36980
  });
36701
36981
  throw new NotFoundError("User", userId);
@@ -36709,7 +36989,7 @@ class VerifyService {
36709
36989
  family_name: undefined,
36710
36990
  timeback_id: userData.timebackId || undefined
36711
36991
  };
36712
- logger36.info("Token verified", { gameId, userId });
36992
+ logger37.info("Token verified", { gameId, userId });
36713
36993
  return {
36714
36994
  claims: payload,
36715
36995
  gameId,
@@ -36717,13 +36997,13 @@ class VerifyService {
36717
36997
  };
36718
36998
  }
36719
36999
  }
36720
- var logger36;
37000
+ var logger37;
36721
37001
  var init_verify_service = __esm(() => {
36722
37002
  init_drizzle_orm();
36723
37003
  init_tables_index();
36724
37004
  init_src2();
36725
37005
  init_errors();
36726
- logger36 = log.scope("VerifyService");
37006
+ logger37 = log.scope("VerifyService");
36727
37007
  });
36728
37008
 
36729
37009
  // ../api-core/src/services/factory/standalone.ts
@@ -41841,7 +42121,7 @@ var humanize = (times) => {
41841
42121
  }
41842
42122
  }
41843
42123
  return `${status}`;
41844
- }, logger37 = (fn = console.log) => {
42124
+ }, logger38 = (fn = console.log) => {
41845
42125
  return async function logger2(c, next) {
41846
42126
  const { method, url: url2 } = c.req;
41847
42127
  const path = url2.slice(url2.indexOf("/", 8));
@@ -42022,7 +42302,7 @@ function createApp(db2, options) {
42022
42302
  const app = new Hono2;
42023
42303
  app.use("*", cors({ origin: "*", credentials: true }));
42024
42304
  if (options.verbose && !options.quiet) {
42025
- app.use("*", logger37());
42305
+ app.use("*", logger38());
42026
42306
  }
42027
42307
  app.use("/api/*", async (c, next) => {
42028
42308
  c.set("db", db2);
@@ -48455,12 +48735,12 @@ var init_session2 = __esm(() => {
48455
48735
  init_utils();
48456
48736
  init_dist5();
48457
48737
  PglitePreparedQuery = class PglitePreparedQuery extends PgPreparedQuery {
48458
- constructor(client, queryString, params, logger38, fields, name3, _isResponseInArrayMode, customResultMapper) {
48738
+ constructor(client, queryString, params, logger39, fields, name3, _isResponseInArrayMode, customResultMapper) {
48459
48739
  super({ sql: queryString, params });
48460
48740
  this.client = client;
48461
48741
  this.queryString = queryString;
48462
48742
  this.params = params;
48463
- this.logger = logger38;
48743
+ this.logger = logger39;
48464
48744
  this.fields = fields;
48465
48745
  this._isResponseInArrayMode = _isResponseInArrayMode;
48466
48746
  this.customResultMapper = customResultMapper;
@@ -48564,11 +48844,11 @@ var init_session2 = __esm(() => {
48564
48844
  // ../../node_modules/.bun/drizzle-orm@0.42.0+f8aef3f54a4d48e2/node_modules/drizzle-orm/pglite/driver.js
48565
48845
  function construct(client, config2 = {}) {
48566
48846
  const dialect2 = new PgDialect({ casing: config2.casing });
48567
- let logger38;
48847
+ let logger39;
48568
48848
  if (config2.logger === true) {
48569
- logger38 = new DefaultLogger;
48849
+ logger39 = new DefaultLogger;
48570
48850
  } else if (config2.logger !== false) {
48571
- logger38 = config2.logger;
48851
+ logger39 = config2.logger;
48572
48852
  }
48573
48853
  let schema2;
48574
48854
  if (config2.schema) {
@@ -48579,7 +48859,7 @@ function construct(client, config2 = {}) {
48579
48859
  tableNamesMap: tablesConfig.tableNamesMap
48580
48860
  };
48581
48861
  }
48582
- const driver = new PgliteDriver(client, dialect2, { logger: logger38 });
48862
+ const driver = new PgliteDriver(client, dialect2, { logger: logger39 });
48583
48863
  const session2 = driver.createSession(schema2);
48584
48864
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
48585
48865
  db2.$client = client;
@@ -94793,8 +95073,8 @@ var init_currencies = __esm(() => {
94793
95073
  });
94794
95074
 
94795
95075
  // src/lib/logging/adapter.ts
94796
- function setLogger(logger38) {
94797
- customLogger = logger38;
95076
+ function setLogger(logger39) {
95077
+ customLogger = logger39;
94798
95078
  }
94799
95079
  function getLogger() {
94800
95080
  if (customLogger) {
@@ -94806,10 +95086,10 @@ function getLogger() {
94806
95086
  error: (msg) => console.error(msg)
94807
95087
  };
94808
95088
  }
94809
- var customLogger, logger38;
95089
+ var customLogger, logger39;
94810
95090
  var init_adapter = __esm(() => {
94811
95091
  init_config();
94812
- logger38 = {
95092
+ logger39 = {
94813
95093
  info: (msg) => {
94814
95094
  if (customLogger || !config.embedded) {
94815
95095
  getLogger().info(msg);
@@ -94899,7 +95179,7 @@ async function seedCoreGames(db2) {
94899
95179
  role: "owner"
94900
95180
  }).onConflictDoNothing();
94901
95181
  } catch (error2) {
94902
- logger38.error(`Error seeding core game '${gameData.slug}': ${error2}`);
95182
+ logger39.error(`Error seeding core game '${gameData.slug}': ${error2}`);
94903
95183
  }
94904
95184
  }
94905
95185
  }
@@ -94949,7 +95229,7 @@ async function seedCurrentProjectGame(db2, project) {
94949
95229
  }
94950
95230
  return newGame;
94951
95231
  } catch (error2) {
94952
- logger38.error(`❌ Error seeding project game: ${error2}`);
95232
+ logger39.error(`❌ Error seeding project game: ${error2}`);
94953
95233
  throw error2;
94954
95234
  }
94955
95235
  }
@@ -95742,7 +96022,7 @@ async function provisionLtiUser(db2, claims) {
95742
96022
  where: eq(users.id, existingAccount.userId)
95743
96023
  });
95744
96024
  if (user) {
95745
- logger39.info("Found user by LTI account", {
96025
+ logger40.info("Found user by LTI account", {
95746
96026
  userId: user.id,
95747
96027
  ltiTimebackId
95748
96028
  });
@@ -95771,13 +96051,13 @@ async function provisionLtiUser(db2, claims) {
95771
96051
  updatedAt: new Date
95772
96052
  }).returning({ id: accounts.id });
95773
96053
  if (!account) {
95774
- logger39.error("LTI account link insert returned no rows", {
96054
+ logger40.error("LTI account link insert returned no rows", {
95775
96055
  userId: existingUser.id,
95776
96056
  ltiTimebackId
95777
96057
  });
95778
96058
  throw new InternalError("Failed to link LTI account");
95779
96059
  }
95780
- logger39.info("Linked LTI account to existing user", {
96060
+ logger40.info("Linked LTI account to existing user", {
95781
96061
  userId: existingUser.id,
95782
96062
  ltiTimebackId
95783
96063
  });
@@ -95797,7 +96077,7 @@ async function provisionLtiUser(db2, claims) {
95797
96077
  updatedAt: new Date
95798
96078
  }).returning();
95799
96079
  if (!insertedUser) {
95800
- logger39.error("LTI user insert returned no rows", { email, ltiTimebackId });
96080
+ logger40.error("LTI user insert returned no rows", { email, ltiTimebackId });
95801
96081
  throw new InternalError("Failed to create user");
95802
96082
  }
95803
96083
  await tx.insert(accounts).values({
@@ -95812,7 +96092,7 @@ async function provisionLtiUser(db2, claims) {
95812
96092
  createdAt: new Date,
95813
96093
  updatedAt: new Date
95814
96094
  });
95815
- logger39.info("Provisioned new user from LTI", {
96095
+ logger40.info("Provisioned new user from LTI", {
95816
96096
  userId: insertedUser.id,
95817
96097
  ltiTimebackId
95818
96098
  });
@@ -95820,7 +96100,7 @@ async function provisionLtiUser(db2, claims) {
95820
96100
  });
95821
96101
  return createdUser;
95822
96102
  }
95823
- var logger39;
96103
+ var logger40;
95824
96104
  var init_lti_provisioning = __esm(() => {
95825
96105
  init_drizzle_orm();
95826
96106
  init_src();
@@ -95828,7 +96108,7 @@ var init_lti_provisioning = __esm(() => {
95828
96108
  init_src2();
95829
96109
  init_errors();
95830
96110
  init_lti_util();
95831
- logger39 = log.scope("LtiProvisioning");
96111
+ logger40 = log.scope("LtiProvisioning");
95832
96112
  });
95833
96113
 
95834
96114
  // ../api-core/src/utils/validation.util.ts
@@ -95876,21 +96156,21 @@ var init_utils11 = __esm(() => {
95876
96156
  });
95877
96157
 
95878
96158
  // ../api-core/src/controllers/achievement.controller.ts
95879
- var logger40, listCurrent, listHistory, postProgress, achievements3;
96159
+ var logger41, listCurrent, listHistory, postProgress, achievements3;
95880
96160
  var init_achievement_controller = __esm(() => {
95881
96161
  init_esm();
95882
96162
  init_schemas_index();
95883
96163
  init_src2();
95884
96164
  init_errors();
95885
96165
  init_utils11();
95886
- logger40 = log.scope("AchievementController");
96166
+ logger41 = log.scope("AchievementController");
95887
96167
  listCurrent = requireNonAnonymous(async (ctx) => {
95888
- logger40.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
96168
+ logger41.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
95889
96169
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
95890
96170
  });
95891
96171
  listHistory = requireNonAnonymous(async (ctx) => {
95892
96172
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
95893
- logger40.debug("Listing achievement history", { userId: ctx.user.id, limit });
96173
+ logger41.debug("Listing achievement history", { userId: ctx.user.id, limit });
95894
96174
  return ctx.services.achievement.listHistory(ctx.user, limit);
95895
96175
  });
95896
96176
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -95901,12 +96181,12 @@ var init_achievement_controller = __esm(() => {
95901
96181
  } catch (error2) {
95902
96182
  if (error2 instanceof exports_external.ZodError) {
95903
96183
  const details = formatZodError(error2);
95904
- logger40.warn("Submit achievement progress validation failed", { details });
96184
+ logger41.warn("Submit achievement progress validation failed", { details });
95905
96185
  throw ApiError.unprocessableEntity("Invalid request body", details);
95906
96186
  }
95907
96187
  throw ApiError.badRequest("Invalid JSON body");
95908
96188
  }
95909
- logger40.debug("Submitting progress", {
96189
+ logger41.debug("Submitting progress", {
95910
96190
  userId: ctx.user.id,
95911
96191
  achievementId: body2.achievementId
95912
96192
  });
@@ -95920,14 +96200,14 @@ var init_achievement_controller = __esm(() => {
95920
96200
  });
95921
96201
 
95922
96202
  // ../api-core/src/controllers/admin.controller.ts
95923
- var logger41, getAllowedOrigins;
96203
+ var logger42, getAllowedOrigins;
95924
96204
  var init_admin_controller = __esm(() => {
95925
96205
  init_src2();
95926
96206
  init_utils11();
95927
- logger41 = log.scope("AdminController");
96207
+ logger42 = log.scope("AdminController");
95928
96208
  getAllowedOrigins = requireAdmin(async (ctx) => {
95929
96209
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
95930
- logger41.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
96210
+ logger42.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
95931
96211
  if (shouldRefresh) {
95932
96212
  await ctx.providers.cache.refreshGameOrigins();
95933
96213
  }
@@ -95942,14 +96222,14 @@ var init_admin_controller = __esm(() => {
95942
96222
  });
95943
96223
 
95944
96224
  // ../api-core/src/controllers/bucket.controller.ts
95945
- var logger42, listFiles, getFile, putFile, deleteFile, initiateUpload;
96225
+ var logger43, listFiles, getFile, putFile, deleteFile, initiateUpload;
95946
96226
  var init_bucket_controller = __esm(() => {
95947
96227
  init_esm();
95948
96228
  init_schemas_index();
95949
96229
  init_src2();
95950
96230
  init_errors();
95951
96231
  init_utils11();
95952
- logger42 = log.scope("BucketController");
96232
+ logger43 = log.scope("BucketController");
95953
96233
  listFiles = requireDeveloper(async (ctx) => {
95954
96234
  const slug2 = ctx.params.slug;
95955
96235
  if (!slug2) {
@@ -95957,7 +96237,7 @@ var init_bucket_controller = __esm(() => {
95957
96237
  }
95958
96238
  const url2 = ctx.url;
95959
96239
  const prefix2 = url2.searchParams.get("prefix") || undefined;
95960
- logger42.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96240
+ logger43.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
95961
96241
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
95962
96242
  return { files };
95963
96243
  });
@@ -95967,7 +96247,7 @@ var init_bucket_controller = __esm(() => {
95967
96247
  if (!slug2 || !key) {
95968
96248
  throw ApiError.badRequest("Missing game slug or file key");
95969
96249
  }
95970
- logger42.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
96250
+ logger43.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
95971
96251
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
95972
96252
  return new Response(Buffer.from(object.body), {
95973
96253
  status: 200,
@@ -95986,7 +96266,7 @@ var init_bucket_controller = __esm(() => {
95986
96266
  const arrayBuffer = await ctx.request.arrayBuffer();
95987
96267
  const body2 = new Uint8Array(arrayBuffer);
95988
96268
  const contentType = ctx.request.headers.get("content-type") || undefined;
95989
- logger42.debug("Uploading file", {
96269
+ logger43.debug("Uploading file", {
95990
96270
  userId: ctx.user.id,
95991
96271
  slug: slug2,
95992
96272
  key,
@@ -96002,7 +96282,7 @@ var init_bucket_controller = __esm(() => {
96002
96282
  if (!slug2 || !key) {
96003
96283
  throw ApiError.badRequest("Missing game slug or file key");
96004
96284
  }
96005
- logger42.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
96285
+ logger43.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
96006
96286
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
96007
96287
  return { success: true, key };
96008
96288
  });
@@ -96014,12 +96294,12 @@ var init_bucket_controller = __esm(() => {
96014
96294
  } catch (error2) {
96015
96295
  if (error2 instanceof exports_external.ZodError) {
96016
96296
  const details = formatZodError(error2);
96017
- logger42.warn("Initiate upload validation failed", { details });
96297
+ logger43.warn("Initiate upload validation failed", { details });
96018
96298
  throw ApiError.unprocessableEntity("Validation failed", details);
96019
96299
  }
96020
96300
  throw ApiError.badRequest("Invalid JSON body");
96021
96301
  }
96022
- logger42.debug("Initiating multipart upload", {
96302
+ logger43.debug("Initiating multipart upload", {
96023
96303
  userId: ctx.user.id,
96024
96304
  gameId: body2.gameId,
96025
96305
  fileName: body2.fileName
@@ -96036,19 +96316,19 @@ async function listComponents(ctx) {
96036
96316
  if (!isNaN(parsed) && isFinite(parsed)) {
96037
96317
  level = Math.floor(Math.max(0, parsed));
96038
96318
  }
96039
- logger43.debug("Listing components", { level });
96319
+ logger44.debug("Listing components", { level });
96040
96320
  return ctx.services.character.listAvailableComponents(level);
96041
96321
  }
96042
- var logger43, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96322
+ var logger44, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96043
96323
  var init_character_controller = __esm(() => {
96044
96324
  init_esm();
96045
96325
  init_schemas_index();
96046
96326
  init_src2();
96047
96327
  init_errors();
96048
96328
  init_utils11();
96049
- logger43 = log.scope("CharacterController");
96329
+ logger44 = log.scope("CharacterController");
96050
96330
  get = requireNonAnonymous(async (ctx) => {
96051
- logger43.debug("Getting character", { userId: ctx.user.id });
96331
+ logger44.debug("Getting character", { userId: ctx.user.id });
96052
96332
  return ctx.services.character.getByUser(ctx.user);
96053
96333
  });
96054
96334
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -96056,7 +96336,7 @@ var init_character_controller = __esm(() => {
96056
96336
  if (!userId) {
96057
96337
  throw ApiError.badRequest("User ID is required in the URL path");
96058
96338
  }
96059
- logger43.debug("Getting character by user ID", { requestedUserId: userId });
96339
+ logger44.debug("Getting character by user ID", { requestedUserId: userId });
96060
96340
  return ctx.services.character.getByUserId(userId);
96061
96341
  });
96062
96342
  create = requireNonAnonymous(async (ctx) => {
@@ -96067,12 +96347,12 @@ var init_character_controller = __esm(() => {
96067
96347
  } catch (error2) {
96068
96348
  if (error2 instanceof exports_external.ZodError) {
96069
96349
  const details = formatZodError(error2);
96070
- logger43.warn("Create character validation failed", { details });
96350
+ logger44.warn("Create character validation failed", { details });
96071
96351
  throw ApiError.unprocessableEntity("Invalid request body", details);
96072
96352
  }
96073
96353
  throw ApiError.badRequest("Invalid JSON body");
96074
96354
  }
96075
- logger43.debug("Creating character", {
96355
+ logger44.debug("Creating character", {
96076
96356
  userId: ctx.user.id,
96077
96357
  bodyComponentId: body2.bodyComponentId,
96078
96358
  hairstyleComponentId: body2.hairstyleComponentId
@@ -96087,12 +96367,12 @@ var init_character_controller = __esm(() => {
96087
96367
  } catch (error2) {
96088
96368
  if (error2 instanceof exports_external.ZodError) {
96089
96369
  const details = formatZodError(error2);
96090
- logger43.warn("Update character validation failed", { details });
96370
+ logger44.warn("Update character validation failed", { details });
96091
96371
  throw ApiError.unprocessableEntity("Invalid request body", details);
96092
96372
  }
96093
96373
  throw ApiError.badRequest("Invalid JSON body");
96094
96374
  }
96095
- logger43.debug("Updating character", {
96375
+ logger44.debug("Updating character", {
96096
96376
  userId: ctx.user.id,
96097
96377
  bodyComponentId: body2.bodyComponentId,
96098
96378
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -96108,12 +96388,12 @@ var init_character_controller = __esm(() => {
96108
96388
  } catch (error2) {
96109
96389
  if (error2 instanceof exports_external.ZodError) {
96110
96390
  const details = formatZodError(error2);
96111
- logger43.warn("Equip accessory validation failed", { details });
96391
+ logger44.warn("Equip accessory validation failed", { details });
96112
96392
  throw ApiError.unprocessableEntity("Invalid request body", details);
96113
96393
  }
96114
96394
  throw ApiError.badRequest("Invalid JSON body");
96115
96395
  }
96116
- logger43.debug("Equipping accessory", {
96396
+ logger44.debug("Equipping accessory", {
96117
96397
  userId: ctx.user.id,
96118
96398
  slot: body2.slot,
96119
96399
  accessoryComponentId: body2.accessoryComponentId
@@ -96125,7 +96405,7 @@ var init_character_controller = __esm(() => {
96125
96405
  if (!slot) {
96126
96406
  throw ApiError.badRequest("Slot is required in the URL path");
96127
96407
  }
96128
- logger43.debug("Removing accessory", { userId: ctx.user.id, slot });
96408
+ logger44.debug("Removing accessory", { userId: ctx.user.id, slot });
96129
96409
  await ctx.services.character.removeAccessory(slot, ctx.user);
96130
96410
  return { success: true };
96131
96411
  });
@@ -96141,7 +96421,7 @@ var init_character_controller = __esm(() => {
96141
96421
  });
96142
96422
 
96143
96423
  // ../api-core/src/controllers/currency.controller.ts
96144
- var logger44, list, getById, create2, update3, remove, currencyController;
96424
+ var logger45, list, getById, create2, update3, remove, currencyController;
96145
96425
  var init_currency_controller = __esm(() => {
96146
96426
  init_esm();
96147
96427
  init_schemas_index();
@@ -96149,9 +96429,9 @@ var init_currency_controller = __esm(() => {
96149
96429
  init_src4();
96150
96430
  init_errors();
96151
96431
  init_utils11();
96152
- logger44 = log.scope("CurrencyController");
96432
+ logger45 = log.scope("CurrencyController");
96153
96433
  list = requireNonAnonymous(async (ctx) => {
96154
- logger44.debug("Listing currencies", { userId: ctx.user.id });
96434
+ logger45.debug("Listing currencies", { userId: ctx.user.id });
96155
96435
  return ctx.services.currency.list();
96156
96436
  });
96157
96437
  getById = requireNonAnonymous(async (ctx) => {
@@ -96162,7 +96442,7 @@ var init_currency_controller = __esm(() => {
96162
96442
  if (!isValidUUID(currencyId)) {
96163
96443
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96164
96444
  }
96165
- logger44.debug("Getting currency", { userId: ctx.user.id, currencyId });
96445
+ logger45.debug("Getting currency", { userId: ctx.user.id, currencyId });
96166
96446
  return ctx.services.currency.getById(currencyId);
96167
96447
  });
96168
96448
  create2 = requireAdmin(async (ctx) => {
@@ -96173,12 +96453,12 @@ var init_currency_controller = __esm(() => {
96173
96453
  } catch (error2) {
96174
96454
  if (error2 instanceof exports_external.ZodError) {
96175
96455
  const details = formatZodError(error2);
96176
- logger44.warn("Create currency validation failed", { details });
96456
+ logger45.warn("Create currency validation failed", { details });
96177
96457
  throw ApiError.unprocessableEntity("Validation failed", details);
96178
96458
  }
96179
96459
  throw ApiError.badRequest("Invalid JSON body");
96180
96460
  }
96181
- logger44.debug("Creating currency", {
96461
+ logger45.debug("Creating currency", {
96182
96462
  userId: ctx.user.id,
96183
96463
  symbol: body2.symbol,
96184
96464
  itemId: body2.itemId,
@@ -96201,12 +96481,12 @@ var init_currency_controller = __esm(() => {
96201
96481
  } catch (error2) {
96202
96482
  if (error2 instanceof exports_external.ZodError) {
96203
96483
  const details = formatZodError(error2);
96204
- logger44.warn("Update currency validation failed", { details });
96484
+ logger45.warn("Update currency validation failed", { details });
96205
96485
  throw ApiError.unprocessableEntity("Validation failed", details);
96206
96486
  }
96207
96487
  throw ApiError.badRequest("Invalid JSON body");
96208
96488
  }
96209
- logger44.debug("Updating currency", {
96489
+ logger45.debug("Updating currency", {
96210
96490
  userId: ctx.user.id,
96211
96491
  currencyId,
96212
96492
  symbol: body2.symbol,
@@ -96223,7 +96503,7 @@ var init_currency_controller = __esm(() => {
96223
96503
  if (!isValidUUID(currencyId)) {
96224
96504
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96225
96505
  }
96226
- logger44.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96506
+ logger45.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96227
96507
  await ctx.services.currency.delete(currencyId);
96228
96508
  });
96229
96509
  currencyController = {
@@ -96236,14 +96516,14 @@ var init_currency_controller = __esm(() => {
96236
96516
  });
96237
96517
 
96238
96518
  // ../api-core/src/controllers/database.controller.ts
96239
- var logger45, reset;
96519
+ var logger46, reset;
96240
96520
  var init_database_controller = __esm(() => {
96241
96521
  init_esm();
96242
96522
  init_schemas_index();
96243
96523
  init_src2();
96244
96524
  init_errors();
96245
96525
  init_utils11();
96246
- logger45 = log.scope("DatabaseController");
96526
+ logger46 = log.scope("DatabaseController");
96247
96527
  reset = requireDeveloper(async (ctx) => {
96248
96528
  const slug2 = ctx.params.slug;
96249
96529
  if (!slug2) {
@@ -96256,11 +96536,11 @@ var init_database_controller = __esm(() => {
96256
96536
  } catch (error2) {
96257
96537
  if (error2 instanceof exports_external.ZodError) {
96258
96538
  const details = formatZodError(error2);
96259
- logger45.warn("Database reset validation failed", { details });
96539
+ logger46.warn("Database reset validation failed", { details });
96260
96540
  throw ApiError.unprocessableEntity("Validation failed", details);
96261
96541
  }
96262
96542
  }
96263
- logger45.debug("Resetting database", {
96543
+ logger46.debug("Resetting database", {
96264
96544
  userId: ctx.user.id,
96265
96545
  slug: slug2,
96266
96546
  hasSchema: Boolean(body2.schema)
@@ -96278,7 +96558,7 @@ async function createJob(ctx) {
96278
96558
  let body2;
96279
96559
  try {
96280
96560
  const json4 = await ctx.request.json();
96281
- logger46.debug("Deploy request body", {
96561
+ logger47.debug("Deploy request body", {
96282
96562
  keys: Object.keys(json4 || {}),
96283
96563
  hasUploadToken: Boolean(json4?.uploadToken),
96284
96564
  hasCode: Boolean(json4?.code),
@@ -96288,7 +96568,7 @@ async function createJob(ctx) {
96288
96568
  } catch (error2) {
96289
96569
  if (error2 instanceof exports_external.ZodError) {
96290
96570
  const details = formatZodError(error2);
96291
- logger46.warn("Deploy validation failed", { details });
96571
+ logger47.warn("Deploy validation failed", { details });
96292
96572
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
96293
96573
  }
96294
96574
  throw ApiError.badRequest("Invalid JSON body");
@@ -96309,14 +96589,14 @@ async function getJob(ctx) {
96309
96589
  }
96310
96590
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
96311
96591
  }
96312
- var logger46, deploy;
96592
+ var logger47, deploy;
96313
96593
  var init_deploy_controller = __esm(() => {
96314
96594
  init_esm();
96315
96595
  init_schemas_index();
96316
96596
  init_src2();
96317
96597
  init_errors();
96318
96598
  init_utils11();
96319
- logger46 = log.scope("DeployController");
96599
+ logger47 = log.scope("DeployController");
96320
96600
  deploy = {
96321
96601
  createJob: requireDeveloper(createJob),
96322
96602
  getJob: requireDeveloper(getJob)
@@ -96324,17 +96604,17 @@ var init_deploy_controller = __esm(() => {
96324
96604
  });
96325
96605
 
96326
96606
  // ../api-core/src/controllers/developer.controller.ts
96327
- var logger47, apply, getStatus, developer;
96607
+ var logger48, apply, getStatus, developer;
96328
96608
  var init_developer_controller = __esm(() => {
96329
96609
  init_src2();
96330
96610
  init_utils11();
96331
- logger47 = log.scope("DeveloperController");
96611
+ logger48 = log.scope("DeveloperController");
96332
96612
  apply = requireNonAnonymous(async (ctx) => {
96333
- logger47.debug("Applying for developer status", { userId: ctx.user.id });
96613
+ logger48.debug("Applying for developer status", { userId: ctx.user.id });
96334
96614
  await ctx.services.developer.apply(ctx.user);
96335
96615
  });
96336
96616
  getStatus = requireNonAnonymous(async (ctx) => {
96337
- logger47.debug("Getting developer status", { userId: ctx.user.id });
96617
+ logger48.debug("Getting developer status", { userId: ctx.user.id });
96338
96618
  const status = await ctx.services.developer.getStatus(ctx.user.id);
96339
96619
  return { status };
96340
96620
  });
@@ -96345,7 +96625,7 @@ var init_developer_controller = __esm(() => {
96345
96625
  });
96346
96626
 
96347
96627
  // ../api-core/src/controllers/domain.controller.ts
96348
- var logger48, add, list2, getStatus2, remove2, domains2;
96628
+ var logger49, add, list2, getStatus2, remove2, domains2;
96349
96629
  var init_domain_controller = __esm(() => {
96350
96630
  init_esm();
96351
96631
  init_schemas_index();
@@ -96353,7 +96633,7 @@ var init_domain_controller = __esm(() => {
96353
96633
  init_config2();
96354
96634
  init_errors();
96355
96635
  init_utils11();
96356
- logger48 = log.scope("DomainController");
96636
+ logger49 = log.scope("DomainController");
96357
96637
  add = requireDeveloper(async (ctx) => {
96358
96638
  const slug2 = ctx.params.slug;
96359
96639
  if (!slug2) {
@@ -96366,13 +96646,13 @@ var init_domain_controller = __esm(() => {
96366
96646
  } catch (error2) {
96367
96647
  if (error2 instanceof exports_external.ZodError) {
96368
96648
  const details = formatZodError(error2);
96369
- logger48.warn("Add domain validation failed", { details });
96649
+ logger49.warn("Add domain validation failed", { details });
96370
96650
  throw ApiError.unprocessableEntity("Validation failed", details);
96371
96651
  }
96372
96652
  throw ApiError.badRequest("Invalid JSON body");
96373
96653
  }
96374
96654
  const environment = getPlatformEnvironment(ctx.config);
96375
- logger48.debug("Adding domain", {
96655
+ logger49.debug("Adding domain", {
96376
96656
  userId: ctx.user.id,
96377
96657
  slug: slug2,
96378
96658
  hostname: body2.hostname,
@@ -96386,7 +96666,7 @@ var init_domain_controller = __esm(() => {
96386
96666
  throw ApiError.badRequest("Missing game slug");
96387
96667
  }
96388
96668
  const environment = getPlatformEnvironment(ctx.config);
96389
- logger48.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96669
+ logger49.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96390
96670
  const domains2 = await ctx.services.domain.list(slug2, environment, ctx.user);
96391
96671
  return { domains: domains2 };
96392
96672
  });
@@ -96401,7 +96681,7 @@ var init_domain_controller = __esm(() => {
96401
96681
  }
96402
96682
  const refresh = ctx.url.searchParams.get("refresh") === "true";
96403
96683
  const environment = getPlatformEnvironment(ctx.config);
96404
- logger48.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96684
+ logger49.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96405
96685
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
96406
96686
  });
96407
96687
  remove2 = requireDeveloper(async (ctx) => {
@@ -96414,7 +96694,7 @@ var init_domain_controller = __esm(() => {
96414
96694
  throw ApiError.badRequest("Missing hostname");
96415
96695
  }
96416
96696
  const environment = getPlatformEnvironment(ctx.config);
96417
- logger48.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96697
+ logger49.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96418
96698
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
96419
96699
  });
96420
96700
  domains2 = {
@@ -96426,7 +96706,7 @@ var init_domain_controller = __esm(() => {
96426
96706
  });
96427
96707
 
96428
96708
  // ../api-core/src/controllers/game.controller.ts
96429
- var logger49, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96709
+ var logger50, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96430
96710
  var init_game_controller = __esm(() => {
96431
96711
  init_esm();
96432
96712
  init_schemas_index();
@@ -96434,17 +96714,17 @@ var init_game_controller = __esm(() => {
96434
96714
  init_src4();
96435
96715
  init_errors();
96436
96716
  init_utils11();
96437
- logger49 = log.scope("GameController");
96717
+ logger50 = log.scope("GameController");
96438
96718
  list3 = requireNonAnonymous(async (ctx) => {
96439
- logger49.debug("Listing games", { userId: ctx.user.id });
96719
+ logger50.debug("Listing games", { userId: ctx.user.id });
96440
96720
  return ctx.services.game.list(ctx.user);
96441
96721
  });
96442
96722
  listAccessible = requireNonAnonymous(async (ctx) => {
96443
- logger49.debug("Listing accessible games", { userId: ctx.user.id });
96723
+ logger50.debug("Listing accessible games", { userId: ctx.user.id });
96444
96724
  return ctx.services.game.listAccessible(ctx.user);
96445
96725
  });
96446
96726
  getSubjects = requireNonAnonymous(async (ctx) => {
96447
- logger49.debug("Getting game subjects", { userId: ctx.user.id });
96727
+ logger50.debug("Getting game subjects", { userId: ctx.user.id });
96448
96728
  return ctx.services.game.getSubjects();
96449
96729
  });
96450
96730
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -96455,7 +96735,7 @@ var init_game_controller = __esm(() => {
96455
96735
  if (!isValidUUID(gameId)) {
96456
96736
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96457
96737
  }
96458
- logger49.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96738
+ logger50.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96459
96739
  return ctx.services.game.getById(gameId, ctx.user);
96460
96740
  });
96461
96741
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -96463,7 +96743,7 @@ var init_game_controller = __esm(() => {
96463
96743
  if (!slug2) {
96464
96744
  throw ApiError.badRequest("Missing game slug");
96465
96745
  }
96466
- logger49.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96746
+ logger50.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96467
96747
  return ctx.services.game.getBySlug(slug2, ctx.user);
96468
96748
  });
96469
96749
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -96474,7 +96754,7 @@ var init_game_controller = __esm(() => {
96474
96754
  if (!isValidUUID(gameId)) {
96475
96755
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96476
96756
  }
96477
- logger49.debug("Getting game manifest by ID", {
96757
+ logger50.debug("Getting game manifest by ID", {
96478
96758
  userId: ctx.user.id,
96479
96759
  gameId,
96480
96760
  launchId: ctx.launchId
@@ -96493,12 +96773,12 @@ var init_game_controller = __esm(() => {
96493
96773
  } catch (error2) {
96494
96774
  if (error2 instanceof exports_external.ZodError) {
96495
96775
  const details = formatZodError(error2);
96496
- logger49.warn("Upsert game validation failed", { details });
96776
+ logger50.warn("Upsert game validation failed", { details });
96497
96777
  throw ApiError.unprocessableEntity("Validation failed", details);
96498
96778
  }
96499
96779
  throw ApiError.badRequest("Invalid JSON body");
96500
96780
  }
96501
- logger49.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96781
+ logger50.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96502
96782
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
96503
96783
  });
96504
96784
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -96509,7 +96789,7 @@ var init_game_controller = __esm(() => {
96509
96789
  if (!isValidUUID(gameId)) {
96510
96790
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96511
96791
  }
96512
- logger49.debug("Deleting game", { userId: ctx.user.id, gameId });
96792
+ logger50.debug("Deleting game", { userId: ctx.user.id, gameId });
96513
96793
  await ctx.services.game.delete(gameId, ctx.user);
96514
96794
  });
96515
96795
  games2 = {
@@ -96525,16 +96805,16 @@ var init_game_controller = __esm(() => {
96525
96805
  });
96526
96806
 
96527
96807
  // ../api-core/src/controllers/inventory.controller.ts
96528
- var logger50, list4, addItem, removeItem, inventory;
96808
+ var logger51, list4, addItem, removeItem, inventory;
96529
96809
  var init_inventory_controller = __esm(() => {
96530
96810
  init_esm();
96531
96811
  init_schemas_index();
96532
96812
  init_src2();
96533
96813
  init_errors();
96534
96814
  init_utils11();
96535
- logger50 = log.scope("InventoryController");
96815
+ logger51 = log.scope("InventoryController");
96536
96816
  list4 = requireNonAnonymous(async (ctx) => {
96537
- logger50.debug("Listing inventory", { userId: ctx.user.id });
96817
+ logger51.debug("Listing inventory", { userId: ctx.user.id });
96538
96818
  return ctx.services.inventory.list(ctx.user);
96539
96819
  });
96540
96820
  addItem = requireNonAnonymous(async (ctx) => {
@@ -96545,12 +96825,12 @@ var init_inventory_controller = __esm(() => {
96545
96825
  } catch (error2) {
96546
96826
  if (error2 instanceof exports_external.ZodError) {
96547
96827
  const details = formatZodError(error2);
96548
- logger50.warn("Add inventory item validation failed", { details });
96828
+ logger51.warn("Add inventory item validation failed", { details });
96549
96829
  throw ApiError.unprocessableEntity("Invalid request body", details);
96550
96830
  }
96551
96831
  throw ApiError.badRequest("Invalid JSON body");
96552
96832
  }
96553
- logger50.debug("Adding item", {
96833
+ logger51.debug("Adding item", {
96554
96834
  userId: ctx.user.id,
96555
96835
  itemId: body2.itemId,
96556
96836
  qty: body2.qty
@@ -96565,12 +96845,12 @@ var init_inventory_controller = __esm(() => {
96565
96845
  } catch (error2) {
96566
96846
  if (error2 instanceof exports_external.ZodError) {
96567
96847
  const details = formatZodError(error2);
96568
- logger50.warn("Remove inventory item validation failed", { details });
96848
+ logger51.warn("Remove inventory item validation failed", { details });
96569
96849
  throw ApiError.unprocessableEntity("Invalid request body", details);
96570
96850
  }
96571
96851
  throw ApiError.badRequest("Invalid JSON body");
96572
96852
  }
96573
- logger50.debug("Removing item", {
96853
+ logger51.debug("Removing item", {
96574
96854
  userId: ctx.user.id,
96575
96855
  itemId: body2.itemId,
96576
96856
  qty: body2.qty
@@ -96585,7 +96865,7 @@ var init_inventory_controller = __esm(() => {
96585
96865
  });
96586
96866
 
96587
96867
  // ../api-core/src/controllers/item.controller.ts
96588
- var logger51, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96868
+ var logger52, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96589
96869
  var init_item_controller = __esm(() => {
96590
96870
  init_esm();
96591
96871
  init_schemas_index();
@@ -96593,10 +96873,10 @@ var init_item_controller = __esm(() => {
96593
96873
  init_src4();
96594
96874
  init_errors();
96595
96875
  init_utils11();
96596
- logger51 = log.scope("ItemController");
96876
+ logger52 = log.scope("ItemController");
96597
96877
  list5 = requireNonAnonymous(async (ctx) => {
96598
96878
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
96599
- logger51.debug("Listing items", { userId: ctx.user.id, gameId });
96879
+ logger52.debug("Listing items", { userId: ctx.user.id, gameId });
96600
96880
  return ctx.services.item.list(gameId);
96601
96881
  });
96602
96882
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -96607,7 +96887,7 @@ var init_item_controller = __esm(() => {
96607
96887
  if (!isValidUUID(itemId)) {
96608
96888
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96609
96889
  }
96610
- logger51.debug("Getting item", { userId: ctx.user.id, itemId });
96890
+ logger52.debug("Getting item", { userId: ctx.user.id, itemId });
96611
96891
  return ctx.services.item.getById(itemId);
96612
96892
  });
96613
96893
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -96619,7 +96899,7 @@ var init_item_controller = __esm(() => {
96619
96899
  if (gameId && !isValidUUID(gameId)) {
96620
96900
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96621
96901
  }
96622
- logger51.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96902
+ logger52.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96623
96903
  return ctx.services.item.resolveBySlug(slug2, gameId);
96624
96904
  });
96625
96905
  create3 = requireRole(["admin"], async (ctx) => {
@@ -96630,12 +96910,12 @@ var init_item_controller = __esm(() => {
96630
96910
  } catch (error2) {
96631
96911
  if (error2 instanceof exports_external.ZodError) {
96632
96912
  const details = formatZodError(error2);
96633
- logger51.warn("Create item validation failed", { details });
96913
+ logger52.warn("Create item validation failed", { details });
96634
96914
  throw ApiError.unprocessableEntity("Validation failed", details);
96635
96915
  }
96636
96916
  throw ApiError.badRequest("Invalid JSON body");
96637
96917
  }
96638
- logger51.debug("Creating item", {
96918
+ logger52.debug("Creating item", {
96639
96919
  userId: ctx.user.id,
96640
96920
  slug: body2.slug,
96641
96921
  displayName: body2.displayName
@@ -96657,7 +96937,7 @@ var init_item_controller = __esm(() => {
96657
96937
  } catch (error2) {
96658
96938
  if (error2 instanceof exports_external.ZodError) {
96659
96939
  const details = formatZodError(error2);
96660
- logger51.warn("Update item validation failed", { details });
96940
+ logger52.warn("Update item validation failed", { details });
96661
96941
  throw ApiError.unprocessableEntity("Validation failed", details);
96662
96942
  }
96663
96943
  throw ApiError.badRequest("Invalid JSON body");
@@ -96665,7 +96945,7 @@ var init_item_controller = __esm(() => {
96665
96945
  if (Object.keys(body2).length === 0) {
96666
96946
  throw ApiError.badRequest("No update data provided");
96667
96947
  }
96668
- logger51.debug("Updating item", {
96948
+ logger52.debug("Updating item", {
96669
96949
  userId: ctx.user.id,
96670
96950
  itemId,
96671
96951
  slug: body2.slug,
@@ -96682,7 +96962,7 @@ var init_item_controller = __esm(() => {
96682
96962
  if (!isValidUUID(itemId)) {
96683
96963
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96684
96964
  }
96685
- logger51.debug("Deleting item", { userId: ctx.user.id, itemId });
96965
+ logger52.debug("Deleting item", { userId: ctx.user.id, itemId });
96686
96966
  await ctx.services.item.delete(itemId);
96687
96967
  });
96688
96968
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -96693,7 +96973,7 @@ var init_item_controller = __esm(() => {
96693
96973
  if (!isValidUUID(gameId)) {
96694
96974
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96695
96975
  }
96696
- logger51.debug("Listing game items", { userId: ctx.user.id, gameId });
96976
+ logger52.debug("Listing game items", { userId: ctx.user.id, gameId });
96697
96977
  return ctx.services.item.listByGame(gameId);
96698
96978
  });
96699
96979
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -96711,12 +96991,12 @@ var init_item_controller = __esm(() => {
96711
96991
  } catch (error2) {
96712
96992
  if (error2 instanceof exports_external.ZodError) {
96713
96993
  const details = formatZodError(error2);
96714
- logger51.warn("Create game item validation failed", { details });
96994
+ logger52.warn("Create game item validation failed", { details });
96715
96995
  throw ApiError.unprocessableEntity("Validation failed", details);
96716
96996
  }
96717
96997
  throw ApiError.badRequest("Invalid JSON body");
96718
96998
  }
96719
- logger51.debug("Creating game item", {
96999
+ logger52.debug("Creating game item", {
96720
97000
  userId: ctx.user.id,
96721
97001
  gameId,
96722
97002
  slug: body2.slug,
@@ -96743,7 +97023,7 @@ var init_item_controller = __esm(() => {
96743
97023
  } catch (error2) {
96744
97024
  if (error2 instanceof exports_external.ZodError) {
96745
97025
  const details = formatZodError(error2);
96746
- logger51.warn("Update game item validation failed", { details });
97026
+ logger52.warn("Update game item validation failed", { details });
96747
97027
  throw ApiError.unprocessableEntity("Validation failed", details);
96748
97028
  }
96749
97029
  throw ApiError.badRequest("Invalid JSON body");
@@ -96751,7 +97031,7 @@ var init_item_controller = __esm(() => {
96751
97031
  if (Object.keys(body2).length === 0) {
96752
97032
  throw ApiError.badRequest("No update data provided");
96753
97033
  }
96754
- logger51.debug("Updating game item", {
97034
+ logger52.debug("Updating game item", {
96755
97035
  userId: ctx.user.id,
96756
97036
  gameId,
96757
97037
  itemId,
@@ -96773,7 +97053,7 @@ var init_item_controller = __esm(() => {
96773
97053
  if (!isValidUUID(itemId)) {
96774
97054
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96775
97055
  }
96776
- logger51.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
97056
+ logger52.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
96777
97057
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
96778
97058
  });
96779
97059
  items2 = {
@@ -96791,14 +97071,14 @@ var init_item_controller = __esm(() => {
96791
97071
  });
96792
97072
 
96793
97073
  // ../api-core/src/controllers/kv.controller.ts
96794
- var logger52, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
97074
+ var logger53, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
96795
97075
  var init_kv_controller = __esm(() => {
96796
97076
  init_esm();
96797
97077
  init_schemas_index();
96798
97078
  init_src2();
96799
97079
  init_errors();
96800
97080
  init_utils11();
96801
- logger52 = log.scope("KVController");
97081
+ logger53 = log.scope("KVController");
96802
97082
  listKeys = requireDeveloper(async (ctx) => {
96803
97083
  const slug2 = ctx.params.slug;
96804
97084
  if (!slug2) {
@@ -96806,7 +97086,7 @@ var init_kv_controller = __esm(() => {
96806
97086
  }
96807
97087
  const url2 = ctx.url;
96808
97088
  const prefix2 = url2.searchParams.get("prefix") || undefined;
96809
- logger52.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
97089
+ logger53.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96810
97090
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
96811
97091
  return { keys };
96812
97092
  });
@@ -96815,7 +97095,7 @@ var init_kv_controller = __esm(() => {
96815
97095
  if (!slug2) {
96816
97096
  throw ApiError.badRequest("Missing game slug");
96817
97097
  }
96818
- logger52.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
97098
+ logger53.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
96819
97099
  return ctx.services.kv.getStats(slug2, ctx.user);
96820
97100
  });
96821
97101
  seed = requireDeveloper(async (ctx) => {
@@ -96830,12 +97110,12 @@ var init_kv_controller = __esm(() => {
96830
97110
  } catch (error2) {
96831
97111
  if (error2 instanceof exports_external.ZodError) {
96832
97112
  const details = formatZodError(error2);
96833
- logger52.warn("Seed validation failed", { details });
97113
+ logger53.warn("Seed validation failed", { details });
96834
97114
  throw ApiError.unprocessableEntity("Validation failed", details);
96835
97115
  }
96836
97116
  throw ApiError.badRequest("Invalid JSON body");
96837
97117
  }
96838
- logger52.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
97118
+ logger53.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
96839
97119
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
96840
97120
  return { success: true, count: body2.entries.length };
96841
97121
  });
@@ -96845,7 +97125,7 @@ var init_kv_controller = __esm(() => {
96845
97125
  if (!slug2 || !key) {
96846
97126
  throw ApiError.badRequest("Missing game slug or key");
96847
97127
  }
96848
- logger52.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
97128
+ logger53.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
96849
97129
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
96850
97130
  return { key, value };
96851
97131
  });
@@ -96859,7 +97139,7 @@ var init_kv_controller = __esm(() => {
96859
97139
  if (!value) {
96860
97140
  throw ApiError.badRequest("Missing value in request body");
96861
97141
  }
96862
- logger52.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
97142
+ logger53.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
96863
97143
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
96864
97144
  return { success: true, key };
96865
97145
  });
@@ -96869,7 +97149,7 @@ var init_kv_controller = __esm(() => {
96869
97149
  if (!slug2 || !key) {
96870
97150
  throw ApiError.badRequest("Missing game slug or key");
96871
97151
  }
96872
- logger52.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
97152
+ logger53.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
96873
97153
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
96874
97154
  return { success: true, key };
96875
97155
  });
@@ -96879,7 +97159,7 @@ var init_kv_controller = __esm(() => {
96879
97159
  if (!slug2 || !key) {
96880
97160
  throw ApiError.badRequest("Missing game slug or key");
96881
97161
  }
96882
- logger52.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
97162
+ logger53.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
96883
97163
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
96884
97164
  return { key, metadata: metadata2 };
96885
97165
  });
@@ -96888,21 +97168,21 @@ var init_kv_controller = __esm(() => {
96888
97168
  if (!slug2) {
96889
97169
  throw ApiError.badRequest("Missing game slug");
96890
97170
  }
96891
- logger52.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
97171
+ logger53.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
96892
97172
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
96893
97173
  return { success: true, deleted };
96894
97174
  });
96895
97175
  });
96896
97176
 
96897
97177
  // ../api-core/src/controllers/leaderboard.controller.ts
96898
- var logger53, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
97178
+ var logger54, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
96899
97179
  var init_leaderboard_controller = __esm(() => {
96900
97180
  init_esm();
96901
97181
  init_schemas_index();
96902
97182
  init_src2();
96903
97183
  init_errors();
96904
97184
  init_utils11();
96905
- logger53 = log.scope("LeaderboardController");
97185
+ logger54 = log.scope("LeaderboardController");
96906
97186
  submitScore = requireAuth(async (ctx) => {
96907
97187
  const gameId = ctx.params.gameId;
96908
97188
  if (!gameId) {
@@ -96915,12 +97195,12 @@ var init_leaderboard_controller = __esm(() => {
96915
97195
  } catch (error2) {
96916
97196
  if (error2 instanceof exports_external.ZodError) {
96917
97197
  const details = formatZodError(error2);
96918
- logger53.warn("Submit score validation failed", { details });
97198
+ logger54.warn("Submit score validation failed", { details });
96919
97199
  throw ApiError.unprocessableEntity("Validation failed", details);
96920
97200
  }
96921
97201
  throw ApiError.badRequest("Invalid JSON body");
96922
97202
  }
96923
- logger53.debug("Submitting score", {
97203
+ logger54.debug("Submitting score", {
96924
97204
  userId: ctx.user.id,
96925
97205
  gameId,
96926
97206
  score: body2.score
@@ -96943,12 +97223,12 @@ var init_leaderboard_controller = __esm(() => {
96943
97223
  } catch (error2) {
96944
97224
  if (error2 instanceof exports_external.ZodError) {
96945
97225
  const details = formatZodError(error2);
96946
- logger53.warn("Get global leaderboard query validation failed", { details });
97226
+ logger54.warn("Get global leaderboard query validation failed", { details });
96947
97227
  throw ApiError.badRequest("Invalid query parameters", details);
96948
97228
  }
96949
97229
  throw ApiError.badRequest("Invalid query parameters");
96950
97230
  }
96951
- logger53.debug("Getting global leaderboard", {
97231
+ logger54.debug("Getting global leaderboard", {
96952
97232
  userId: ctx.user.id,
96953
97233
  gameId,
96954
97234
  ...query
@@ -96971,12 +97251,12 @@ var init_leaderboard_controller = __esm(() => {
96971
97251
  } catch (error2) {
96972
97252
  if (error2 instanceof exports_external.ZodError) {
96973
97253
  const details = formatZodError(error2);
96974
- logger53.warn("Get leaderboard query validation failed", { details });
97254
+ logger54.warn("Get leaderboard query validation failed", { details });
96975
97255
  throw ApiError.badRequest("Invalid query parameters", details);
96976
97256
  }
96977
97257
  throw ApiError.badRequest("Invalid query parameters");
96978
97258
  }
96979
- logger53.debug("Getting leaderboard", {
97259
+ logger54.debug("Getting leaderboard", {
96980
97260
  userId: ctx.user.id,
96981
97261
  gameId,
96982
97262
  ...query
@@ -96988,7 +97268,7 @@ var init_leaderboard_controller = __esm(() => {
96988
97268
  if (!gameId || !userId) {
96989
97269
  throw ApiError.badRequest("Game ID and User ID are required");
96990
97270
  }
96991
- logger53.debug("Getting user rank", {
97271
+ logger54.debug("Getting user rank", {
96992
97272
  requesterId: ctx.user.id,
96993
97273
  gameId,
96994
97274
  targetUserId: userId
@@ -97003,7 +97283,7 @@ var init_leaderboard_controller = __esm(() => {
97003
97283
  const url2 = ctx.url;
97004
97284
  const limit = Math.min(Number(url2.searchParams.get("limit") || "50"), 100);
97005
97285
  const gameId = url2.searchParams.get("gameId") || undefined;
97006
- logger53.debug("Getting user all scores", {
97286
+ logger54.debug("Getting user all scores", {
97007
97287
  requesterId: ctx.user.id,
97008
97288
  targetUserId: userId,
97009
97289
  gameId,
@@ -97018,7 +97298,7 @@ var init_leaderboard_controller = __esm(() => {
97018
97298
  }
97019
97299
  const url2 = ctx.url;
97020
97300
  const limit = Math.min(Number(url2.searchParams.get("limit") || "10"), 100);
97021
- logger53.debug("Getting user scores", {
97301
+ logger54.debug("Getting user scores", {
97022
97302
  requesterId: ctx.user.id,
97023
97303
  gameId,
97024
97304
  targetUserId: userId,
@@ -97038,7 +97318,7 @@ var init_leaderboard_controller = __esm(() => {
97038
97318
 
97039
97319
  // ../api-core/src/controllers/level.controller.ts
97040
97320
  async function listConfigs(ctx) {
97041
- logger54.debug("Listing level configs");
97321
+ logger55.debug("Listing level configs");
97042
97322
  return ctx.services.level.listConfigs();
97043
97323
  }
97044
97324
  async function getConfig(ctx) {
@@ -97050,21 +97330,21 @@ async function getConfig(ctx) {
97050
97330
  if (isNaN(level) || level < 1) {
97051
97331
  throw ApiError.badRequest("Level must be a positive integer");
97052
97332
  }
97053
- logger54.debug("Getting level config", { level });
97333
+ logger55.debug("Getting level config", { level });
97054
97334
  return ctx.services.level.getConfig(level);
97055
97335
  }
97056
- var logger54, getByUser, getProgress, levels;
97336
+ var logger55, getByUser, getProgress, levels;
97057
97337
  var init_level_controller = __esm(() => {
97058
97338
  init_src2();
97059
97339
  init_errors();
97060
97340
  init_utils11();
97061
- logger54 = log.scope("LevelController");
97341
+ logger55 = log.scope("LevelController");
97062
97342
  getByUser = requireNonAnonymous(async (ctx) => {
97063
- logger54.debug("Getting user level", { userId: ctx.user.id });
97343
+ logger55.debug("Getting user level", { userId: ctx.user.id });
97064
97344
  return ctx.services.level.getByUser(ctx.user);
97065
97345
  });
97066
97346
  getProgress = requireNonAnonymous(async (ctx) => {
97067
- logger54.debug("Getting level progress", { userId: ctx.user.id });
97347
+ logger55.debug("Getting level progress", { userId: ctx.user.id });
97068
97348
  return ctx.services.level.getProgress(ctx.user);
97069
97349
  });
97070
97350
  levels = {
@@ -97076,12 +97356,12 @@ var init_level_controller = __esm(() => {
97076
97356
  });
97077
97357
 
97078
97358
  // ../api-core/src/controllers/logs.controller.ts
97079
- var logger55, generateToken, logs;
97359
+ var logger56, generateToken, logs;
97080
97360
  var init_logs_controller = __esm(() => {
97081
97361
  init_src2();
97082
97362
  init_errors();
97083
97363
  init_utils11();
97084
- logger55 = log.scope("LogsController");
97364
+ logger56 = log.scope("LogsController");
97085
97365
  generateToken = requireDeveloper(async (ctx) => {
97086
97366
  const slug2 = ctx.params.slug;
97087
97367
  if (!slug2) {
@@ -97100,7 +97380,7 @@ var init_logs_controller = __esm(() => {
97100
97380
  }
97101
97381
  throw ApiError.badRequest("Invalid JSON body");
97102
97382
  }
97103
- logger55.debug("Generating log stream token", {
97383
+ logger56.debug("Generating log stream token", {
97104
97384
  userId: ctx.user.id,
97105
97385
  slug: slug2,
97106
97386
  environment: body2.environment
@@ -97119,13 +97399,13 @@ var init_logs_controller = __esm(() => {
97119
97399
  });
97120
97400
 
97121
97401
  // ../api-core/src/controllers/lti.controller.ts
97122
- var logger56, getStatus3, lti;
97402
+ var logger57, getStatus3, lti;
97123
97403
  var init_lti_controller = __esm(() => {
97124
97404
  init_src2();
97125
97405
  init_utils11();
97126
- logger56 = log.scope("LtiController");
97406
+ logger57 = log.scope("LtiController");
97127
97407
  getStatus3 = requireNonAnonymous(async (ctx) => {
97128
- logger56.debug("Getting status", { userId: ctx.user.id });
97408
+ logger57.debug("Getting status", { userId: ctx.user.id });
97129
97409
  return ctx.services.lti.getStatus(ctx.user);
97130
97410
  });
97131
97411
  lti = {
@@ -97134,7 +97414,7 @@ var init_lti_controller = __esm(() => {
97134
97414
  });
97135
97415
 
97136
97416
  // ../api-core/src/controllers/map.controller.ts
97137
- var logger57, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97417
+ var logger58, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97138
97418
  var init_map_controller = __esm(() => {
97139
97419
  init_esm();
97140
97420
  init_schemas_index();
@@ -97142,13 +97422,13 @@ var init_map_controller = __esm(() => {
97142
97422
  init_src4();
97143
97423
  init_errors();
97144
97424
  init_utils11();
97145
- logger57 = log.scope("MapController");
97425
+ logger58 = log.scope("MapController");
97146
97426
  getByIdentifier = requireNonAnonymous(async (ctx) => {
97147
97427
  const identifier = ctx.params.identifier;
97148
97428
  if (!identifier) {
97149
97429
  throw ApiError.badRequest("Missing map identifier");
97150
97430
  }
97151
- logger57.debug("Getting map", { userId: ctx.user.id, identifier });
97431
+ logger58.debug("Getting map", { userId: ctx.user.id, identifier });
97152
97432
  return ctx.services.map.getByIdentifier(identifier);
97153
97433
  });
97154
97434
  getElements = requireNonAnonymous(async (ctx) => {
@@ -97159,7 +97439,7 @@ var init_map_controller = __esm(() => {
97159
97439
  if (!isValidUUID(mapId)) {
97160
97440
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97161
97441
  }
97162
- logger57.debug("Getting map elements", { userId: ctx.user.id, mapId });
97442
+ logger58.debug("Getting map elements", { userId: ctx.user.id, mapId });
97163
97443
  return ctx.services.map.getElements(mapId);
97164
97444
  });
97165
97445
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -97170,7 +97450,7 @@ var init_map_controller = __esm(() => {
97170
97450
  if (!isValidUUID(mapId)) {
97171
97451
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97172
97452
  }
97173
- logger57.debug("Getting map objects", { userId: ctx.user.id, mapId });
97453
+ logger58.debug("Getting map objects", { userId: ctx.user.id, mapId });
97174
97454
  return ctx.services.map.getObjects(mapId, ctx.user.id);
97175
97455
  });
97176
97456
  createObject = requireNonAnonymous(async (ctx) => {
@@ -97192,12 +97472,12 @@ var init_map_controller = __esm(() => {
97192
97472
  } catch (error2) {
97193
97473
  if (error2 instanceof exports_external.ZodError) {
97194
97474
  const details = formatZodError(error2);
97195
- logger57.warn("Create map object validation failed", { details });
97475
+ logger58.warn("Create map object validation failed", { details });
97196
97476
  throw ApiError.unprocessableEntity("Validation failed", details);
97197
97477
  }
97198
97478
  throw ApiError.badRequest("Invalid JSON body");
97199
97479
  }
97200
- logger57.debug("Creating map object", {
97480
+ logger58.debug("Creating map object", {
97201
97481
  userId: ctx.user.id,
97202
97482
  mapId,
97203
97483
  itemId: body2.itemId,
@@ -97221,7 +97501,7 @@ var init_map_controller = __esm(() => {
97221
97501
  if (!isValidUUID(objectId)) {
97222
97502
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
97223
97503
  }
97224
- logger57.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97504
+ logger58.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97225
97505
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
97226
97506
  });
97227
97507
  maps2 = {
@@ -97234,14 +97514,14 @@ var init_map_controller = __esm(() => {
97234
97514
  });
97235
97515
 
97236
97516
  // ../api-core/src/controllers/notification.controller.ts
97237
- var logger58, list6, updateStatus, getStats2, create4, deliver, notifications2;
97517
+ var logger59, list6, updateStatus, getStats2, create4, deliver, notifications2;
97238
97518
  var init_notification_controller = __esm(() => {
97239
97519
  init_esm();
97240
97520
  init_schemas_index();
97241
97521
  init_src2();
97242
97522
  init_errors();
97243
97523
  init_utils11();
97244
- logger58 = log.scope("NotificationController");
97524
+ logger59 = log.scope("NotificationController");
97245
97525
  list6 = requireNonAnonymous(async (ctx) => {
97246
97526
  const query = {
97247
97527
  status: ctx.url.searchParams.get("status") || undefined,
@@ -97252,10 +97532,10 @@ var init_notification_controller = __esm(() => {
97252
97532
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
97253
97533
  if (!result.success) {
97254
97534
  const details = formatZodError(result.error);
97255
- logger58.warn("List notifications query validation failed", { details });
97535
+ logger59.warn("List notifications query validation failed", { details });
97256
97536
  throw ApiError.badRequest("Invalid query parameters", details);
97257
97537
  }
97258
- logger58.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97538
+ logger59.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97259
97539
  return ctx.services.notification.list(ctx.user, result.data);
97260
97540
  });
97261
97541
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -97270,12 +97550,12 @@ var init_notification_controller = __esm(() => {
97270
97550
  } catch (error2) {
97271
97551
  if (error2 instanceof exports_external.ZodError) {
97272
97552
  const details = formatZodError(error2);
97273
- logger58.warn("Update notification status validation failed", { details });
97553
+ logger59.warn("Update notification status validation failed", { details });
97274
97554
  throw ApiError.unprocessableEntity("Invalid request body", details);
97275
97555
  }
97276
97556
  throw ApiError.badRequest("Invalid JSON body");
97277
97557
  }
97278
- logger58.debug("Updating status", {
97558
+ logger59.debug("Updating status", {
97279
97559
  userId: ctx.user.id,
97280
97560
  notificationId,
97281
97561
  status: body2.status
@@ -97285,7 +97565,7 @@ var init_notification_controller = __esm(() => {
97285
97565
  getStats2 = requireNonAnonymous(async (ctx) => {
97286
97566
  const startDate = ctx.url.searchParams.get("startDate");
97287
97567
  const endDate = ctx.url.searchParams.get("endDate");
97288
- logger58.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97568
+ logger59.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97289
97569
  return ctx.services.notification.getStats(ctx.user, {
97290
97570
  startDate: startDate ? new Date(startDate) : undefined,
97291
97571
  endDate: endDate ? new Date(endDate) : undefined
@@ -97299,12 +97579,12 @@ var init_notification_controller = __esm(() => {
97299
97579
  } catch (error2) {
97300
97580
  if (error2 instanceof exports_external.ZodError) {
97301
97581
  const details = formatZodError(error2);
97302
- logger58.warn("Create notification validation failed", { details });
97582
+ logger59.warn("Create notification validation failed", { details });
97303
97583
  throw ApiError.unprocessableEntity("Invalid request body", details);
97304
97584
  }
97305
97585
  throw ApiError.badRequest("Invalid JSON body");
97306
97586
  }
97307
- logger58.debug("Creating notification", {
97587
+ logger59.debug("Creating notification", {
97308
97588
  userId: ctx.user.id,
97309
97589
  targetUserId: body2.userId,
97310
97590
  type: body2.type
@@ -97322,12 +97602,12 @@ var init_notification_controller = __esm(() => {
97322
97602
  });
97323
97603
  });
97324
97604
  deliver = requireNonAnonymous(async (ctx) => {
97325
- logger58.debug("Delivering notifications", { userId: ctx.user.id });
97605
+ logger59.debug("Delivering notifications", { userId: ctx.user.id });
97326
97606
  try {
97327
97607
  await ctx.services.notification.deliverPending(ctx.user.id);
97328
97608
  return { success: true };
97329
97609
  } catch (error2) {
97330
- logger58.error("Failed to deliver notifications", { error: error2 });
97610
+ logger59.error("Failed to deliver notifications", { error: error2 });
97331
97611
  throw ApiError.internal("Failed to deliver notifications");
97332
97612
  }
97333
97613
  });
@@ -97341,14 +97621,14 @@ var init_notification_controller = __esm(() => {
97341
97621
  });
97342
97622
 
97343
97623
  // ../api-core/src/controllers/realtime.controller.ts
97344
- var logger59, generateToken2, realtime;
97624
+ var logger60, generateToken2, realtime;
97345
97625
  var init_realtime_controller = __esm(() => {
97346
97626
  init_src2();
97347
97627
  init_utils11();
97348
- logger59 = log.scope("RealtimeController");
97628
+ logger60 = log.scope("RealtimeController");
97349
97629
  generateToken2 = requireNonAnonymous(async (ctx) => {
97350
97630
  const gameIdOrSlug = ctx.params.gameId;
97351
- logger59.debug("Generating token", {
97631
+ logger60.debug("Generating token", {
97352
97632
  userId: ctx.user.id,
97353
97633
  gameId: gameIdOrSlug || "global",
97354
97634
  launchId: ctx.launchId
@@ -97361,20 +97641,20 @@ var init_realtime_controller = __esm(() => {
97361
97641
  });
97362
97642
 
97363
97643
  // ../api-core/src/controllers/secrets.controller.ts
97364
- var logger60, listKeys2, setSecrets, deleteSecret, secrets;
97644
+ var logger61, listKeys2, setSecrets, deleteSecret, secrets;
97365
97645
  var init_secrets_controller = __esm(() => {
97366
97646
  init_esm();
97367
97647
  init_schemas_index();
97368
97648
  init_src2();
97369
97649
  init_errors();
97370
97650
  init_utils11();
97371
- logger60 = log.scope("SecretsController");
97651
+ logger61 = log.scope("SecretsController");
97372
97652
  listKeys2 = requireDeveloper(async (ctx) => {
97373
97653
  const slug2 = ctx.params.slug;
97374
97654
  if (!slug2) {
97375
97655
  throw ApiError.badRequest("Missing game slug");
97376
97656
  }
97377
- logger60.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97657
+ logger61.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97378
97658
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
97379
97659
  return { keys };
97380
97660
  });
@@ -97390,12 +97670,12 @@ var init_secrets_controller = __esm(() => {
97390
97670
  } catch (error2) {
97391
97671
  if (error2 instanceof exports_external.ZodError) {
97392
97672
  const details = formatZodError(error2);
97393
- logger60.warn("Set secrets validation failed", { details });
97673
+ logger61.warn("Set secrets validation failed", { details });
97394
97674
  throw ApiError.unprocessableEntity("Validation failed", details);
97395
97675
  }
97396
97676
  throw ApiError.badRequest("Invalid JSON body");
97397
97677
  }
97398
- logger60.debug("Setting secrets", {
97678
+ logger61.debug("Setting secrets", {
97399
97679
  userId: ctx.user.id,
97400
97680
  slug: slug2,
97401
97681
  keyCount: Object.keys(body2).length
@@ -97412,7 +97692,7 @@ var init_secrets_controller = __esm(() => {
97412
97692
  if (!key) {
97413
97693
  throw ApiError.badRequest("Missing secret key");
97414
97694
  }
97415
- logger60.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97695
+ logger61.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97416
97696
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
97417
97697
  return { success: true };
97418
97698
  });
@@ -97424,14 +97704,14 @@ var init_secrets_controller = __esm(() => {
97424
97704
  });
97425
97705
 
97426
97706
  // ../api-core/src/controllers/seed.controller.ts
97427
- var logger61, seed2;
97707
+ var logger62, seed2;
97428
97708
  var init_seed_controller = __esm(() => {
97429
97709
  init_esm();
97430
97710
  init_schemas_index();
97431
97711
  init_src2();
97432
97712
  init_errors();
97433
97713
  init_utils11();
97434
- logger61 = log.scope("SeedController");
97714
+ logger62 = log.scope("SeedController");
97435
97715
  seed2 = requireDeveloper(async (ctx) => {
97436
97716
  const slug2 = ctx.params.slug;
97437
97717
  if (!slug2) {
@@ -97444,12 +97724,12 @@ var init_seed_controller = __esm(() => {
97444
97724
  } catch (error2) {
97445
97725
  if (error2 instanceof exports_external.ZodError) {
97446
97726
  const details = formatZodError(error2);
97447
- logger61.warn("Seed database validation failed", { details });
97727
+ logger62.warn("Seed database validation failed", { details });
97448
97728
  throw ApiError.unprocessableEntity("Validation failed", details);
97449
97729
  }
97450
97730
  throw ApiError.badRequest("Invalid JSON body");
97451
97731
  }
97452
- logger61.debug("Seeding database", {
97732
+ logger62.debug("Seeding database", {
97453
97733
  userId: ctx.user.id,
97454
97734
  slug: slug2,
97455
97735
  codeLength: body2.code.length,
@@ -97460,19 +97740,19 @@ var init_seed_controller = __esm(() => {
97460
97740
  });
97461
97741
 
97462
97742
  // ../api-core/src/controllers/session.controller.ts
97463
- var logger62, start2, end, mintToken, sessions2;
97743
+ var logger63, start2, end, mintToken, sessions2;
97464
97744
  var init_session_controller = __esm(() => {
97465
97745
  init_src2();
97466
97746
  init_tunnel();
97467
97747
  init_errors();
97468
97748
  init_utils11();
97469
- logger62 = log.scope("SessionController");
97749
+ logger63 = log.scope("SessionController");
97470
97750
  start2 = requireAuth(async (ctx) => {
97471
97751
  const gameIdOrSlug = ctx.params.gameId;
97472
97752
  if (!gameIdOrSlug) {
97473
97753
  throw ApiError.badRequest("Missing game ID or slug");
97474
97754
  }
97475
- logger62.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97755
+ logger63.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97476
97756
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
97477
97757
  });
97478
97758
  end = requireAuth(async (ctx) => {
@@ -97484,7 +97764,7 @@ var init_session_controller = __esm(() => {
97484
97764
  if (!sessionId) {
97485
97765
  throw ApiError.badRequest("Missing session ID");
97486
97766
  }
97487
- logger62.debug("Ending session", {
97767
+ logger63.debug("Ending session", {
97488
97768
  userId: ctx.user.id,
97489
97769
  gameIdOrSlug,
97490
97770
  sessionId,
@@ -97497,7 +97777,7 @@ var init_session_controller = __esm(() => {
97497
97777
  if (!gameIdOrSlug) {
97498
97778
  throw ApiError.badRequest("Missing game ID or slug");
97499
97779
  }
97500
- logger62.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97780
+ logger63.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97501
97781
  const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
97502
97782
  let baseUrl;
97503
97783
  if (ctx.config.isLocal) {
@@ -97515,13 +97795,13 @@ var init_session_controller = __esm(() => {
97515
97795
  });
97516
97796
 
97517
97797
  // ../api-core/src/controllers/shop.controller.ts
97518
- var logger63, getShopView, shop;
97798
+ var logger64, getShopView, shop;
97519
97799
  var init_shop_controller = __esm(() => {
97520
97800
  init_src2();
97521
97801
  init_utils11();
97522
- logger63 = log.scope("ShopController");
97802
+ logger64 = log.scope("ShopController");
97523
97803
  getShopView = requireNonAnonymous(async (ctx) => {
97524
- logger63.debug("Getting shop view", { userId: ctx.user.id });
97804
+ logger64.debug("Getting shop view", { userId: ctx.user.id });
97525
97805
  return ctx.services.shop.getShopView(ctx.user);
97526
97806
  });
97527
97807
  shop = {
@@ -97530,7 +97810,7 @@ var init_shop_controller = __esm(() => {
97530
97810
  });
97531
97811
 
97532
97812
  // ../api-core/src/controllers/shop-listing.controller.ts
97533
- var logger64, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97813
+ var logger65, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97534
97814
  var init_shop_listing_controller = __esm(() => {
97535
97815
  init_esm();
97536
97816
  init_schemas_index();
@@ -97538,9 +97818,9 @@ var init_shop_listing_controller = __esm(() => {
97538
97818
  init_src4();
97539
97819
  init_errors();
97540
97820
  init_utils11();
97541
- logger64 = log.scope("ShopListingController");
97821
+ logger65 = log.scope("ShopListingController");
97542
97822
  list7 = requireAdmin(async (ctx) => {
97543
- logger64.debug("Listing shop listings", { userId: ctx.user.id });
97823
+ logger65.debug("Listing shop listings", { userId: ctx.user.id });
97544
97824
  return ctx.services.shopListing.list();
97545
97825
  });
97546
97826
  getById4 = requireAdmin(async (ctx) => {
@@ -97551,7 +97831,7 @@ var init_shop_listing_controller = __esm(() => {
97551
97831
  if (!isValidUUID(listingId)) {
97552
97832
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97553
97833
  }
97554
- logger64.debug("Getting listing", { userId: ctx.user.id, listingId });
97834
+ logger65.debug("Getting listing", { userId: ctx.user.id, listingId });
97555
97835
  return ctx.services.shopListing.getById(listingId);
97556
97836
  });
97557
97837
  create5 = requireAdmin(async (ctx) => {
@@ -97562,12 +97842,12 @@ var init_shop_listing_controller = __esm(() => {
97562
97842
  } catch (error2) {
97563
97843
  if (error2 instanceof exports_external.ZodError) {
97564
97844
  const details = formatZodError(error2);
97565
- logger64.warn("Create shop listing validation failed", { details });
97845
+ logger65.warn("Create shop listing validation failed", { details });
97566
97846
  throw ApiError.unprocessableEntity("Validation failed", details);
97567
97847
  }
97568
97848
  throw ApiError.badRequest("Invalid JSON body");
97569
97849
  }
97570
- logger64.debug("Creating listing", {
97850
+ logger65.debug("Creating listing", {
97571
97851
  userId: ctx.user.id,
97572
97852
  itemId: body2.itemId,
97573
97853
  currencyId: body2.currencyId,
@@ -97590,12 +97870,12 @@ var init_shop_listing_controller = __esm(() => {
97590
97870
  } catch (error2) {
97591
97871
  if (error2 instanceof exports_external.ZodError) {
97592
97872
  const details = formatZodError(error2);
97593
- logger64.warn("Update shop listing validation failed", { details });
97873
+ logger65.warn("Update shop listing validation failed", { details });
97594
97874
  throw ApiError.unprocessableEntity("Validation failed", details);
97595
97875
  }
97596
97876
  throw ApiError.badRequest("Invalid JSON body");
97597
97877
  }
97598
- logger64.debug("Updating listing", {
97878
+ logger65.debug("Updating listing", {
97599
97879
  userId: ctx.user.id,
97600
97880
  listingId,
97601
97881
  price: body2.price,
@@ -97612,7 +97892,7 @@ var init_shop_listing_controller = __esm(() => {
97612
97892
  if (!isValidUUID(listingId)) {
97613
97893
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97614
97894
  }
97615
- logger64.debug("Deleting listing", { userId: ctx.user.id, listingId });
97895
+ logger65.debug("Deleting listing", { userId: ctx.user.id, listingId });
97616
97896
  await ctx.services.shopListing.delete(listingId);
97617
97897
  });
97618
97898
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -97623,7 +97903,7 @@ var init_shop_listing_controller = __esm(() => {
97623
97903
  if (!isValidUUID(gameId)) {
97624
97904
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
97625
97905
  }
97626
- logger64.debug("Listing game listings", { userId: ctx.user.id, gameId });
97906
+ logger65.debug("Listing game listings", { userId: ctx.user.id, gameId });
97627
97907
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
97628
97908
  });
97629
97909
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -97638,7 +97918,7 @@ var init_shop_listing_controller = __esm(() => {
97638
97918
  if (!isValidUUID(itemId)) {
97639
97919
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97640
97920
  }
97641
- logger64.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97921
+ logger65.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97642
97922
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
97643
97923
  });
97644
97924
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -97660,12 +97940,12 @@ var init_shop_listing_controller = __esm(() => {
97660
97940
  } catch (error2) {
97661
97941
  if (error2 instanceof exports_external.ZodError) {
97662
97942
  const details = formatZodError(error2);
97663
- logger64.warn("Create game item listing validation failed", { details });
97943
+ logger65.warn("Create game item listing validation failed", { details });
97664
97944
  throw ApiError.unprocessableEntity("Validation failed", details);
97665
97945
  }
97666
97946
  throw ApiError.badRequest("Invalid JSON body");
97667
97947
  }
97668
- logger64.debug("Creating game item listing", {
97948
+ logger65.debug("Creating game item listing", {
97669
97949
  userId: ctx.user.id,
97670
97950
  gameId,
97671
97951
  itemId,
@@ -97693,12 +97973,12 @@ var init_shop_listing_controller = __esm(() => {
97693
97973
  } catch (error2) {
97694
97974
  if (error2 instanceof exports_external.ZodError) {
97695
97975
  const details = formatZodError(error2);
97696
- logger64.warn("Update game item listing validation failed", { details });
97976
+ logger65.warn("Update game item listing validation failed", { details });
97697
97977
  throw ApiError.unprocessableEntity("Validation failed", details);
97698
97978
  }
97699
97979
  throw ApiError.badRequest("Invalid JSON body");
97700
97980
  }
97701
- logger64.debug("Updating game item listing", {
97981
+ logger65.debug("Updating game item listing", {
97702
97982
  userId: ctx.user.id,
97703
97983
  gameId,
97704
97984
  itemId,
@@ -97720,7 +98000,7 @@ var init_shop_listing_controller = __esm(() => {
97720
98000
  if (!isValidUUID(itemId)) {
97721
98001
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97722
98002
  }
97723
- logger64.debug("Deleting game item listing", {
98003
+ logger65.debug("Deleting game item listing", {
97724
98004
  userId: ctx.user.id,
97725
98005
  gameId,
97726
98006
  itemId
@@ -97747,21 +98027,21 @@ async function getBySlug2(ctx) {
97747
98027
  if (!slug2) {
97748
98028
  throw ApiError.badRequest("Template slug is required");
97749
98029
  }
97750
- logger65.debug("Getting sprite by slug", { slug: slug2 });
98030
+ logger66.debug("Getting sprite by slug", { slug: slug2 });
97751
98031
  return ctx.services.sprite.getBySlug(slug2);
97752
98032
  }
97753
- var logger65, sprites;
98033
+ var logger66, sprites;
97754
98034
  var init_sprite_controller = __esm(() => {
97755
98035
  init_src2();
97756
98036
  init_errors();
97757
- logger65 = log.scope("SpriteController");
98037
+ logger66 = log.scope("SpriteController");
97758
98038
  sprites = {
97759
98039
  getBySlug: getBySlug2
97760
98040
  };
97761
98041
  });
97762
98042
 
97763
98043
  // ../api-core/src/controllers/timeback.controller.ts
97764
- var logger66, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, deleteAssessment, reorderAssessments, reorderQuestions, activateAssessment, deactivateAssessment, listQuestions, createQuestion, updateQuestion, deleteQuestion, getAssessmentBankStatus, destroyAssessmentBank, timeback2;
98044
+ var logger67, getTodayXp, getTotalXp, updateTodayXp, getXpHistory, populateStudent, getUser, getUserById, setupIntegration, getIntegrations, updateIntegration, getIntegrationConfig, verifyIntegration, getConfig2, deleteIntegrations, endActivity, heartbeat, advanceCourse, getStudentXp, getRoster, getStudentOverview, getGameMetrics, getStudentActivity, getActivityDetail, grantXp, adjustTime, adjustMastery, reconcileMasteryForConfigChange, searchStudents, enrollStudent, unenrollStudent, reactivateEnrollment, listAssessments, createAssessment, deleteAssessment, reorderAssessments, reorderQuestions, activateAssessment, deactivateAssessment, listQuestions, createQuestion, updateQuestion, deleteQuestion, getAssessmentBankStatus, destroyAssessmentBank, timeback2;
97765
98045
  var init_timeback_controller = __esm(() => {
97766
98046
  init_esm();
97767
98047
  init_schemas_index();
@@ -97769,15 +98049,15 @@ var init_timeback_controller = __esm(() => {
97769
98049
  init_src4();
97770
98050
  init_errors();
97771
98051
  init_utils11();
97772
- logger66 = log.scope("TimebackController");
98052
+ logger67 = log.scope("TimebackController");
97773
98053
  getTodayXp = requireNonAnonymous(async (ctx) => {
97774
98054
  const date4 = ctx.url.searchParams.get("date") || undefined;
97775
98055
  const tz = ctx.url.searchParams.get("tz") || undefined;
97776
- logger66.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
98056
+ logger67.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
97777
98057
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
97778
98058
  });
97779
98059
  getTotalXp = requireNonAnonymous(async (ctx) => {
97780
- logger66.debug("Getting total XP", { userId: ctx.user.id });
98060
+ logger67.debug("Getting total XP", { userId: ctx.user.id });
97781
98061
  return ctx.services.timeback.getTotalXp(ctx.user.id);
97782
98062
  });
97783
98063
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -97788,18 +98068,18 @@ var init_timeback_controller = __esm(() => {
97788
98068
  } catch (error2) {
97789
98069
  if (error2 instanceof exports_external.ZodError) {
97790
98070
  const details = formatZodError(error2);
97791
- logger66.warn("Update today XP validation failed", { details });
98071
+ logger67.warn("Update today XP validation failed", { details });
97792
98072
  throw ApiError.unprocessableEntity("Validation failed", details);
97793
98073
  }
97794
98074
  throw ApiError.badRequest("Invalid JSON body");
97795
98075
  }
97796
- logger66.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
98076
+ logger67.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
97797
98077
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
97798
98078
  });
97799
98079
  getXpHistory = requireNonAnonymous(async (ctx) => {
97800
98080
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
97801
98081
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
97802
- logger66.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
98082
+ logger67.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
97803
98083
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
97804
98084
  });
97805
98085
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -97810,18 +98090,18 @@ var init_timeback_controller = __esm(() => {
97810
98090
  } catch (error2) {
97811
98091
  if (error2 instanceof exports_external.ZodError) {
97812
98092
  const details = formatZodError(error2);
97813
- logger66.warn("Populate student validation failed", { details });
98093
+ logger67.warn("Populate student validation failed", { details });
97814
98094
  throw ApiError.unprocessableEntity("Validation failed", details);
97815
98095
  }
97816
98096
  }
97817
- logger66.debug("Populating student", {
98097
+ logger67.debug("Populating student", {
97818
98098
  userId: ctx.user.id,
97819
98099
  hasProvidedNames: Boolean(providedNames)
97820
98100
  });
97821
98101
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
97822
98102
  });
97823
98103
  getUser = requireNonAnonymous(async (ctx) => {
97824
- logger66.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
98104
+ logger67.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
97825
98105
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
97826
98106
  });
97827
98107
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -97829,7 +98109,7 @@ var init_timeback_controller = __esm(() => {
97829
98109
  if (!timebackId) {
97830
98110
  throw ApiError.badRequest("Missing timebackId parameter");
97831
98111
  }
97832
- logger66.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
98112
+ logger67.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
97833
98113
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
97834
98114
  });
97835
98115
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -97840,12 +98120,12 @@ var init_timeback_controller = __esm(() => {
97840
98120
  } catch (error2) {
97841
98121
  if (error2 instanceof exports_external.ZodError) {
97842
98122
  const details = formatZodError(error2);
97843
- logger66.warn("Setup integration validation failed", { details });
98123
+ logger67.warn("Setup integration validation failed", { details });
97844
98124
  throw ApiError.unprocessableEntity("Validation failed", details);
97845
98125
  }
97846
98126
  throw ApiError.badRequest("Invalid JSON body");
97847
98127
  }
97848
- logger66.debug("Setting up integration", {
98128
+ logger67.debug("Setting up integration", {
97849
98129
  userId: ctx.user.id,
97850
98130
  gameId: body2.gameId
97851
98131
  });
@@ -97859,9 +98139,37 @@ var init_timeback_controller = __esm(() => {
97859
98139
  if (!isValidUUID(gameId)) {
97860
98140
  throw ApiError.unprocessableEntity("Invalid gameId format");
97861
98141
  }
97862
- logger66.debug("Getting integrations", { userId: ctx.user.id, gameId });
98142
+ logger67.debug("Getting integrations", { userId: ctx.user.id, gameId });
97863
98143
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
97864
98144
  });
98145
+ updateIntegration = requireDeveloper(async (ctx) => {
98146
+ const { gameId, courseId } = ctx.params;
98147
+ if (!gameId || !courseId) {
98148
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98149
+ }
98150
+ if (!isValidUUID(gameId)) {
98151
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98152
+ }
98153
+ const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
98154
+ logger67.debug("Updating integration", {
98155
+ userId: ctx.user.id,
98156
+ gameId,
98157
+ courseId,
98158
+ fields: Object.keys(body2)
98159
+ });
98160
+ return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
98161
+ });
98162
+ getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
98163
+ const { gameId, courseId } = ctx.params;
98164
+ if (!gameId || !courseId) {
98165
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98166
+ }
98167
+ if (!isValidUUID(gameId)) {
98168
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98169
+ }
98170
+ logger67.debug("Getting integration config", { userId: ctx.user.id, gameId, courseId });
98171
+ return ctx.services.timeback.getIntegrationConfig(gameId, courseId, ctx.user);
98172
+ });
97865
98173
  verifyIntegration = requireDeveloper(async (ctx) => {
97866
98174
  const gameId = ctx.params.gameId;
97867
98175
  if (!gameId) {
@@ -97870,7 +98178,7 @@ var init_timeback_controller = __esm(() => {
97870
98178
  if (!isValidUUID(gameId)) {
97871
98179
  throw ApiError.unprocessableEntity("Invalid gameId format");
97872
98180
  }
97873
- logger66.debug("Verifying integration", { userId: ctx.user.id, gameId });
98181
+ logger67.debug("Verifying integration", { userId: ctx.user.id, gameId });
97874
98182
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
97875
98183
  });
97876
98184
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -97881,7 +98189,7 @@ var init_timeback_controller = __esm(() => {
97881
98189
  if (!isValidUUID(gameId)) {
97882
98190
  throw ApiError.unprocessableEntity("Invalid gameId format");
97883
98191
  }
97884
- logger66.debug("Getting config", { userId: ctx.user.id, gameId });
98192
+ logger67.debug("Getting config", { userId: ctx.user.id, gameId });
97885
98193
  return ctx.services.timeback.getConfig(gameId, ctx.user);
97886
98194
  });
97887
98195
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -97892,7 +98200,7 @@ var init_timeback_controller = __esm(() => {
97892
98200
  if (!isValidUUID(gameId)) {
97893
98201
  throw ApiError.unprocessableEntity("Invalid gameId format");
97894
98202
  }
97895
- logger66.debug("Deleting integrations", { userId: ctx.user.id, gameId });
98203
+ logger67.debug("Deleting integrations", { userId: ctx.user.id, gameId });
97896
98204
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
97897
98205
  });
97898
98206
  endActivity = requireDeveloper(async (ctx) => {
@@ -97903,7 +98211,7 @@ var init_timeback_controller = __esm(() => {
97903
98211
  } catch (error2) {
97904
98212
  if (error2 instanceof exports_external.ZodError) {
97905
98213
  const details = formatZodError(error2);
97906
- logger66.warn("End activity validation failed", { details });
98214
+ logger67.warn("End activity validation failed", { details });
97907
98215
  throw ApiError.unprocessableEntity("Validation failed", details);
97908
98216
  }
97909
98217
  throw ApiError.badRequest("Invalid JSON body");
@@ -97921,7 +98229,7 @@ var init_timeback_controller = __esm(() => {
97921
98229
  masteredUnits,
97922
98230
  extensions
97923
98231
  } = body2;
97924
- logger66.debug("Ending activity", { userId: ctx.user.id, gameId });
98232
+ logger67.debug("Ending activity", { userId: ctx.user.id, gameId });
97925
98233
  return ctx.services.timeback.endActivity({
97926
98234
  gameId,
97927
98235
  studentId,
@@ -97945,7 +98253,7 @@ var init_timeback_controller = __esm(() => {
97945
98253
  } catch (error2) {
97946
98254
  if (error2 instanceof exports_external.ZodError) {
97947
98255
  const details = formatZodError(error2);
97948
- logger66.warn("Heartbeat validation failed", { details });
98256
+ logger67.warn("Heartbeat validation failed", { details });
97949
98257
  throw ApiError.unprocessableEntity("Validation failed", details);
97950
98258
  }
97951
98259
  throw ApiError.badRequest("Invalid JSON body");
@@ -97961,7 +98269,7 @@ var init_timeback_controller = __esm(() => {
97961
98269
  windowSequence,
97962
98270
  isFinal
97963
98271
  } = body2;
97964
- logger66.debug("Recording heartbeat", {
98272
+ logger67.debug("Recording heartbeat", {
97965
98273
  userId: ctx.user.id,
97966
98274
  gameId,
97967
98275
  runId,
@@ -97986,7 +98294,7 @@ var init_timeback_controller = __esm(() => {
97986
98294
  });
97987
98295
  advanceCourse = requireDeveloper(async (ctx) => {
97988
98296
  const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
97989
- logger66.debug("Advancing student manually", {
98297
+ logger67.debug("Advancing student manually", {
97990
98298
  userId: ctx.user.id,
97991
98299
  gameId: body2.gameId,
97992
98300
  studentId: body2.studentId,
@@ -98027,7 +98335,7 @@ var init_timeback_controller = __esm(() => {
98027
98335
  perCourse: includeOptions.includes("percourse"),
98028
98336
  today: includeOptions.includes("today")
98029
98337
  };
98030
- logger66.debug("Getting student XP", {
98338
+ logger67.debug("Getting student XP", {
98031
98339
  requesterId: ctx.user.id,
98032
98340
  timebackId,
98033
98341
  gameId,
@@ -98049,7 +98357,7 @@ var init_timeback_controller = __esm(() => {
98049
98357
  if (!gameId || !courseId) {
98050
98358
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98051
98359
  }
98052
- logger66.debug("Getting course roster", {
98360
+ logger67.debug("Getting course roster", {
98053
98361
  requesterId: ctx.user.id,
98054
98362
  gameId,
98055
98363
  courseId,
@@ -98066,7 +98374,7 @@ var init_timeback_controller = __esm(() => {
98066
98374
  if (!timebackId || !gameId) {
98067
98375
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
98068
98376
  }
98069
- logger66.debug("Getting student overview", {
98377
+ logger67.debug("Getting student overview", {
98070
98378
  requesterId: ctx.user.id,
98071
98379
  timebackId,
98072
98380
  gameId,
@@ -98080,7 +98388,7 @@ var init_timeback_controller = __esm(() => {
98080
98388
  if (!gameId || !timebackId) {
98081
98389
  throw ApiError.badRequest("Missing gameId or timebackId path parameter");
98082
98390
  }
98083
- logger66.debug("Getting game metrics", {
98391
+ logger67.debug("Getting game metrics", {
98084
98392
  requesterId: ctx.user.id,
98085
98393
  gameId,
98086
98394
  timebackId
@@ -98098,7 +98406,7 @@ var init_timeback_controller = __esm(() => {
98098
98406
  if (!timebackId || !courseId || !gameId) {
98099
98407
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
98100
98408
  }
98101
- logger66.debug("Getting student activity", {
98409
+ logger67.debug("Getting student activity", {
98102
98410
  requesterId: ctx.user.id,
98103
98411
  timebackId,
98104
98412
  courseId,
@@ -98123,7 +98431,7 @@ var init_timeback_controller = __esm(() => {
98123
98431
  if (!timebackId || !courseId || !activityId || !gameId) {
98124
98432
  throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
98125
98433
  }
98126
- logger66.debug("Getting activity detail", {
98434
+ logger67.debug("Getting activity detail", {
98127
98435
  requesterId: ctx.user.id,
98128
98436
  timebackId,
98129
98437
  courseId,
@@ -98141,7 +98449,7 @@ var init_timeback_controller = __esm(() => {
98141
98449
  });
98142
98450
  grantXp = requireDeveloper(async (ctx) => {
98143
98451
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
98144
- logger66.debug("Granting manual XP", {
98452
+ logger67.debug("Granting manual XP", {
98145
98453
  requesterId: ctx.user.id,
98146
98454
  gameId: body2.gameId,
98147
98455
  courseId: body2.courseId,
@@ -98153,7 +98461,7 @@ var init_timeback_controller = __esm(() => {
98153
98461
  });
98154
98462
  adjustTime = requireDeveloper(async (ctx) => {
98155
98463
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
98156
- logger66.debug("Adjusting time spent", {
98464
+ logger67.debug("Adjusting time spent", {
98157
98465
  requesterId: ctx.user.id,
98158
98466
  gameId: body2.gameId,
98159
98467
  courseId: body2.courseId,
@@ -98165,7 +98473,7 @@ var init_timeback_controller = __esm(() => {
98165
98473
  });
98166
98474
  adjustMastery = requireDeveloper(async (ctx) => {
98167
98475
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
98168
- logger66.debug("Adjusting mastered units", {
98476
+ logger67.debug("Adjusting mastered units", {
98169
98477
  requesterId: ctx.user.id,
98170
98478
  gameId: body2.gameId,
98171
98479
  courseId: body2.courseId,
@@ -98175,6 +98483,22 @@ var init_timeback_controller = __esm(() => {
98175
98483
  });
98176
98484
  return ctx.services.timebackAdmin.adjustMasteredUnits(body2, ctx.user);
98177
98485
  });
98486
+ reconcileMasteryForConfigChange = requireDeveloper(async (ctx) => {
98487
+ const body2 = await parseRequestBody(ctx.request, ReconcileMasteryForConfigChangeSchema);
98488
+ logger67.debug("Reconciling mastery completion for config change", {
98489
+ requesterId: ctx.user.id,
98490
+ gameId: body2.gameId,
98491
+ courseId: body2.courseId,
98492
+ oldMasterableUnits: body2.oldMasterableUnits,
98493
+ newMasterableUnits: body2.newMasterableUnits,
98494
+ affectedCount: body2.affectedStudentIds.length
98495
+ });
98496
+ return ctx.services.timebackAdmin.reconcileMasteryForConfigChange(body2.gameId, body2.courseId, ctx.user, {
98497
+ oldMasterableUnits: body2.oldMasterableUnits,
98498
+ newMasterableUnits: body2.newMasterableUnits,
98499
+ affectedStudentIds: body2.affectedStudentIds
98500
+ });
98501
+ });
98178
98502
  searchStudents = requireGameManagementAccess(async (ctx) => {
98179
98503
  const gameId = ctx.params.gameId;
98180
98504
  const courseId = ctx.params.courseId;
@@ -98182,7 +98506,7 @@ var init_timeback_controller = __esm(() => {
98182
98506
  if (!gameId || !courseId) {
98183
98507
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98184
98508
  }
98185
- logger66.debug("Searching students for enrollment", {
98509
+ logger67.debug("Searching students for enrollment", {
98186
98510
  requesterId: ctx.user.id,
98187
98511
  gameId,
98188
98512
  courseId,
@@ -98192,7 +98516,7 @@ var init_timeback_controller = __esm(() => {
98192
98516
  });
98193
98517
  enrollStudent = requireGameManagementAccess(async (ctx) => {
98194
98518
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
98195
- logger66.debug("Enrolling student", {
98519
+ logger67.debug("Enrolling student", {
98196
98520
  requesterId: ctx.user.id,
98197
98521
  gameId: body2.gameId,
98198
98522
  courseId: body2.courseId,
@@ -98202,7 +98526,7 @@ var init_timeback_controller = __esm(() => {
98202
98526
  });
98203
98527
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
98204
98528
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
98205
- logger66.debug("Unenrolling student", {
98529
+ logger67.debug("Unenrolling student", {
98206
98530
  requesterId: ctx.user.id,
98207
98531
  gameId: body2.gameId,
98208
98532
  courseId: body2.courseId,
@@ -98212,7 +98536,7 @@ var init_timeback_controller = __esm(() => {
98212
98536
  });
98213
98537
  reactivateEnrollment = requireGameManagementAccess(async (ctx) => {
98214
98538
  const body2 = await parseRequestBody(ctx.request, ReactivateEnrollmentRequestSchema);
98215
- logger66.debug("Reactivating enrollment", {
98539
+ logger67.debug("Reactivating enrollment", {
98216
98540
  requesterId: ctx.user.id,
98217
98541
  gameId: body2.gameId,
98218
98542
  courseId: body2.courseId,
@@ -98352,6 +98676,8 @@ var init_timeback_controller = __esm(() => {
98352
98676
  getUserById,
98353
98677
  setupIntegration,
98354
98678
  getIntegrations,
98679
+ updateIntegration,
98680
+ getIntegrationConfig,
98355
98681
  verifyIntegration,
98356
98682
  getConfig: getConfig2,
98357
98683
  deleteIntegrations,
@@ -98367,6 +98693,7 @@ var init_timeback_controller = __esm(() => {
98367
98693
  grantXp,
98368
98694
  adjustTime,
98369
98695
  adjustMastery,
98696
+ reconcileMasteryForConfigChange,
98370
98697
  searchStudents,
98371
98698
  enrollStudent,
98372
98699
  unenrollStudent,
@@ -98388,14 +98715,14 @@ var init_timeback_controller = __esm(() => {
98388
98715
  });
98389
98716
 
98390
98717
  // ../api-core/src/controllers/upload.controller.ts
98391
- var logger67, initiate;
98718
+ var logger68, initiate;
98392
98719
  var init_upload_controller = __esm(() => {
98393
98720
  init_esm();
98394
98721
  init_schemas_index();
98395
98722
  init_src2();
98396
98723
  init_errors();
98397
98724
  init_utils11();
98398
- logger67 = log.scope("UploadController");
98725
+ logger68 = log.scope("UploadController");
98399
98726
  initiate = requireDeveloper(async (ctx) => {
98400
98727
  let body2;
98401
98728
  try {
@@ -98404,34 +98731,34 @@ var init_upload_controller = __esm(() => {
98404
98731
  } catch (error2) {
98405
98732
  if (error2 instanceof exports_external.ZodError) {
98406
98733
  const details = formatZodError(error2);
98407
- logger67.warn("Initiate upload validation failed", { details });
98734
+ logger68.warn("Initiate upload validation failed", { details });
98408
98735
  throw ApiError.unprocessableEntity("Validation failed", details);
98409
98736
  }
98410
98737
  throw ApiError.badRequest("Invalid JSON body");
98411
98738
  }
98412
- logger67.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98739
+ logger68.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98413
98740
  return ctx.services.upload.initiate(body2, ctx.user);
98414
98741
  });
98415
98742
  });
98416
98743
 
98417
98744
  // ../api-core/src/controllers/user.controller.ts
98418
- var logger68, getMe, getDemoProfile, updateDemoProfile, users2;
98745
+ var logger69, getMe, getDemoProfile, updateDemoProfile, users2;
98419
98746
  var init_user_controller = __esm(() => {
98420
98747
  init_schemas_index();
98421
98748
  init_src2();
98422
98749
  init_utils11();
98423
- logger68 = log.scope("UserController");
98750
+ logger69 = log.scope("UserController");
98424
98751
  getMe = requireNonAnonymous(async (ctx) => {
98425
- logger68.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98752
+ logger69.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98426
98753
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
98427
98754
  });
98428
98755
  getDemoProfile = requireAnonymous(async (ctx) => {
98429
- logger68.debug("Getting demo profile", { userId: ctx.user.id });
98756
+ logger69.debug("Getting demo profile", { userId: ctx.user.id });
98430
98757
  return ctx.services.user.getDemoProfile(ctx.user.id);
98431
98758
  });
98432
98759
  updateDemoProfile = requireAnonymous(async (ctx) => {
98433
98760
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
98434
- logger68.debug("Updating demo profile", {
98761
+ logger69.debug("Updating demo profile", {
98435
98762
  userId: ctx.user.id,
98436
98763
  displayName: body2.displayName
98437
98764
  });
@@ -98445,13 +98772,13 @@ var init_user_controller = __esm(() => {
98445
98772
  });
98446
98773
 
98447
98774
  // ../api-core/src/controllers/verify.controller.ts
98448
- var logger69;
98775
+ var logger70;
98449
98776
  var init_verify_controller = __esm(() => {
98450
98777
  init_schemas_index();
98451
98778
  init_src2();
98452
98779
  init_errors();
98453
98780
  init_utils11();
98454
- logger69 = log.scope("VerifyController");
98781
+ logger70 = log.scope("VerifyController");
98455
98782
  });
98456
98783
 
98457
98784
  // ../api-core/src/controllers/index.ts
@@ -98765,7 +99092,7 @@ var init_uploads = __esm(() => {
98765
99092
  });
98766
99093
 
98767
99094
  // src/routes/platform/games/deploy.ts
98768
- var logger70, gameDeployRouter;
99095
+ var logger71, gameDeployRouter;
98769
99096
  var init_deploy = __esm(() => {
98770
99097
  init_drizzle_orm();
98771
99098
  init_dist4();
@@ -98775,7 +99102,7 @@ var init_deploy = __esm(() => {
98775
99102
  init_src2();
98776
99103
  init_api();
98777
99104
  init_uploads();
98778
- logger70 = log.scope("SandboxDeploy");
99105
+ logger71 = log.scope("SandboxDeploy");
98779
99106
  gameDeployRouter = new Hono2;
98780
99107
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
98781
99108
  const user = c2.get("user");
@@ -98901,7 +99228,7 @@ var init_deploy = __esm(() => {
98901
99228
  completedAt: now2
98902
99229
  };
98903
99230
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
98904
- logger70.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
99231
+ logger71.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
98905
99232
  return c2.json({
98906
99233
  id: insertedJob.id,
98907
99234
  status: "succeeded",
@@ -99548,7 +99875,7 @@ function verifyMockToken(idToken) {
99548
99875
  throw new Error("Invalid LTI token format");
99549
99876
  }
99550
99877
  }
99551
- var logger71, ltiRouter;
99878
+ var logger72, ltiRouter;
99552
99879
  var init_lti = __esm(() => {
99553
99880
  init_drizzle_orm();
99554
99881
  init_dist4();
@@ -99558,7 +99885,7 @@ var init_lti = __esm(() => {
99558
99885
  init_src2();
99559
99886
  init_constants();
99560
99887
  init_api();
99561
- logger71 = log.scope("SandboxLti");
99888
+ logger72 = log.scope("SandboxLti");
99562
99889
  ltiRouter = new Hono2;
99563
99890
  ltiRouter.post("/launch", async (c2) => {
99564
99891
  const db2 = c2.get("db");
@@ -99576,7 +99903,7 @@ var init_lti = __esm(() => {
99576
99903
  claims = verifyMockToken(idToken);
99577
99904
  } catch (error2) {
99578
99905
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99579
- logger71.error("LTI token verification failed", { error: errorMessage });
99906
+ logger72.error("LTI token verification failed", { error: errorMessage });
99580
99907
  return c2.json({
99581
99908
  error: "invalid_token",
99582
99909
  message: errorMessage
@@ -99584,7 +99911,7 @@ var init_lti = __esm(() => {
99584
99911
  }
99585
99912
  const validationError = validateLtiClaims(claims);
99586
99913
  if (validationError) {
99587
- logger71.warn("LTI claims validation failed", {
99914
+ logger72.warn("LTI claims validation failed", {
99588
99915
  error: validationError,
99589
99916
  sub: claims.sub
99590
99917
  });
@@ -99604,7 +99931,7 @@ var init_lti = __esm(() => {
99604
99931
  createdAt: new Date,
99605
99932
  updatedAt: new Date
99606
99933
  });
99607
- logger71.info("LTI launch successful", { userId: user.id });
99934
+ logger72.info("LTI launch successful", { userId: user.id });
99608
99935
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
99609
99936
  const currentHost = new URL(c2.req.url).hostname;
99610
99937
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -99612,7 +99939,7 @@ var init_lti = __esm(() => {
99612
99939
  return c2.redirect(redirectPath);
99613
99940
  } catch (error2) {
99614
99941
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99615
- logger71.error("Unexpected error during LTI launch", { error: errorMessage });
99942
+ logger72.error("Unexpected error during LTI launch", { error: errorMessage });
99616
99943
  return c2.json({
99617
99944
  error: "unexpected_error",
99618
99945
  message: "An unexpected error occurred during LTI launch"