@playcademy/sandbox 0.3.18 → 0.3.19-beta.2

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 +840 -501
  2. package/dist/server.js +840 -501
  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.2",
1320
1320
  description: "Local development server for Playcademy game development",
1321
1321
  type: "module",
1322
1322
  exports: {
@@ -5897,7 +5897,8 @@ var init_schema = __esm(() => {
5897
5897
  ltiTestMode: exports_external.boolean().default(false),
5898
5898
  realtime: realtimeConfigSchema.optional(),
5899
5899
  platformServiceJwt: platformServiceJwtConfigSchema.optional(),
5900
- uploadBucket: exports_external.string().optional()
5900
+ uploadBucket: exports_external.string().optional(),
5901
+ queueIngressSecret: exports_external.string().optional()
5901
5902
  });
5902
5903
  });
5903
5904
 
@@ -26417,6 +26418,14 @@ class DeployService {
26417
26418
  await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
26418
26419
  logger3.info("Set API key on worker", { slug, deploymentId });
26419
26420
  }
26421
+ async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
26422
+ const secret = this.deps.config.queueIngressSecret;
26423
+ if (!secret) {
26424
+ return;
26425
+ }
26426
+ const cf = this.getCloudflare();
26427
+ await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
26428
+ }
26420
26429
  async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
26421
26430
  const frontendZip = await this.timeStep("Fetch temporary files", () => uploadDeps.getObjectAsByteArray(uploadToken), { slug });
26422
26431
  if (!frontendZip || frontendZip.length === 0) {
@@ -26499,7 +26508,10 @@ class DeployService {
26499
26508
  const codeHash = hasBackend ? await generateDeploymentHash(request.code) : null;
26500
26509
  await this.saveDeployment(game.id, result.deploymentId, result.url, codeHash, result.resources);
26501
26510
  if (hasBackend) {
26502
- await this.timeStep("Configure worker secrets", () => this.ensureApiKeyOnWorker(user, slug, result.deploymentId), { slug, deploymentId: result.deploymentId });
26511
+ await this.timeStep("Configure worker secrets", async () => {
26512
+ await this.ensureApiKeyOnWorker(user, slug, result.deploymentId);
26513
+ await this.ensureQueueIngressSecretOnWorker(slug, result.deploymentId);
26514
+ }, { slug, deploymentId: result.deploymentId });
26503
26515
  }
26504
26516
  if (hasMetadata || hasFrontend) {
26505
26517
  await this.applyGameMetadata(game.id, request, hasFrontend, hasMetadata, result.url);
@@ -31180,7 +31192,7 @@ function isValidAdminAttributionDate(value) {
31180
31192
  const date3 = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
31181
31193
  return date3.getUTCFullYear() === year && date3.getUTCMonth() + 1 === month && date3.getUTCDate() === day;
31182
31194
  }
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;
31195
+ 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
31196
  var init_schemas11 = __esm(() => {
31185
31197
  init_drizzle_zod();
31186
31198
  init_esm();
@@ -31205,6 +31217,24 @@ var init_schemas11 = __esm(() => {
31205
31217
  xp: exports_external.number().min(0, "XP must be a non-negative number"),
31206
31218
  userTimestamp: exports_external.string().datetime().optional()
31207
31219
  });
31220
+ CourseGoalsSchema = exports_external.object({
31221
+ dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
31222
+ dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
31223
+ dailyActiveMinutes: exports_external.number().int().nonnegative().nullable().optional(),
31224
+ dailyAccuracy: exports_external.number().int().min(0).max(100).nullable().optional(),
31225
+ dailyMasteredUnits: exports_external.number().int().nonnegative().nullable().optional()
31226
+ });
31227
+ UpdateGameTimebackIntegrationRequestSchema = exports_external.object({
31228
+ title: exports_external.string().trim().min(1).optional(),
31229
+ courseCode: exports_external.string().trim().min(1).optional(),
31230
+ subject: TimebackSubjectSchema.optional(),
31231
+ totalXp: exports_external.number().int().nonnegative().nullable().optional(),
31232
+ masterableUnits: exports_external.number().int().nonnegative().nullable().optional(),
31233
+ goals: CourseGoalsSchema.optional(),
31234
+ publishStatus: exports_external.enum(["draft", "testing", "published", "deactivated"]).nullable().optional(),
31235
+ isSupplemental: exports_external.boolean().optional(),
31236
+ timebackVisible: exports_external.boolean().nullable().optional()
31237
+ });
31208
31238
  TimebackActivityDataSchema = exports_external.object({
31209
31239
  activityId: exports_external.string().min(1),
31210
31240
  activityName: exports_external.string().optional(),
@@ -31374,6 +31404,13 @@ var init_schemas11 = __esm(() => {
31374
31404
  date: AdminAttributionDateSchema.optional(),
31375
31405
  useCurrentTime: exports_external.boolean().optional()
31376
31406
  });
31407
+ ReconcileMasteryForConfigChangeSchema = exports_external.object({
31408
+ gameId: exports_external.string().uuid(),
31409
+ courseId: exports_external.string().min(1),
31410
+ oldMasterableUnits: exports_external.number().int().positive(),
31411
+ newMasterableUnits: exports_external.number().int().positive(),
31412
+ affectedStudentIds: exports_external.array(exports_external.string().min(1)).min(1).max(500)
31413
+ });
31377
31414
  EnrollStudentRequestSchema = exports_external.object({
31378
31415
  gameId: exports_external.string().uuid(),
31379
31416
  courseId: exports_external.string().min(1),
@@ -31529,6 +31566,65 @@ var init_timeback_admin_util = __esm(() => {
31529
31566
  init_errors();
31530
31567
  });
31531
31568
 
31569
+ // ../api-core/src/utils/timeback-mastery-completion.util.ts
31570
+ async function upsertMasteryCompletionEntry(params) {
31571
+ const { client, courseId, studentId, appName, action } = params;
31572
+ const ids = deriveSourcedIds(courseId);
31573
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
31574
+ const resultId = `${lineItemId}:${studentId}:completion`;
31575
+ if (action === "complete") {
31576
+ await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
31577
+ sourcedId: lineItemId,
31578
+ title: "Mastery Completion",
31579
+ status: ONEROSTER_STATUS.active,
31580
+ course: { sourcedId: ids.course },
31581
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
31582
+ });
31583
+ await client.oneroster.assessmentResults.upsert(resultId, {
31584
+ sourcedId: resultId,
31585
+ status: ONEROSTER_STATUS.active,
31586
+ assessmentLineItem: { sourcedId: lineItemId },
31587
+ student: { sourcedId: studentId },
31588
+ score: 100,
31589
+ scoreDate: new Date().toISOString(),
31590
+ scoreStatus: SCORE_STATUS.fullyGraded,
31591
+ inProgress: "false",
31592
+ metadata: {
31593
+ isMasteryCompletion: true,
31594
+ adminAction: true,
31595
+ appName
31596
+ }
31597
+ });
31598
+ } else {
31599
+ try {
31600
+ await client.oneroster.assessmentResults.upsert(resultId, {
31601
+ sourcedId: resultId,
31602
+ status: ONEROSTER_STATUS.active,
31603
+ assessmentLineItem: { sourcedId: lineItemId },
31604
+ student: { sourcedId: studentId },
31605
+ score: 0,
31606
+ scoreDate: new Date().toISOString(),
31607
+ scoreStatus: SCORE_STATUS.notSubmitted,
31608
+ inProgress: "true",
31609
+ metadata: {
31610
+ isMasteryCompletion: true,
31611
+ adminAction: true,
31612
+ appName
31613
+ }
31614
+ });
31615
+ } catch {
31616
+ logger17.debug("No completion entry to revoke", { studentId, courseId });
31617
+ }
31618
+ }
31619
+ }
31620
+ var logger17;
31621
+ var init_timeback_mastery_completion_util = __esm(() => {
31622
+ init_src2();
31623
+ init_constants4();
31624
+ init_utils6();
31625
+ logger17 = log.scope("timeback-mastery-completion");
31626
+ });
31627
+
31532
31628
  // ../api-core/src/utils/timeback.util.ts
31533
31629
  function isRecord2(value) {
31534
31630
  return typeof value === "object" && value !== null;
@@ -31886,7 +31982,7 @@ class TimebackAdminService {
31886
31982
  }
31887
31983
  requireClient() {
31888
31984
  if (!this.deps.timeback) {
31889
- logger17.error("Timeback client not available in context");
31985
+ logger18.error("Timeback client not available in context");
31890
31986
  throw new ValidationError("Timeback integration not available in this environment");
31891
31987
  }
31892
31988
  return this.deps.timeback;
@@ -32106,7 +32202,7 @@ class TimebackAdminService {
32106
32202
  });
32107
32203
  return [enrollmentId, this.summarizeAnalyticsFacts(analytics.facts)];
32108
32204
  } catch (error) {
32109
- logger17.warn("Failed to load enrollment analytics summary", {
32205
+ logger18.warn("Failed to load enrollment analytics summary", {
32110
32206
  enrollmentId,
32111
32207
  error: error instanceof Error ? error.message : String(error)
32112
32208
  });
@@ -32136,7 +32232,7 @@ class TimebackAdminService {
32136
32232
  const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
32137
32233
  return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
32138
32234
  } catch (error) {
32139
- logger17.warn("Failed to load recent Caliper activity", {
32235
+ logger18.warn("Failed to load recent Caliper activity", {
32140
32236
  studentId,
32141
32237
  gameId: source.gameId,
32142
32238
  sourceMode: source.sourceMode,
@@ -32462,60 +32558,44 @@ class TimebackAdminService {
32462
32558
  const wasMastered = currentMastered >= masterableUnits;
32463
32559
  const willBeMastered = currentMastered + data.units >= masterableUnits;
32464
32560
  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
- }
32561
+ await upsertMasteryCompletionEntry({
32562
+ client,
32563
+ courseId: data.courseId,
32564
+ studentId: data.studentId,
32565
+ appName,
32566
+ action: willBeMastered ? "complete" : "revoke"
32567
+ });
32515
32568
  }
32516
32569
  }
32517
32570
  return { status: "ok" };
32518
32571
  }
32572
+ async reconcileMasteryForConfigChange(gameId, courseId, user, context) {
32573
+ const { client, appName } = await this.resolveAdminMutationContext(gameId, courseId, user);
32574
+ const action = context.newMasterableUnits < context.oldMasterableUnits ? "complete" : "revoke";
32575
+ const failed = [];
32576
+ let processed = 0;
32577
+ await TimebackAdminService.runWithConcurrency(context.affectedStudentIds, 8, async (studentId) => {
32578
+ try {
32579
+ await upsertMasteryCompletionEntry({
32580
+ client,
32581
+ courseId,
32582
+ studentId,
32583
+ appName,
32584
+ action
32585
+ });
32586
+ processed++;
32587
+ } catch (error) {
32588
+ logger18.warn("Failed to reconcile mastery completion for student", {
32589
+ studentId,
32590
+ courseId,
32591
+ action,
32592
+ error: error instanceof Error ? error.message : String(error)
32593
+ });
32594
+ failed.push(studentId);
32595
+ }
32596
+ });
32597
+ return { processed, failed };
32598
+ }
32519
32599
  async searchStudentsForEnrollment(gameId, courseId, query, user) {
32520
32600
  const client = this.requireClient();
32521
32601
  await this.deps.validateGameManagementAccess(user, gameId);
@@ -32542,7 +32622,7 @@ class TimebackAdminService {
32542
32622
  const response = await client["request"](endpoint, "GET");
32543
32623
  allUsers = response.users || [];
32544
32624
  } catch (error) {
32545
- logger17.warn("Failed to search OneRoster users", {
32625
+ logger18.warn("Failed to search OneRoster users", {
32546
32626
  query: trimmedQuery,
32547
32627
  error: error instanceof Error ? error.message : String(error)
32548
32628
  });
@@ -32687,7 +32767,7 @@ class TimebackAdminService {
32687
32767
  return results;
32688
32768
  }
32689
32769
  }
32690
- var logger17;
32770
+ var logger18;
32691
32771
  var init_timeback_admin_service = __esm(() => {
32692
32772
  init_drizzle_orm();
32693
32773
  init_src();
@@ -32700,8 +32780,9 @@ var init_timeback_admin_service = __esm(() => {
32700
32780
  init_errors();
32701
32781
  init_timeback_admin_metrics_util();
32702
32782
  init_timeback_admin_util();
32783
+ init_timeback_mastery_completion_util();
32703
32784
  init_timeback_util();
32704
- logger17 = log.scope("TimebackAdminService");
32785
+ logger18 = log.scope("TimebackAdminService");
32705
32786
  });
32706
32787
 
32707
32788
  // ../timeback/dist/errors.js
@@ -32948,7 +33029,7 @@ class TimebackAssessmentsService {
32948
33029
  isActive: row.bankActive
32949
33030
  };
32950
33031
  } catch {
32951
- logger18.warn("Failed to fetch QTI test metadata", {
33032
+ logger19.warn("Failed to fetch QTI test metadata", {
32952
33033
  identifier: row.qtiTestIdentifier
32953
33034
  });
32954
33035
  return {
@@ -33002,7 +33083,7 @@ class TimebackAssessmentsService {
33002
33083
  });
33003
33084
  } catch (error) {
33004
33085
  if (error instanceof TimebackApiError && error.status === 409) {
33005
- logger18.info("QTI test already exists (idempotent retry)", {
33086
+ logger19.info("QTI test already exists (idempotent retry)", {
33006
33087
  qtiTestIdentifier: input.qtiTestIdentifier
33007
33088
  });
33008
33089
  } else {
@@ -33015,7 +33096,7 @@ class TimebackAssessmentsService {
33015
33096
  qtiTestIdentifier: input.qtiTestIdentifier,
33016
33097
  sortOrder: maxSortOrder + 1
33017
33098
  }).returning();
33018
- logger18.info("Assessment created", {
33099
+ logger19.info("Assessment created", {
33019
33100
  integrationId,
33020
33101
  qtiTestIdentifier: input.qtiTestIdentifier
33021
33102
  });
@@ -33037,13 +33118,13 @@ class TimebackAssessmentsService {
33037
33118
  }
33038
33119
  await client.qti.tests.delete(qtiTestIdentifier);
33039
33120
  } catch (error) {
33040
- logger18.warn("Partial QTI cleanup during assessment deletion", {
33121
+ logger19.warn("Partial QTI cleanup during assessment deletion", {
33041
33122
  qtiTestIdentifier,
33042
33123
  error: error instanceof Error ? error.message : String(error)
33043
33124
  });
33044
33125
  }
33045
33126
  await this.deps.db.delete(gameTimebackAssessmentTests).where(eq(gameTimebackAssessmentTests.id, row.id));
33046
- logger18.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33127
+ logger19.info("Assessment deleted", { integrationId, qtiTestIdentifier });
33047
33128
  }
33048
33129
  async reorderAssessments(integrationId, identifiers) {
33049
33130
  await this.requireIntegration(integrationId);
@@ -33088,7 +33169,7 @@ class TimebackAssessmentsService {
33088
33169
  return client.qti.tests.reorderItems(qtiTestIdentifier, partId, sectionId, items2);
33089
33170
  }
33090
33171
  async activateAssessment(integrationId, qtiTestIdentifier) {
33091
- logger18.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33172
+ logger19.debug("Activating assessment", { integrationId, qtiTestIdentifier });
33092
33173
  const client = this.requireClient();
33093
33174
  const integration = await this.requireIntegration(integrationId);
33094
33175
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33126,7 +33207,7 @@ class TimebackAssessmentsService {
33126
33207
  });
33127
33208
  }
33128
33209
  } catch {
33129
- logger18.warn("Failed to reactivate existing child resource, will create new", {
33210
+ logger19.warn("Failed to reactivate existing child resource, will create new", {
33130
33211
  childResourceId
33131
33212
  });
33132
33213
  childResourceId = null;
@@ -33139,7 +33220,7 @@ class TimebackAssessmentsService {
33139
33220
  const resourceUrl = resource.metadata?.url;
33140
33221
  if (resourceUrl === qtiTestUrl) {
33141
33222
  childResourceId = resourceId;
33142
- logger18.info("Found existing child Resource for QTI test (idempotent retry)", {
33223
+ logger19.info("Found existing child Resource for QTI test (idempotent retry)", {
33143
33224
  qtiTestIdentifier,
33144
33225
  childResourceId
33145
33226
  });
@@ -33148,7 +33229,7 @@ class TimebackAssessmentsService {
33148
33229
  } catch {}
33149
33230
  }
33150
33231
  if (!childResourceId) {
33151
- logger18.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33232
+ logger19.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
33152
33233
  const childResult = await client.oneroster.resources.create({
33153
33234
  resource: {
33154
33235
  status: "active",
@@ -33176,10 +33257,10 @@ class TimebackAssessmentsService {
33176
33257
  }
33177
33258
  }
33178
33259
  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 });
33260
+ logger19.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
33180
33261
  }
33181
33262
  async deactivateAssessment(integrationId, qtiTestIdentifier) {
33182
- logger18.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33263
+ logger19.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
33183
33264
  const client = this.requireClient();
33184
33265
  const integration = await this.requireIntegration(integrationId);
33185
33266
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -33192,7 +33273,7 @@ class TimebackAssessmentsService {
33192
33273
  const childResourceId = row.bankResourceId;
33193
33274
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33194
33275
  try {
33195
- logger18.debug("Reading parent resource for deactivation", {
33276
+ logger19.debug("Reading parent resource for deactivation", {
33196
33277
  resourceId: bankIds.resource
33197
33278
  });
33198
33279
  const parentResource = await client.oneroster.resources.get(bankIds.resource);
@@ -33211,22 +33292,22 @@ class TimebackAssessmentsService {
33211
33292
  });
33212
33293
  }
33213
33294
  } catch (error) {
33214
- logger18.warn("Failed to update parent resource during deactivation", {
33295
+ logger19.warn("Failed to update parent resource during deactivation", {
33215
33296
  bankResourceId: bankIds.resource,
33216
33297
  error: error instanceof Error ? error.message : String(error)
33217
33298
  });
33218
33299
  }
33219
33300
  try {
33220
- logger18.debug("Deleting child resource", { childResourceId });
33301
+ logger19.debug("Deleting child resource", { childResourceId });
33221
33302
  await client.oneroster.resources.delete(childResourceId);
33222
33303
  } catch (error) {
33223
- logger18.warn("Failed to delete child resource (may already be deleted)", {
33304
+ logger19.warn("Failed to delete child resource (may already be deleted)", {
33224
33305
  childResourceId,
33225
33306
  error: error instanceof Error ? error.message : String(error)
33226
33307
  });
33227
33308
  }
33228
33309
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankActive: false }).where(eq(gameTimebackAssessmentTests.id, row.id));
33229
- logger18.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33310
+ logger19.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
33230
33311
  }
33231
33312
  isAssessmentActive(row) {
33232
33313
  return row.bankActive;
@@ -33257,14 +33338,14 @@ class TimebackAssessmentsService {
33257
33338
  status: "tobedeleted"
33258
33339
  });
33259
33340
  } catch (error) {
33260
- logger18.warn("Failed to delete component resource", { error });
33341
+ logger19.warn("Failed to delete component resource", { error });
33261
33342
  }
33262
33343
  for (const row of activeRows) {
33263
33344
  if (row.bankResourceId) {
33264
33345
  try {
33265
33346
  await client.oneroster.resources.delete(row.bankResourceId);
33266
33347
  } catch (error) {
33267
- logger18.warn("Failed to delete child resource", {
33348
+ logger19.warn("Failed to delete child resource", {
33268
33349
  childResourceId: row.bankResourceId,
33269
33350
  error
33270
33351
  });
@@ -33274,17 +33355,17 @@ class TimebackAssessmentsService {
33274
33355
  try {
33275
33356
  await client.oneroster.resources.delete(bankIds.resource);
33276
33357
  } catch (error) {
33277
- logger18.warn("Failed to delete parent resource", { error });
33358
+ logger19.warn("Failed to delete parent resource", { error });
33278
33359
  }
33279
33360
  try {
33280
33361
  await client.oneroster.courseComponents.update(bankIds.component, {
33281
33362
  status: "tobedeleted"
33282
33363
  });
33283
33364
  } catch (error) {
33284
- logger18.warn("Failed to delete course component", { error });
33365
+ logger19.warn("Failed to delete course component", { error });
33285
33366
  }
33286
33367
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: null, bankActive: false }).where(eq(gameTimebackAssessmentTests.integrationId, integrationId));
33287
- logger18.info("Bank destroyed", { integrationId });
33368
+ logger19.info("Bank destroyed", { integrationId });
33288
33369
  }
33289
33370
  requireClient() {
33290
33371
  if (!this.deps.timeback) {
@@ -33313,7 +33394,7 @@ class TimebackAssessmentsService {
33313
33394
  async ensureBank(integration) {
33314
33395
  const client = this.requireClient();
33315
33396
  const bankIds = deriveAssessmentBankIds(integration.courseId);
33316
- logger18.debug("Ensuring assessment bank hierarchy", {
33397
+ logger19.debug("Ensuring assessment bank hierarchy", {
33317
33398
  courseId: integration.courseId,
33318
33399
  bankIds
33319
33400
  });
@@ -33330,7 +33411,7 @@ class TimebackAssessmentsService {
33330
33411
  });
33331
33412
  } catch (error) {
33332
33413
  if (error instanceof TimebackApiError && error.status === 409) {
33333
- logger18.debug("Course component already exists", { sourcedId: bankIds.component });
33414
+ logger19.debug("Course component already exists", { sourcedId: bankIds.component });
33334
33415
  } else {
33335
33416
  throw error;
33336
33417
  }
@@ -33354,7 +33435,7 @@ class TimebackAssessmentsService {
33354
33435
  });
33355
33436
  } catch (error) {
33356
33437
  if (error instanceof TimebackApiError && error.status === 409) {
33357
- logger18.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33438
+ logger19.debug("Parent resource already exists", { sourcedId: bankIds.resource });
33358
33439
  } else {
33359
33440
  throw error;
33360
33441
  }
@@ -33374,14 +33455,14 @@ class TimebackAssessmentsService {
33374
33455
  });
33375
33456
  } catch (error) {
33376
33457
  if (error instanceof TimebackApiError && error.status === 409) {
33377
- logger18.debug("Component resource already exists", {
33458
+ logger19.debug("Component resource already exists", {
33378
33459
  sourcedId: bankIds.componentResource
33379
33460
  });
33380
33461
  } else {
33381
33462
  throw error;
33382
33463
  }
33383
33464
  }
33384
- logger18.info("Assessment bank hierarchy created", {
33465
+ logger19.info("Assessment bank hierarchy created", {
33385
33466
  courseId: integration.courseId
33386
33467
  });
33387
33468
  }
@@ -33396,7 +33477,7 @@ class TimebackAssessmentsService {
33396
33477
  return Math.max(...rows.map((r) => r.sortOrder));
33397
33478
  }
33398
33479
  }
33399
- var logger18;
33480
+ var logger19;
33400
33481
  var init_timeback_assessments_service = __esm(() => {
33401
33482
  init_drizzle_orm();
33402
33483
  init_tables_index();
@@ -33405,7 +33486,7 @@ var init_timeback_assessments_service = __esm(() => {
33405
33486
  init_errors4();
33406
33487
  init_utils6();
33407
33488
  init_errors();
33408
- logger18 = log.scope("TimebackAssessmentsService");
33489
+ logger19 = log.scope("TimebackAssessmentsService");
33409
33490
  });
33410
33491
 
33411
33492
  // ../api-core/src/utils/timeback-promotion.util.ts
@@ -33421,7 +33502,7 @@ async function promoteCompletedCourse({
33421
33502
  });
33422
33503
  const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
33423
33504
  if (!nextIntegration) {
33424
- logger19.debug("Skipping promotion because no next course is configured", {
33505
+ logger20.debug("Skipping promotion because no next course is configured", {
33425
33506
  gameId: currentIntegration.gameId,
33426
33507
  studentId,
33427
33508
  grade: currentIntegration.grade,
@@ -33438,7 +33519,7 @@ async function promoteCompletedCourse({
33438
33519
  const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
33439
33520
  if (!currentEnrollment) {
33440
33521
  if (nextEnrollment) {
33441
- logger19.debug("Skipping promotion because student is already on the next course", {
33522
+ logger20.debug("Skipping promotion because student is already on the next course", {
33442
33523
  gameId: currentIntegration.gameId,
33443
33524
  studentId,
33444
33525
  grade: currentIntegration.grade,
@@ -33452,7 +33533,7 @@ async function promoteCompletedCourse({
33452
33533
  nextCourseId: nextIntegration.courseId
33453
33534
  };
33454
33535
  }
33455
- logger19.debug("Skipping promotion because student is not enrolled in the current course", {
33536
+ logger20.debug("Skipping promotion because student is not enrolled in the current course", {
33456
33537
  gameId: currentIntegration.gameId,
33457
33538
  studentId,
33458
33539
  grade: currentIntegration.grade,
@@ -33483,7 +33564,7 @@ async function promoteCompletedCourse({
33483
33564
  client.invalidateEnrollments(studentId);
33484
33565
  }
33485
33566
  }
33486
- logger19.info("Promoted student to next course", {
33567
+ logger20.info("Promoted student to next course", {
33487
33568
  gameId: currentIntegration.gameId,
33488
33569
  studentId,
33489
33570
  subject: currentIntegration.subject,
@@ -33498,16 +33579,16 @@ async function promoteCompletedCourse({
33498
33579
  nextCourseId: nextIntegration.courseId
33499
33580
  };
33500
33581
  }
33501
- var logger19;
33582
+ var logger20;
33502
33583
  var init_timeback_promotion_util = __esm(() => {
33503
33584
  init_drizzle_orm();
33504
33585
  init_tables_index();
33505
33586
  init_src2();
33506
- logger19 = log.scope("TimebackPromotion");
33587
+ logger20 = log.scope("TimebackPromotion");
33507
33588
  });
33508
33589
 
33509
33590
  // ../api-core/src/services/timeback.service.ts
33510
- var logger20, TimebackService;
33591
+ var logger21, TimebackService;
33511
33592
  var init_timeback_service = __esm(() => {
33512
33593
  init_drizzle_orm();
33513
33594
  init_src();
@@ -33518,7 +33599,7 @@ var init_timeback_service = __esm(() => {
33518
33599
  init_errors();
33519
33600
  init_timeback_promotion_util();
33520
33601
  init_timeback_util();
33521
- logger20 = log.scope("TimebackService");
33602
+ logger21 = log.scope("TimebackService");
33522
33603
  TimebackService = class TimebackService {
33523
33604
  static HEARTBEAT_DEDUPE_TTL_MS = 5 * 60 * 1000;
33524
33605
  static processedHeartbeatWindows = new Map;
@@ -33564,7 +33645,7 @@ var init_timeback_service = __esm(() => {
33564
33645
  }
33565
33646
  requireClient() {
33566
33647
  if (!this.deps.timeback) {
33567
- logger20.error("Timeback client not available in context");
33648
+ logger21.error("Timeback client not available in context");
33568
33649
  throw new ValidationError("Timeback integration not available in this environment");
33569
33650
  }
33570
33651
  return this.deps.timeback;
@@ -33617,7 +33698,7 @@ var init_timeback_service = __esm(() => {
33617
33698
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
33618
33699
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
33619
33700
  if (!result) {
33620
- logger20.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33701
+ logger21.error("Daily XP upsert returned no rows", { userId, date: targetDate });
33621
33702
  throw new InternalError("Failed to update daily XP record");
33622
33703
  }
33623
33704
  return { xp: result.xp, date: result.date.toISOString() };
@@ -33648,7 +33729,7 @@ var init_timeback_service = __esm(() => {
33648
33729
  columns: { id: true, timebackId: true }
33649
33730
  });
33650
33731
  if (dbUser?.timebackId) {
33651
- logger20.info("Student already onboarded", { userId: user.id });
33732
+ logger21.info("Student already onboarded", { userId: user.id });
33652
33733
  return { status: "already_populated" };
33653
33734
  }
33654
33735
  let timebackId;
@@ -33657,7 +33738,7 @@ var init_timeback_service = __esm(() => {
33657
33738
  const existingUser = await client.oneroster.users.findByEmail(user.email);
33658
33739
  timebackId = existingUser.sourcedId;
33659
33740
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
33660
- logger20.info("Found existing student in OneRoster", {
33741
+ logger21.info("Found existing student in OneRoster", {
33661
33742
  userId: user.id,
33662
33743
  timebackId
33663
33744
  });
@@ -33686,7 +33767,7 @@ var init_timeback_service = __esm(() => {
33686
33767
  }
33687
33768
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
33688
33769
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
33689
- logger20.info("Created student in OneRoster", { userId: user.id, timebackId });
33770
+ logger21.info("Created student in OneRoster", { userId: user.id, timebackId });
33690
33771
  }
33691
33772
  const assessments = await this.fetchAssessments(timebackId);
33692
33773
  await db2.transaction(async (tx) => {
@@ -33720,7 +33801,7 @@ var init_timeback_service = __esm(() => {
33720
33801
  }
33721
33802
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
33722
33803
  if (!updated) {
33723
- logger20.error("User Timeback ID update returned no rows", {
33804
+ logger21.error("User Timeback ID update returned no rows", {
33724
33805
  userId: user.id,
33725
33806
  timebackId
33726
33807
  });
@@ -33744,13 +33825,13 @@ var init_timeback_service = __esm(() => {
33744
33825
  }
33745
33826
  offset += limit;
33746
33827
  }
33747
- logger20.debug("Fetched assessments", {
33828
+ logger21.debug("Fetched assessments", {
33748
33829
  studentSourcedId,
33749
33830
  totalCount: allAssessments.length
33750
33831
  });
33751
33832
  return allAssessments;
33752
33833
  } catch (error) {
33753
- logger20.warn("Failed to fetch assessments", { studentSourcedId, error });
33834
+ logger21.warn("Failed to fetch assessments", { studentSourcedId, error });
33754
33835
  return [];
33755
33836
  }
33756
33837
  }
@@ -33863,7 +33944,7 @@ var init_timeback_service = __esm(() => {
33863
33944
  masterableUnits: derivedMasterableUnits
33864
33945
  } = courseConfig;
33865
33946
  if (!isTimebackSubject(subjectInput)) {
33866
- logger20.warn("Invalid Timeback subject in course config", {
33947
+ logger21.warn("Invalid Timeback subject in course config", {
33867
33948
  subject: subjectInput,
33868
33949
  courseCode,
33869
33950
  title
@@ -33871,7 +33952,7 @@ var init_timeback_service = __esm(() => {
33871
33952
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
33872
33953
  }
33873
33954
  if (!isTimebackGrade(grade)) {
33874
- logger20.warn("Invalid Timeback grade in course config", {
33955
+ logger21.warn("Invalid Timeback grade in course config", {
33875
33956
  grade,
33876
33957
  courseCode,
33877
33958
  title
@@ -33883,7 +33964,7 @@ var init_timeback_service = __esm(() => {
33883
33964
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
33884
33965
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
33885
33966
  if (typeof totalXp !== "number") {
33886
- logger20.warn("Course missing totalXp in Timeback config", {
33967
+ logger21.warn("Course missing totalXp in Timeback config", {
33887
33968
  courseCode,
33888
33969
  title
33889
33970
  });
@@ -33899,7 +33980,9 @@ var init_timeback_service = __esm(() => {
33899
33980
  courseCode,
33900
33981
  level,
33901
33982
  gradingScheme: "STANDARD",
33902
- metadata: metadata2
33983
+ metadata: TimebackService.patchCourseMetadata(metadata2, totalXp, {
33984
+ masterableUnits: masterableUnits ?? null
33985
+ })
33903
33986
  },
33904
33987
  component: {
33905
33988
  ...baseConfig.component,
@@ -33924,7 +34007,7 @@ var init_timeback_service = __esm(() => {
33924
34007
  const existingIntegration = existing.find((i2) => i2.grade === grade && i2.subject === subject);
33925
34008
  if (existingIntegration) {
33926
34009
  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();
34010
+ const [updated] = await db2.update(gameTimebackIntegrations).set({ subject, totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
33928
34011
  if (updated) {
33929
34012
  integrations.push(this.toGameTimebackIntegration(updated));
33930
34013
  }
@@ -33949,6 +34032,60 @@ var init_timeback_service = __esm(() => {
33949
34032
  });
33950
34033
  return rows.map((row) => this.toGameTimebackIntegration(row));
33951
34034
  }
34035
+ async getIntegrationConfig(gameId, courseId, user) {
34036
+ const client = this.requireClient();
34037
+ await this.deps.validateGameManagementAccess(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 config2 = await client.getConfig(courseId);
34045
+ return this.toGameTimebackIntegrationConfig(integration, config2);
34046
+ }
34047
+ async updateIntegration(gameId, courseId, user, patch) {
34048
+ const client = this.requireClient();
34049
+ await this.deps.validateDeveloperAccess(user, gameId);
34050
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34051
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
34052
+ });
34053
+ if (!integration) {
34054
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34055
+ }
34056
+ const timebackConfig = await client.getConfig(courseId);
34057
+ const liveSubject = timebackConfig.course.subjects[0];
34058
+ const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
34059
+ if (!isTimebackSubject(subject)) {
34060
+ throw new ValidationError(`Invalid subject "${subject}"`);
34061
+ }
34062
+ if (subject !== integration.subject) {
34063
+ const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
34064
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
34065
+ });
34066
+ if (subjectConflict && subjectConflict.id !== integration.id) {
34067
+ throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
34068
+ }
34069
+ }
34070
+ const totalXp = "totalXp" in patch ? patch.totalXp ?? null : TimebackService.getTotalXpFromConfig(timebackConfig) ?? integration.totalXp ?? null;
34071
+ const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : TimebackService.getMasterableUnitsFromConfig(timebackConfig);
34072
+ await client.update(courseId, TimebackService.patchTimebackConfig(timebackConfig, {
34073
+ ...patch,
34074
+ subject,
34075
+ totalXp,
34076
+ masterableUnits,
34077
+ grade: integration.grade
34078
+ }));
34079
+ const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
34080
+ subject,
34081
+ totalXp,
34082
+ updatedAt: new Date
34083
+ }).where(eq(gameTimebackIntegrations.id, integration.id)).returning();
34084
+ if (!updated) {
34085
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
34086
+ }
34087
+ return this.toGameTimebackIntegration(updated);
34088
+ }
33952
34089
  async verifyIntegration(gameId, user) {
33953
34090
  const client = this.requireClient();
33954
34091
  const db2 = this.deps.db;
@@ -34006,6 +34143,146 @@ var init_timeback_service = __esm(() => {
34006
34143
  }
34007
34144
  await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
34008
34145
  }
34146
+ static getMasterableUnitsFromConfig(config2) {
34147
+ const playcademyMetadata = config2.resource.metadata?.playcademy;
34148
+ if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
34149
+ return null;
34150
+ }
34151
+ return playcademyMetadata?.mastery?.masterableUnits ?? null;
34152
+ }
34153
+ static getTotalXpFromConfig(config2) {
34154
+ const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
34155
+ if (typeof courseMetadata?.metrics?.totalXp === "number") {
34156
+ return courseMetadata.metrics.totalXp;
34157
+ }
34158
+ const resourceMetadata = config2.resource.metadata;
34159
+ if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
34160
+ return resourceMetadata.xp;
34161
+ }
34162
+ return null;
34163
+ }
34164
+ static patchCourseMetadata(metadata2, totalXp, options) {
34165
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34166
+ const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
34167
+ const metrics = { ...currentMetrics };
34168
+ if (totalXp === null) {
34169
+ delete metrics.totalXp;
34170
+ } else {
34171
+ metrics.totalXp = totalXp;
34172
+ }
34173
+ if (options?.masterableUnits !== undefined) {
34174
+ if (options.masterableUnits === null) {
34175
+ delete metrics.totalLessons;
34176
+ } else {
34177
+ metrics.totalLessons = options.masterableUnits;
34178
+ }
34179
+ }
34180
+ metrics.totalGrades = 1;
34181
+ if (Object.keys(metrics).length > 0) {
34182
+ nextMetadata.metrics = metrics;
34183
+ } else {
34184
+ delete nextMetadata.metrics;
34185
+ }
34186
+ const goals = options?.goals;
34187
+ if (goals !== undefined) {
34188
+ if (goals === null) {
34189
+ delete nextMetadata.goals;
34190
+ } else {
34191
+ const currentGoals = isRecord2(nextMetadata.goals) ? nextMetadata.goals : {};
34192
+ const nextGoals = { ...currentGoals };
34193
+ for (const [key, value] of Object.entries(goals)) {
34194
+ if (value === null) {
34195
+ delete nextGoals[key];
34196
+ } else if (value !== undefined) {
34197
+ nextGoals[key] = value;
34198
+ }
34199
+ }
34200
+ if (Object.keys(nextGoals).length > 0) {
34201
+ nextMetadata.goals = nextGoals;
34202
+ } else {
34203
+ delete nextMetadata.goals;
34204
+ }
34205
+ }
34206
+ }
34207
+ if (options?.publishStatus !== undefined) {
34208
+ if (options.publishStatus === null) {
34209
+ delete nextMetadata.publishStatus;
34210
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34211
+ delete alphaLearn.publishStatus;
34212
+ if (Object.keys(alphaLearn).length > 0) {
34213
+ nextMetadata.AlphaLearn = alphaLearn;
34214
+ } else {
34215
+ delete nextMetadata.AlphaLearn;
34216
+ }
34217
+ } else {
34218
+ nextMetadata.publishStatus = options.publishStatus;
34219
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
34220
+ alphaLearn.publishStatus = options.publishStatus === "published" ? "active" : options.publishStatus;
34221
+ nextMetadata.AlphaLearn = alphaLearn;
34222
+ }
34223
+ }
34224
+ if (options?.isSupplemental !== undefined) {
34225
+ nextMetadata.isSupplemental = options.isSupplemental;
34226
+ }
34227
+ if (options?.timebackVisible !== undefined) {
34228
+ if (options.timebackVisible === null) {
34229
+ delete nextMetadata.timebackVisible;
34230
+ } else {
34231
+ nextMetadata.timebackVisible = options.timebackVisible;
34232
+ }
34233
+ }
34234
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
34235
+ }
34236
+ static patchResourceMetadata(metadata2, options) {
34237
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
34238
+ const playcademyMetadata = isRecord2(nextMetadata.playcademy) ? { ...nextMetadata.playcademy } : {};
34239
+ const masteryMetadata = isRecord2(playcademyMetadata.mastery) ? { ...playcademyMetadata.mastery } : {};
34240
+ nextMetadata.subject = options.subject;
34241
+ nextMetadata.grades = [options.grade];
34242
+ if (options.totalXp === null) {
34243
+ delete nextMetadata.xp;
34244
+ } else {
34245
+ nextMetadata.xp = options.totalXp;
34246
+ }
34247
+ if (options.masterableUnits === null) {
34248
+ delete masteryMetadata.masterableUnits;
34249
+ } else {
34250
+ masteryMetadata.masterableUnits = options.masterableUnits;
34251
+ }
34252
+ if (Object.keys(masteryMetadata).length > 0) {
34253
+ playcademyMetadata.mastery = masteryMetadata;
34254
+ } else {
34255
+ delete playcademyMetadata.mastery;
34256
+ }
34257
+ if (Object.keys(playcademyMetadata).length > 0) {
34258
+ nextMetadata.playcademy = playcademyMetadata;
34259
+ } else {
34260
+ delete nextMetadata.playcademy;
34261
+ }
34262
+ return nextMetadata;
34263
+ }
34264
+ static patchTimebackConfig(config2, patch) {
34265
+ return {
34266
+ ...config2,
34267
+ course: {
34268
+ ...config2.course,
34269
+ title: patch.title ?? config2.course.title,
34270
+ courseCode: patch.courseCode ?? config2.course.courseCode,
34271
+ subjects: [patch.subject],
34272
+ metadata: TimebackService.patchCourseMetadata(config2.course.metadata, patch.totalXp, {
34273
+ masterableUnits: patch.masterableUnits,
34274
+ goals: patch.goals,
34275
+ publishStatus: patch.publishStatus,
34276
+ isSupplemental: patch.isSupplemental,
34277
+ timebackVisible: patch.timebackVisible
34278
+ })
34279
+ },
34280
+ resource: {
34281
+ ...config2.resource,
34282
+ metadata: TimebackService.patchResourceMetadata(config2.resource.metadata, patch)
34283
+ }
34284
+ };
34285
+ }
34009
34286
  toGameTimebackIntegration(integration) {
34010
34287
  return {
34011
34288
  id: integration.id,
@@ -34019,6 +34296,21 @@ var init_timeback_service = __esm(() => {
34019
34296
  lastVerifiedAt: integration.lastVerifiedAt ?? null
34020
34297
  };
34021
34298
  }
34299
+ toGameTimebackIntegrationConfig(integration, config2) {
34300
+ const subject = config2.course.subjects[0] ?? integration.subject;
34301
+ if (!isTimebackSubject(subject)) {
34302
+ throw new ValidationError(`Invalid subject "${subject}"`);
34303
+ }
34304
+ return {
34305
+ integration: this.toGameTimebackIntegration(integration),
34306
+ title: config2.course.title,
34307
+ courseCode: config2.course.courseCode,
34308
+ subject,
34309
+ totalXp: TimebackService.getTotalXpFromConfig(config2) ?? integration.totalXp ?? null,
34310
+ masterableUnits: TimebackService.getMasterableUnitsFromConfig(config2),
34311
+ metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
34312
+ };
34313
+ }
34022
34314
  async endActivity({
34023
34315
  gameId,
34024
34316
  studentId,
@@ -34084,7 +34376,7 @@ var init_timeback_service = __esm(() => {
34084
34376
  ...runId ? { runId } : {}
34085
34377
  });
34086
34378
  }
34087
- logger20.info("Recorded activity completion", {
34379
+ logger21.info("Recorded activity completion", {
34088
34380
  gameId,
34089
34381
  courseId: integration.courseId,
34090
34382
  studentId,
@@ -34138,7 +34430,7 @@ var init_timeback_service = __esm(() => {
34138
34430
  masteredUnits: masteryStatus.masteredUnits,
34139
34431
  masterableUnits: masteryStatus.masterableUnits
34140
34432
  };
34141
- logger20.debug("Skipping course advancement because mastery is incomplete", {
34433
+ logger21.debug("Skipping course advancement because mastery is incomplete", {
34142
34434
  gameId,
34143
34435
  studentId,
34144
34436
  subject: currentIntegration.subject,
@@ -34156,7 +34448,7 @@ var init_timeback_service = __esm(() => {
34156
34448
  studentId,
34157
34449
  enrollments
34158
34450
  });
34159
- logger20.info("Manually advanced student", {
34451
+ logger21.info("Manually advanced student", {
34160
34452
  gameId,
34161
34453
  studentId,
34162
34454
  subject: currentIntegration.subject,
@@ -34189,7 +34481,7 @@ var init_timeback_service = __esm(() => {
34189
34481
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
34190
34482
  const effectiveResumeId = resumeId ?? runId;
34191
34483
  if (TimebackService.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
34192
- logger20.debug("Skipping duplicate heartbeat window", {
34484
+ logger21.debug("Skipping duplicate heartbeat window", {
34193
34485
  gameId,
34194
34486
  studentId,
34195
34487
  runId,
@@ -34202,7 +34494,7 @@ var init_timeback_service = __esm(() => {
34202
34494
  await this.deps.validateDeveloperAccess(user, gameId);
34203
34495
  const inFlightHeartbeat = TimebackService.getInFlightHeartbeatWindow(heartbeatWindowKey);
34204
34496
  if (inFlightHeartbeat) {
34205
- logger20.debug("Joining in-flight heartbeat window", {
34497
+ logger21.debug("Joining in-flight heartbeat window", {
34206
34498
  gameId,
34207
34499
  studentId,
34208
34500
  runId,
@@ -34239,7 +34531,7 @@ var init_timeback_service = __esm(() => {
34239
34531
  });
34240
34532
  }
34241
34533
  TimebackService.markHeartbeatWindowProcessed(heartbeatWindowKey);
34242
- logger20.debug("Recorded heartbeat", {
34534
+ logger21.debug("Recorded heartbeat", {
34243
34535
  gameId,
34244
34536
  courseId: integration.courseId,
34245
34537
  studentId,
@@ -34274,7 +34566,7 @@ var init_timeback_service = __esm(() => {
34274
34566
  });
34275
34567
  courseIds = integrations.map((i2) => i2.courseId);
34276
34568
  if (courseIds.length === 0) {
34277
- logger20.debug("No integrations found for game, returning 0 XP", {
34569
+ logger21.debug("No integrations found for game, returning 0 XP", {
34278
34570
  timebackId,
34279
34571
  gameId: options.gameId,
34280
34572
  grade: options.grade,
@@ -34291,7 +34583,7 @@ var init_timeback_service = __esm(() => {
34291
34583
  courseIds: courseIds.length > 0 ? courseIds : undefined,
34292
34584
  include: options?.include
34293
34585
  });
34294
- logger20.debug("Retrieved student XP", {
34586
+ logger21.debug("Retrieved student XP", {
34295
34587
  timebackId,
34296
34588
  gameId: options?.gameId,
34297
34589
  grade: options?.grade,
@@ -34320,15 +34612,15 @@ class UploadService {
34320
34612
  const { fileName, gameId } = request;
34321
34613
  const bucketName = this.deps.uploadBucket;
34322
34614
  if (!bucketName) {
34323
- logger21.error("Upload bucket not configured in environment");
34615
+ logger22.error("Upload bucket not configured in environment");
34324
34616
  throw new ValidationError("Upload bucket not configured");
34325
34617
  }
34326
34618
  await this.deps.validateDeveloperAccess(user, gameId);
34327
34619
  const version2 = ulid();
34328
34620
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
34329
- logger21.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34621
+ logger22.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
34330
34622
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
34331
- logger21.info("Presigned URL generated", {
34623
+ logger22.info("Presigned URL generated", {
34332
34624
  userId: user.id,
34333
34625
  gameId,
34334
34626
  version: version2
@@ -34341,12 +34633,12 @@ class UploadService {
34341
34633
  };
34342
34634
  }
34343
34635
  }
34344
- var logger21;
34636
+ var logger22;
34345
34637
  var init_upload_service = __esm(() => {
34346
34638
  init_node();
34347
34639
  init_src2();
34348
34640
  init_errors();
34349
- logger21 = log.scope("UploadService");
34641
+ logger22 = log.scope("UploadService");
34350
34642
  });
34351
34643
 
34352
34644
  // ../api-core/src/services/factory/platform.ts
@@ -34660,7 +34952,7 @@ class AchievementService {
34660
34952
  results.push(result);
34661
34953
  }
34662
34954
  }
34663
- logger22.debug("Listed current achievements", { userId: user.id, count: results.length });
34955
+ logger23.debug("Listed current achievements", { userId: user.id, count: results.length });
34664
34956
  return results;
34665
34957
  }
34666
34958
  async listHistory(user, limit) {
@@ -34678,14 +34970,14 @@ class AchievementService {
34678
34970
  createdAt: c.createdAt,
34679
34971
  scopeKey: c.scopeKey
34680
34972
  }));
34681
- logger22.debug("Listed achievement history", { userId: user.id, count: results.length });
34973
+ logger23.debug("Listed achievement history", { userId: user.id, count: results.length });
34682
34974
  return results;
34683
34975
  }
34684
34976
  async submitProgress(achievementId, user) {
34685
34977
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
34686
34978
  broadcast: false
34687
34979
  });
34688
- logger22.debug("Submitted progress", {
34980
+ logger23.debug("Submitted progress", {
34689
34981
  userId: user.id,
34690
34982
  achievementId,
34691
34983
  wasNewClaim
@@ -34717,7 +35009,7 @@ class AchievementService {
34717
35009
  rewardCredits
34718
35010
  }).returning();
34719
35011
  if (!newClaim) {
34720
- logger22.error("Achievement claim insert returned no rows", {
35012
+ logger23.error("Achievement claim insert returned no rows", {
34721
35013
  userId,
34722
35014
  achievementId,
34723
35015
  scopeKey
@@ -34726,7 +35018,7 @@ class AchievementService {
34726
35018
  }
34727
35019
  await this.deps.addCredits(userId, rewardCredits);
34728
35020
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
34729
- logger22.info("Awarded achievement", {
35021
+ logger23.info("Awarded achievement", {
34730
35022
  userId,
34731
35023
  achievementId,
34732
35024
  scopeKey,
@@ -34763,7 +35055,7 @@ class AchievementService {
34763
35055
  return { title, body: body2 };
34764
35056
  }
34765
35057
  }
34766
- var logger22;
35058
+ var logger23;
34767
35059
  var init_achievement_service = __esm(() => {
34768
35060
  init_drizzle_orm();
34769
35061
  init_tables_index();
@@ -34772,7 +35064,7 @@ var init_achievement_service = __esm(() => {
34772
35064
  init_errors();
34773
35065
  init_leaderboard_util();
34774
35066
  init_scope_util();
34775
- logger22 = log.scope("AchievementService");
35067
+ logger23 = log.scope("AchievementService");
34776
35068
  });
34777
35069
 
34778
35070
  // ../api-core/src/services/inventory.service.ts
@@ -34800,7 +35092,7 @@ class InventoryService {
34800
35092
  },
34801
35093
  updatedAt: inventoryItems.updatedAt
34802
35094
  }).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 });
35095
+ logger24.debug("Listed inventory", { userId: user.id, count: inventory.length });
34804
35096
  return inventory;
34805
35097
  }
34806
35098
  async addItem(itemId, quantity, user) {
@@ -34817,7 +35109,7 @@ class InventoryService {
34817
35109
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
34818
35110
  return inserted?.quantity ?? 0;
34819
35111
  });
34820
- logger23.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
35112
+ logger24.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
34821
35113
  return { newTotal };
34822
35114
  }
34823
35115
  async removeItem(itemId, quantity, user) {
@@ -34828,7 +35120,7 @@ class InventoryService {
34828
35120
  }
34829
35121
  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
35122
  if (!currentItem) {
34831
- logger23.warn("Insufficient inventory for removal", {
35123
+ logger24.warn("Insufficient inventory for removal", {
34832
35124
  userId: user.id,
34833
35125
  itemId,
34834
35126
  requestedQuantity: quantity
@@ -34838,13 +35130,13 @@ class InventoryService {
34838
35130
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
34839
35131
  return updated?.quantity ?? 0;
34840
35132
  });
34841
- logger23.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
35133
+ logger24.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
34842
35134
  return { newTotal };
34843
35135
  }
34844
35136
  async addCredits(userId, amount) {
34845
35137
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
34846
35138
  if (!creditsItem) {
34847
- logger23.error("Primary currency not found", {
35139
+ logger24.error("Primary currency not found", {
34848
35140
  userId,
34849
35141
  amount
34850
35142
  });
@@ -34857,17 +35149,17 @@ class InventoryService {
34857
35149
  updatedAt: new Date
34858
35150
  }
34859
35151
  });
34860
- logger23.debug("Added credits", { userId, amount });
35152
+ logger24.debug("Added credits", { userId, amount });
34861
35153
  }
34862
35154
  }
34863
- var logger23;
35155
+ var logger24;
34864
35156
  var init_inventory_service = __esm(() => {
34865
35157
  init_drizzle_orm();
34866
35158
  init_src();
34867
35159
  init_tables_index();
34868
35160
  init_src2();
34869
35161
  init_errors();
34870
- logger23 = log.scope("InventoryService");
35162
+ logger24 = log.scope("InventoryService");
34871
35163
  });
34872
35164
 
34873
35165
  // ../api-core/src/services/leaderboard.service.ts
@@ -34899,7 +35191,7 @@ class LeaderboardService {
34899
35191
  sessionId
34900
35192
  }).returning();
34901
35193
  if (!newScore) {
34902
- logger24.error("Score insert returned no rows", { userId, gameId, score: input.score });
35194
+ logger25.error("Score insert returned no rows", { userId, gameId, score: input.score });
34903
35195
  throw new InternalError("Failed to insert score");
34904
35196
  }
34905
35197
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -34927,7 +35219,7 @@ class LeaderboardService {
34927
35219
  movedUpWithinTop3
34928
35220
  });
34929
35221
  }
34930
- logger24.info("Score submitted", {
35222
+ logger25.info("Score submitted", {
34931
35223
  gameId,
34932
35224
  userId,
34933
35225
  isAnonymousUser,
@@ -35004,7 +35296,7 @@ class LeaderboardService {
35004
35296
  });
35005
35297
  }
35006
35298
  } catch (error) {
35007
- logger24.warn("Failed to publish notification", { error });
35299
+ logger25.warn("Failed to publish notification", { error });
35008
35300
  }
35009
35301
  }
35010
35302
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -35123,7 +35415,7 @@ class LeaderboardService {
35123
35415
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
35124
35416
  }
35125
35417
  }
35126
- var logger24;
35418
+ var logger25;
35127
35419
  var init_leaderboard_service = __esm(() => {
35128
35420
  init_drizzle_orm();
35129
35421
  init_src();
@@ -35133,7 +35425,7 @@ var init_leaderboard_service = __esm(() => {
35133
35425
  init_notification();
35134
35426
  init_errors();
35135
35427
  init_leaderboard_util();
35136
- logger24 = log.scope("LeaderboardService");
35428
+ logger25 = log.scope("LeaderboardService");
35137
35429
  });
35138
35430
 
35139
35431
  // ../api-core/src/services/level.service.ts
@@ -35149,9 +35441,9 @@ class LevelService {
35149
35441
  for (const config2 of configs) {
35150
35442
  levelConfigCache.set(config2.level, config2);
35151
35443
  }
35152
- logger25.info("Cache pre-warmed", { count: configs.length });
35444
+ logger26.info("Cache pre-warmed", { count: configs.length });
35153
35445
  } catch (error) {
35154
- logger25.error("Cache pre-warm failed", { error });
35446
+ logger26.error("Cache pre-warm failed", { error });
35155
35447
  }
35156
35448
  }
35157
35449
  async getConfig(level) {
@@ -35185,7 +35477,7 @@ class LevelService {
35185
35477
  totalXP
35186
35478
  }).returning();
35187
35479
  if (!newUserLevel) {
35188
- logger25.error("User level insert returned no rows", { userId: user.id });
35480
+ logger26.error("User level insert returned no rows", { userId: user.id });
35189
35481
  throw new InternalError("Failed to create user level cache record");
35190
35482
  }
35191
35483
  userLevel = newUserLevel;
@@ -35200,7 +35492,7 @@ class LevelService {
35200
35492
  userLevel = updatedUserLevel;
35201
35493
  }
35202
35494
  }
35203
- logger25.debug("Retrieved user level", {
35495
+ logger26.debug("Retrieved user level", {
35204
35496
  userId: user.id,
35205
35497
  totalXP,
35206
35498
  currentLevel,
@@ -35211,7 +35503,7 @@ class LevelService {
35211
35503
  async getProgress(user) {
35212
35504
  const userLevel = await this.getByUser(user);
35213
35505
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
35214
- logger25.debug("Retrieved progress", { userId: user.id });
35506
+ logger26.debug("Retrieved progress", { userId: user.id });
35215
35507
  return {
35216
35508
  level: userLevel.currentLevel,
35217
35509
  currentXp: userLevel.currentXp,
@@ -35245,7 +35537,7 @@ class LevelService {
35245
35537
  if (leveledUp && previousUserLevel) {
35246
35538
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
35247
35539
  }
35248
- logger25.info("Synced from Timeback", {
35540
+ logger26.info("Synced from Timeback", {
35249
35541
  userId,
35250
35542
  totalXP,
35251
35543
  currentLevel,
@@ -35286,7 +35578,7 @@ class LevelService {
35286
35578
  }
35287
35579
  if (totalCredits > 0) {
35288
35580
  await this.deps.addCredits(userId, totalCredits);
35289
- logger25.info("Awarded level-up credits", {
35581
+ logger26.info("Awarded level-up credits", {
35290
35582
  userId,
35291
35583
  fromLevel,
35292
35584
  toLevel,
@@ -35324,14 +35616,14 @@ class LevelService {
35324
35616
  };
35325
35617
  }
35326
35618
  }
35327
- var logger25, levelConfigCache = null;
35619
+ var logger26, levelConfigCache = null;
35328
35620
  var init_level_service = __esm(() => {
35329
35621
  init_drizzle_orm();
35330
35622
  init_src();
35331
35623
  init_tables_index();
35332
35624
  init_src2();
35333
35625
  init_errors();
35334
- logger25 = log.scope("LevelService");
35626
+ logger26 = log.scope("LevelService");
35335
35627
  });
35336
35628
 
35337
35629
  // ../realtime/src/server/domain/events.ts
@@ -35352,13 +35644,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
35352
35644
  });
35353
35645
  if (!res.ok) {
35354
35646
  const text3 = await res.text().catch(() => "");
35355
- logger26.warn("Failed to publish to user", {
35647
+ logger27.warn("Failed to publish to user", {
35356
35648
  status: res.status,
35357
35649
  body: text3
35358
35650
  });
35359
35651
  }
35360
35652
  } catch (error) {
35361
- logger26.error("Publish to user error", { error });
35653
+ logger27.error("Publish to user error", { error });
35362
35654
  }
35363
35655
  }
35364
35656
 
@@ -35380,7 +35672,7 @@ class NotificationService {
35380
35672
  conditions2.push(eq(notifications.type, type));
35381
35673
  }
35382
35674
  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 });
35675
+ logger27.debug("Listed notifications", { userId: user.id, count: results.length });
35384
35676
  return results;
35385
35677
  }
35386
35678
  async updateStatus(notificationId, status, method) {
@@ -35399,7 +35691,7 @@ class NotificationService {
35399
35691
  if (!updated) {
35400
35692
  throw new NotFoundError("Notification", notificationId);
35401
35693
  }
35402
- logger26.debug("Updated status", { notificationId, status });
35694
+ logger27.debug("Updated status", { notificationId, status });
35403
35695
  return updated;
35404
35696
  }
35405
35697
  async getStats(user, options) {
@@ -35425,7 +35717,7 @@ class NotificationService {
35425
35717
  const clicked = statsMap.clicked || 0;
35426
35718
  const dismissed = statsMap.dismissed || 0;
35427
35719
  const expired = statsMap.expired || 0;
35428
- logger26.debug("Retrieved stats", { userId: user.id, total });
35720
+ logger27.debug("Retrieved stats", { userId: user.id, total });
35429
35721
  return {
35430
35722
  total,
35431
35723
  delivered,
@@ -35474,7 +35766,7 @@ class NotificationService {
35474
35766
  options: { data, clickUrl, metadata: metadata2 }
35475
35767
  });
35476
35768
  }
35477
- logger26.debug("Created notification", {
35769
+ logger27.debug("Created notification", {
35478
35770
  userId,
35479
35771
  type,
35480
35772
  id: notificationId,
@@ -35482,7 +35774,7 @@ class NotificationService {
35482
35774
  });
35483
35775
  return notificationId;
35484
35776
  } catch (error) {
35485
- logger26.error("Failed to create notification", { userId, type, error });
35777
+ logger27.error("Failed to create notification", { userId, type, error });
35486
35778
  return null;
35487
35779
  }
35488
35780
  }
@@ -35496,7 +35788,7 @@ class NotificationService {
35496
35788
  }) {
35497
35789
  const realtimeConfig = this.deps.realtime;
35498
35790
  if (!realtimeConfig) {
35499
- logger26.warn("No realtime config for publish");
35791
+ logger27.warn("No realtime config for publish");
35500
35792
  return;
35501
35793
  }
35502
35794
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35538,13 +35830,13 @@ class NotificationService {
35538
35830
  metadata: data.metadata || {}
35539
35831
  }).returning();
35540
35832
  if (!notification) {
35541
- logger26.error("Notification insert returned no rows", {
35833
+ logger27.error("Notification insert returned no rows", {
35542
35834
  userId: data.userId,
35543
35835
  type: data.type
35544
35836
  });
35545
35837
  throw new InternalError("Failed to create notification");
35546
35838
  }
35547
- logger26.info("Inserted notification", {
35839
+ logger27.info("Inserted notification", {
35548
35840
  notificationId: notification.id,
35549
35841
  userId: notification.userId,
35550
35842
  type: notification.type
@@ -35554,7 +35846,7 @@ class NotificationService {
35554
35846
  async deliverPending(userId) {
35555
35847
  const realtimeConfig = this.deps.realtime;
35556
35848
  if (!realtimeConfig) {
35557
- logger26.warn("No realtime config for delivery");
35849
+ logger27.warn("No realtime config for delivery");
35558
35850
  return;
35559
35851
  }
35560
35852
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -35581,13 +35873,13 @@ class NotificationService {
35581
35873
  metadata: notification.metadata,
35582
35874
  clickUrl: notification.clickUrl
35583
35875
  });
35584
- logger26.info("Delivered notification", {
35876
+ logger27.info("Delivered notification", {
35585
35877
  notificationId: notification.id,
35586
35878
  userId,
35587
35879
  type: notification.type
35588
35880
  });
35589
35881
  } catch (error) {
35590
- logger26.warn("Failed to deliver", {
35882
+ logger27.warn("Failed to deliver", {
35591
35883
  notificationId: notification.id,
35592
35884
  error
35593
35885
  });
@@ -35595,7 +35887,7 @@ class NotificationService {
35595
35887
  }
35596
35888
  }
35597
35889
  }
35598
- var logger26;
35890
+ var logger27;
35599
35891
  var init_notification_service = __esm(() => {
35600
35892
  init_drizzle_orm();
35601
35893
  init_src();
@@ -35604,7 +35896,7 @@ var init_notification_service = __esm(() => {
35604
35896
  init_events();
35605
35897
  init_notification();
35606
35898
  init_errors();
35607
- logger26 = log.scope("NotificationService");
35899
+ logger27 = log.scope("NotificationService");
35608
35900
  });
35609
35901
 
35610
35902
  // ../api-core/src/services/factory/player.ts
@@ -35728,7 +36020,7 @@ class CharacterService {
35728
36020
  createdAt: characterComponents.createdAt,
35729
36021
  updatedAt: characterComponents.updatedAt
35730
36022
  }).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", {
36023
+ logger28.debug("Listed available components", {
35732
36024
  level,
35733
36025
  count: components.length
35734
36026
  });
@@ -35746,7 +36038,7 @@ class CharacterService {
35746
36038
  }
35747
36039
  }
35748
36040
  });
35749
- logger27.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
36041
+ logger28.debug("Retrieved character", { userId: user.id, found: Boolean(pc) });
35750
36042
  return pc ?? null;
35751
36043
  }
35752
36044
  async getByUserId(userId) {
@@ -35761,7 +36053,7 @@ class CharacterService {
35761
36053
  }
35762
36054
  }
35763
36055
  });
35764
- logger27.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
36056
+ logger28.debug("Retrieved character by ID", { userId, found: Boolean(pc) });
35765
36057
  return pc ?? null;
35766
36058
  }
35767
36059
  async create(input, user) {
@@ -35776,13 +36068,13 @@ class CharacterService {
35776
36068
  }
35777
36069
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
35778
36070
  if (!characterRow) {
35779
- logger27.error("Character insert returned no rows", { userId: user.id });
36071
+ logger28.error("Character insert returned no rows", { userId: user.id });
35780
36072
  throw new InternalError("Failed to create character in database");
35781
36073
  }
35782
36074
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
35783
36075
  return characterRow;
35784
36076
  });
35785
- logger27.info("Created character", { userId: user.id, characterId: result.id });
36077
+ logger28.info("Created character", { userId: user.id, characterId: result.id });
35786
36078
  return result;
35787
36079
  }
35788
36080
  async update(input, user) {
@@ -35794,7 +36086,7 @@ class CharacterService {
35794
36086
  if (!row) {
35795
36087
  throw new NotFoundError("Player character");
35796
36088
  }
35797
- logger27.info("Updated character", {
36089
+ logger28.info("Updated character", {
35798
36090
  userId: user.id,
35799
36091
  characterId: row.id,
35800
36092
  updatedFields: Object.keys(input)
@@ -35816,7 +36108,7 @@ class CharacterService {
35816
36108
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
35817
36109
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
35818
36110
  if (!validation.isValid) {
35819
- logger27.warn("Accessory validation failed", {
36111
+ logger28.warn("Accessory validation failed", {
35820
36112
  userId: user.id,
35821
36113
  slot,
35822
36114
  accessoryComponentId,
@@ -35832,14 +36124,14 @@ class CharacterService {
35832
36124
  slot
35833
36125
  }).returning();
35834
36126
  if (!result) {
35835
- logger27.error("Accessory insert returned no rows", {
36127
+ logger28.error("Accessory insert returned no rows", {
35836
36128
  userId: user.id,
35837
36129
  slot,
35838
36130
  accessoryComponentId
35839
36131
  });
35840
36132
  throw new InternalError("Failed to equip accessory");
35841
36133
  }
35842
- logger27.info("Equipped accessory", {
36134
+ logger28.info("Equipped accessory", {
35843
36135
  userId: user.id,
35844
36136
  slot,
35845
36137
  accessoryComponentId
@@ -35860,7 +36152,7 @@ class CharacterService {
35860
36152
  const playerLevel = userLevel?.currentLevel ?? 1;
35861
36153
  const validation = validateAccessoryRemoval(slot, playerLevel);
35862
36154
  if (!validation.isValid) {
35863
- logger27.warn("Accessory removal validation failed", {
36155
+ logger28.warn("Accessory removal validation failed", {
35864
36156
  userId: user.id,
35865
36157
  slot,
35866
36158
  playerLevel,
@@ -35869,17 +36161,17 @@ class CharacterService {
35869
36161
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
35870
36162
  }
35871
36163
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
35872
- logger27.info("Removed accessory", { userId: user.id, slot });
36164
+ logger28.info("Removed accessory", { userId: user.id, slot });
35873
36165
  }
35874
36166
  }
35875
- var logger27;
36167
+ var logger28;
35876
36168
  var init_character_service = __esm(() => {
35877
36169
  init_drizzle_orm();
35878
36170
  init_tables_index();
35879
36171
  init_src2();
35880
36172
  init_errors();
35881
36173
  init_accessory_util();
35882
- logger27 = log.scope("CharacterService");
36174
+ logger28 = log.scope("CharacterService");
35883
36175
  });
35884
36176
 
35885
36177
  // ../api-core/src/services/currency.service.ts
@@ -35891,7 +36183,7 @@ class CurrencyService {
35891
36183
  async list() {
35892
36184
  const db2 = this.deps.db;
35893
36185
  const allCurrencies = await db2.query.currencies.findMany();
35894
- logger28.debug("Listed currencies", { count: allCurrencies.length });
36186
+ logger29.debug("Listed currencies", { count: allCurrencies.length });
35895
36187
  return allCurrencies;
35896
36188
  }
35897
36189
  async getById(currencyId) {
@@ -35902,7 +36194,7 @@ class CurrencyService {
35902
36194
  if (!currency) {
35903
36195
  throw new NotFoundError("Currency", currencyId);
35904
36196
  }
35905
- logger28.debug("Retrieved currency", { currencyId });
36197
+ logger29.debug("Retrieved currency", { currencyId });
35906
36198
  return currency;
35907
36199
  }
35908
36200
  async create(data) {
@@ -35910,13 +36202,13 @@ class CurrencyService {
35910
36202
  try {
35911
36203
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
35912
36204
  if (!newCurrency) {
35913
- logger28.error("Currency insert returned no rows", {
36205
+ logger29.error("Currency insert returned no rows", {
35914
36206
  itemId: data.itemId,
35915
36207
  symbol: data.symbol
35916
36208
  });
35917
36209
  throw new InternalError("Failed to create currency");
35918
36210
  }
35919
- logger28.info("Created currency", {
36211
+ logger29.info("Created currency", {
35920
36212
  currencyId: newCurrency.id,
35921
36213
  itemId: newCurrency.itemId,
35922
36214
  symbol: newCurrency.symbol,
@@ -35945,7 +36237,7 @@ class CurrencyService {
35945
36237
  if (!updatedCurrency) {
35946
36238
  throw new NotFoundError("Currency", currencyId);
35947
36239
  }
35948
- logger28.info("Updated currency", {
36240
+ logger29.info("Updated currency", {
35949
36241
  currencyId: updatedCurrency.id,
35950
36242
  updatedFields: Object.keys(data)
35951
36243
  });
@@ -35971,16 +36263,16 @@ class CurrencyService {
35971
36263
  if (result.length === 0) {
35972
36264
  throw new NotFoundError("Currency", currencyId);
35973
36265
  }
35974
- logger28.info("Deleted currency", { currencyId });
36266
+ logger29.info("Deleted currency", { currencyId });
35975
36267
  }
35976
36268
  }
35977
- var logger28;
36269
+ var logger29;
35978
36270
  var init_currency_service = __esm(() => {
35979
36271
  init_drizzle_orm();
35980
36272
  init_tables_index();
35981
36273
  init_src2();
35982
36274
  init_errors();
35983
- logger28 = log.scope("CurrencyService");
36275
+ logger29 = log.scope("CurrencyService");
35984
36276
  });
35985
36277
 
35986
36278
  // ../api-core/src/services/logs.service.ts
@@ -35999,11 +36291,11 @@ class LogsService {
35999
36291
  if (!game) {
36000
36292
  throw new NotFoundError("Game", slug2);
36001
36293
  }
36002
- logger29.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
36294
+ logger30.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
36003
36295
  } else {
36004
36296
  const isApprovedDev = user.developerStatus === "approved";
36005
36297
  if (!isApprovedDev) {
36006
- logger29.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
36298
+ logger30.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
36007
36299
  throw new AccessDeniedError("Must be an approved developer");
36008
36300
  }
36009
36301
  const game = await db2.query.games.findFirst({
@@ -36018,7 +36310,7 @@ class LogsService {
36018
36310
  columns: { id: true }
36019
36311
  });
36020
36312
  if (!membership) {
36021
- logger29.warn("Developer attempted access to unowned game logs", {
36313
+ logger30.warn("Developer attempted access to unowned game logs", {
36022
36314
  userId: user.id,
36023
36315
  slug: slug2
36024
36316
  });
@@ -36027,7 +36319,7 @@ class LogsService {
36027
36319
  }
36028
36320
  const workerId = getDeploymentId(slug2, sstStage);
36029
36321
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
36030
- logger29.debug("Generated log stream token", {
36322
+ logger30.debug("Generated log stream token", {
36031
36323
  userId: user.id,
36032
36324
  slug: slug2,
36033
36325
  workerId
@@ -36035,14 +36327,14 @@ class LogsService {
36035
36327
  return { token, workerId };
36036
36328
  }
36037
36329
  }
36038
- var logger29;
36330
+ var logger30;
36039
36331
  var init_logs_service = __esm(() => {
36040
36332
  init_drizzle_orm();
36041
36333
  init_tables_index();
36042
36334
  init_src2();
36043
36335
  init_errors();
36044
36336
  init_deployment_util();
36045
- logger29 = log.scope("LogsService");
36337
+ logger30 = log.scope("LogsService");
36046
36338
  });
36047
36339
 
36048
36340
  // ../api-core/src/services/lti.service.ts
@@ -36095,7 +36387,7 @@ class MapService {
36095
36387
  if (!mapDetails) {
36096
36388
  throw new NotFoundError("Map", identifier);
36097
36389
  }
36098
- logger30.debug("Retrieved map", { identifier });
36390
+ logger31.debug("Retrieved map", { identifier });
36099
36391
  return mapDetails;
36100
36392
  }
36101
36393
  async getElements(mapId) {
@@ -36111,7 +36403,7 @@ class MapService {
36111
36403
  }
36112
36404
  }
36113
36405
  });
36114
- logger30.debug("Retrieved elements", { mapId, count: elements.length });
36406
+ logger31.debug("Retrieved elements", { mapId, count: elements.length });
36115
36407
  return elements;
36116
36408
  }
36117
36409
  async getObjects(mapId, userId) {
@@ -36132,7 +36424,7 @@ class MapService {
36132
36424
  }
36133
36425
  }
36134
36426
  });
36135
- logger30.debug("Retrieved objects", { mapId, userId, count: objects.length });
36427
+ logger31.debug("Retrieved objects", { mapId, userId, count: objects.length });
36136
36428
  return objects.map((object) => this.formatMapObjectWithItem(object));
36137
36429
  }
36138
36430
  async createObject(mapId, data, user) {
@@ -36159,7 +36451,7 @@ class MapService {
36159
36451
  throw new NotFoundError("Item", data.itemId);
36160
36452
  }
36161
36453
  if (!item.isPlaceable) {
36162
- logger30.warn("Attempted to place non-placeable item", {
36454
+ logger31.warn("Attempted to place non-placeable item", {
36163
36455
  userId: user.id,
36164
36456
  itemId: data.itemId,
36165
36457
  mapId
@@ -36173,7 +36465,7 @@ class MapService {
36173
36465
  };
36174
36466
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
36175
36467
  if (!createdObject) {
36176
- logger30.error("Map object insert returned no rows", {
36468
+ logger31.error("Map object insert returned no rows", {
36177
36469
  userId: user.id,
36178
36470
  mapId,
36179
36471
  itemId: data.itemId
@@ -36197,12 +36489,12 @@ class MapService {
36197
36489
  }
36198
36490
  });
36199
36491
  if (!objectWithItem) {
36200
- logger30.error("Map object query after insert returned no rows", {
36492
+ logger31.error("Map object query after insert returned no rows", {
36201
36493
  objectId: createdObject.id
36202
36494
  });
36203
36495
  throw new InternalError("Failed to retrieve created object");
36204
36496
  }
36205
- logger30.info("Created object", {
36497
+ logger31.info("Created object", {
36206
36498
  userId: user.id,
36207
36499
  mapId,
36208
36500
  objectId: createdObject.id,
@@ -36226,7 +36518,7 @@ class MapService {
36226
36518
  if (result.length === 0) {
36227
36519
  throw new NotFoundError("MapObject", objectId);
36228
36520
  }
36229
- logger30.info("Deleted object", {
36521
+ logger31.info("Deleted object", {
36230
36522
  userId: user.id,
36231
36523
  mapId,
36232
36524
  objectId
@@ -36255,13 +36547,13 @@ class MapService {
36255
36547
  };
36256
36548
  }
36257
36549
  }
36258
- var logger30;
36550
+ var logger31;
36259
36551
  var init_map_service = __esm(() => {
36260
36552
  init_drizzle_orm();
36261
36553
  init_tables_index();
36262
36554
  init_src2();
36263
36555
  init_errors();
36264
- logger30 = log.scope("MapService");
36556
+ logger31 = log.scope("MapService");
36265
36557
  });
36266
36558
 
36267
36559
  // ../api-core/src/services/realtime.service.ts
@@ -36296,20 +36588,20 @@ class RealtimeService {
36296
36588
  }
36297
36589
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
36298
36590
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
36299
- logger31.info("Generated token", {
36591
+ logger32.info("Generated token", {
36300
36592
  userId: user.id,
36301
36593
  gameId: resolvedGameId || "global"
36302
36594
  });
36303
36595
  return { token };
36304
36596
  }
36305
36597
  }
36306
- var logger31;
36598
+ var logger32;
36307
36599
  var init_realtime_service = __esm(() => {
36308
36600
  init_drizzle_orm();
36309
36601
  init_tables_index();
36310
36602
  init_src2();
36311
36603
  init_errors();
36312
- logger31 = log.scope("RealtimeService");
36604
+ logger32 = log.scope("RealtimeService");
36313
36605
  });
36314
36606
 
36315
36607
  // ../api-core/src/services/session.service.ts
@@ -36344,10 +36636,10 @@ class SessionService {
36344
36636
  };
36345
36637
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
36346
36638
  if (!newSession?.sessionId) {
36347
- logger32.error("Game session insert returned no rows", { userId, gameId });
36639
+ logger33.error("Game session insert returned no rows", { userId, gameId });
36348
36640
  throw new InternalError("Failed to create game session");
36349
36641
  }
36350
- logger32.info("Started new session", {
36642
+ logger33.info("Started new session", {
36351
36643
  sessionId: newSession.sessionId,
36352
36644
  gameId,
36353
36645
  userId
@@ -36368,23 +36660,23 @@ class SessionService {
36368
36660
  return { success: true, message: "Session already ended" };
36369
36661
  }
36370
36662
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
36371
- logger32.info("Ended session", { sessionId, gameId, userId });
36663
+ logger33.info("Ended session", { sessionId, gameId, userId });
36372
36664
  return { success: true };
36373
36665
  }
36374
36666
  async mintToken(gameIdOrSlug, userId) {
36375
36667
  const gameId = await this.resolveGameId(gameIdOrSlug);
36376
36668
  const result = await this.deps.mintGameToken(gameId, userId);
36377
- logger32.debug("Minted game token", { gameId, userId });
36669
+ logger33.debug("Minted game token", { gameId, userId });
36378
36670
  return result;
36379
36671
  }
36380
36672
  }
36381
- var logger32;
36673
+ var logger33;
36382
36674
  var init_session_service = __esm(() => {
36383
36675
  init_drizzle_orm();
36384
36676
  init_tables_index();
36385
36677
  init_src2();
36386
36678
  init_errors();
36387
- logger32 = log.scope("SessionService");
36679
+ logger33 = log.scope("SessionService");
36388
36680
  });
36389
36681
 
36390
36682
  // ../api-core/src/services/shop.service.ts
@@ -36430,7 +36722,7 @@ class ShopService {
36430
36722
  const shopItems = [];
36431
36723
  for (const listing of listingsWithRelations) {
36432
36724
  if (!listing.item || !listing.currency) {
36433
- logger33.warn("Listing missing item or currency, skipping", {
36725
+ logger34.warn("Listing missing item or currency, skipping", {
36434
36726
  listingId: listing.id
36435
36727
  });
36436
36728
  } else {
@@ -36447,7 +36739,7 @@ class ShopService {
36447
36739
  });
36448
36740
  }
36449
36741
  }
36450
- logger33.debug("Retrieved shop view", {
36742
+ logger34.debug("Retrieved shop view", {
36451
36743
  userId: user.id,
36452
36744
  itemCount: shopItems.length,
36453
36745
  currencyCount: shopCurrencies.length
@@ -36458,12 +36750,12 @@ class ShopService {
36458
36750
  };
36459
36751
  }
36460
36752
  }
36461
- var logger33;
36753
+ var logger34;
36462
36754
  var init_shop_service = __esm(() => {
36463
36755
  init_drizzle_orm();
36464
36756
  init_tables_index();
36465
36757
  init_src2();
36466
- logger33 = log.scope("ShopService");
36758
+ logger34 = log.scope("ShopService");
36467
36759
  });
36468
36760
 
36469
36761
  // ../api-core/src/services/sprite.service.ts
@@ -36480,17 +36772,17 @@ class SpriteService {
36480
36772
  if (!template) {
36481
36773
  throw new NotFoundError("SpriteTemplate", slug2);
36482
36774
  }
36483
- logger34.debug("Retrieved sprite", { slug: slug2 });
36775
+ logger35.debug("Retrieved sprite", { slug: slug2 });
36484
36776
  return template;
36485
36777
  }
36486
36778
  }
36487
- var logger34;
36779
+ var logger35;
36488
36780
  var init_sprite_service = __esm(() => {
36489
36781
  init_drizzle_orm();
36490
36782
  init_tables_index();
36491
36783
  init_src2();
36492
36784
  init_errors();
36493
- logger34 = log.scope("SpriteService");
36785
+ logger35 = log.scope("SpriteService");
36494
36786
  });
36495
36787
 
36496
36788
  // ../api-core/src/services/user.service.ts
@@ -36505,12 +36797,12 @@ class UserService {
36505
36797
  where: eq(users.id, user.id)
36506
36798
  });
36507
36799
  if (!userData) {
36508
- logger35.error("User not found", { userId: user.id });
36800
+ logger36.error("User not found", { userId: user.id });
36509
36801
  throw new NotFoundError("User", user.id);
36510
36802
  }
36511
36803
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
36512
36804
  if (gameId) {
36513
- logger35.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36805
+ logger36.debug("Fetched user profile (game context)", { userId: user.id, gameId });
36514
36806
  return {
36515
36807
  id: userData.id,
36516
36808
  name: userData.name,
@@ -36523,7 +36815,7 @@ class UserService {
36523
36815
  const timebackAccount = await db2.query.accounts.findFirst({
36524
36816
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
36525
36817
  });
36526
- logger35.debug("Fetched user profile (platform context)", { userId: user.id });
36818
+ logger36.debug("Fetched user profile (platform context)", { userId: user.id });
36527
36819
  return {
36528
36820
  id: userData.id,
36529
36821
  name: userData.name,
@@ -36546,7 +36838,7 @@ class UserService {
36546
36838
  columns: { name: true }
36547
36839
  });
36548
36840
  if (!userData) {
36549
- logger35.error("Demo user not found", { userId });
36841
+ logger36.error("Demo user not found", { userId });
36550
36842
  throw new NotFoundError("User", userId);
36551
36843
  }
36552
36844
  return {
@@ -36560,10 +36852,10 @@ class UserService {
36560
36852
  updatedAt: new Date
36561
36853
  }).where(eq(users.id, userId)).returning({ name: users.name });
36562
36854
  if (!updatedUser) {
36563
- logger35.error("Demo user not found for profile update", { userId });
36855
+ logger36.error("Demo user not found for profile update", { userId });
36564
36856
  throw new NotFoundError("User", userId);
36565
36857
  }
36566
- logger35.debug("Updated demo profile", { userId, displayName });
36858
+ logger36.debug("Updated demo profile", { userId, displayName });
36567
36859
  return {
36568
36860
  displayName: updatedUser.name,
36569
36861
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -36576,7 +36868,7 @@ class UserService {
36576
36868
  ]);
36577
36869
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
36578
36870
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
36579
- logger35.debug("Fetched Timeback data", {
36871
+ logger36.debug("Fetched Timeback data", {
36580
36872
  timebackId,
36581
36873
  role,
36582
36874
  enrollmentCount: enrollments.length,
@@ -36585,9 +36877,9 @@ class UserService {
36585
36877
  return { id: timebackId, role, enrollments, organizations };
36586
36878
  }
36587
36879
  async fetchStudentProfile(timebackId) {
36588
- logger35.debug("Fetching student profile", { timebackId });
36880
+ logger36.debug("Fetching student profile", { timebackId });
36589
36881
  if (!this.deps.timeback) {
36590
- logger35.warn("Timeback client not available");
36882
+ logger36.warn("Timeback client not available");
36591
36883
  return { role: "student", organizations: [] };
36592
36884
  }
36593
36885
  try {
@@ -36615,14 +36907,14 @@ class UserService {
36615
36907
  }
36616
36908
  return { role, organizations: [...orgMap.values()] };
36617
36909
  } catch (error) {
36618
- logger35.warn("Failed to fetch student profile", { error, timebackId });
36910
+ logger36.warn("Failed to fetch student profile", { error, timebackId });
36619
36911
  return { role: "student", organizations: [] };
36620
36912
  }
36621
36913
  }
36622
36914
  async fetchEnrollments(timebackId) {
36623
- logger35.debug("Fetching enrollments", { timebackId });
36915
+ logger36.debug("Fetching enrollments", { timebackId });
36624
36916
  if (!this.deps.timeback) {
36625
- logger35.warn("Timeback client not available");
36917
+ logger36.warn("Timeback client not available");
36626
36918
  return [];
36627
36919
  }
36628
36920
  try {
@@ -36643,7 +36935,7 @@ class UserService {
36643
36935
  orgId: courseToSchool.get(i2.courseId)
36644
36936
  }));
36645
36937
  } catch (error) {
36646
- logger35.warn("Failed to fetch enrollments", { error, timebackId });
36938
+ logger36.warn("Failed to fetch enrollments", { error, timebackId });
36647
36939
  return [];
36648
36940
  }
36649
36941
  }
@@ -36658,14 +36950,14 @@ class UserService {
36658
36950
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
36659
36951
  }
36660
36952
  }
36661
- var logger35;
36953
+ var logger36;
36662
36954
  var init_user_service = __esm(() => {
36663
36955
  init_drizzle_orm();
36664
36956
  init_src();
36665
36957
  init_tables_index();
36666
36958
  init_src2();
36667
36959
  init_errors();
36668
- logger35 = log.scope("UserService");
36960
+ logger36 = log.scope("UserService");
36669
36961
  });
36670
36962
 
36671
36963
  // ../api-core/src/services/verify.service.ts
@@ -36675,16 +36967,16 @@ class VerifyService {
36675
36967
  this.deps = deps;
36676
36968
  }
36677
36969
  async verifyGameToken(token) {
36678
- logger36.debug("Verifying game token");
36970
+ logger37.debug("Verifying game token");
36679
36971
  const payload = await this.deps.validateGameToken(token);
36680
36972
  if (!payload) {
36681
- logger36.warn("Invalid or expired game token presented");
36973
+ logger37.warn("Invalid or expired game token presented");
36682
36974
  throw new ValidationError("Invalid or expired token");
36683
36975
  }
36684
36976
  const gameId = payload.sub;
36685
36977
  const userId = payload.uid;
36686
36978
  if (typeof gameId !== "string" || typeof userId !== "string") {
36687
- logger36.warn("Game token missing required claims", {
36979
+ logger37.warn("Game token missing required claims", {
36688
36980
  hasGameId: typeof gameId === "string",
36689
36981
  hasUserId: typeof userId === "string"
36690
36982
  });
@@ -36695,7 +36987,7 @@ class VerifyService {
36695
36987
  where: eq(users.id, userId)
36696
36988
  });
36697
36989
  if (!userData) {
36698
- logger36.error("User not found for valid token", {
36990
+ logger37.error("User not found for valid token", {
36699
36991
  userId
36700
36992
  });
36701
36993
  throw new NotFoundError("User", userId);
@@ -36709,7 +37001,7 @@ class VerifyService {
36709
37001
  family_name: undefined,
36710
37002
  timeback_id: userData.timebackId || undefined
36711
37003
  };
36712
- logger36.info("Token verified", { gameId, userId });
37004
+ logger37.info("Token verified", { gameId, userId });
36713
37005
  return {
36714
37006
  claims: payload,
36715
37007
  gameId,
@@ -36717,13 +37009,13 @@ class VerifyService {
36717
37009
  };
36718
37010
  }
36719
37011
  }
36720
- var logger36;
37012
+ var logger37;
36721
37013
  var init_verify_service = __esm(() => {
36722
37014
  init_drizzle_orm();
36723
37015
  init_tables_index();
36724
37016
  init_src2();
36725
37017
  init_errors();
36726
- logger36 = log.scope("VerifyService");
37018
+ logger37 = log.scope("VerifyService");
36727
37019
  });
36728
37020
 
36729
37021
  // ../api-core/src/services/factory/standalone.ts
@@ -41841,7 +42133,7 @@ var humanize = (times) => {
41841
42133
  }
41842
42134
  }
41843
42135
  return `${status}`;
41844
- }, logger37 = (fn = console.log) => {
42136
+ }, logger38 = (fn = console.log) => {
41845
42137
  return async function logger2(c, next) {
41846
42138
  const { method, url: url2 } = c.req;
41847
42139
  const path = url2.slice(url2.indexOf("/", 8));
@@ -42022,7 +42314,7 @@ function createApp(db2, options) {
42022
42314
  const app = new Hono2;
42023
42315
  app.use("*", cors({ origin: "*", credentials: true }));
42024
42316
  if (options.verbose && !options.quiet) {
42025
- app.use("*", logger37());
42317
+ app.use("*", logger38());
42026
42318
  }
42027
42319
  app.use("/api/*", async (c, next) => {
42028
42320
  c.set("db", db2);
@@ -48455,12 +48747,12 @@ var init_session2 = __esm(() => {
48455
48747
  init_utils();
48456
48748
  init_dist5();
48457
48749
  PglitePreparedQuery = class PglitePreparedQuery extends PgPreparedQuery {
48458
- constructor(client, queryString, params, logger38, fields, name3, _isResponseInArrayMode, customResultMapper) {
48750
+ constructor(client, queryString, params, logger39, fields, name3, _isResponseInArrayMode, customResultMapper) {
48459
48751
  super({ sql: queryString, params });
48460
48752
  this.client = client;
48461
48753
  this.queryString = queryString;
48462
48754
  this.params = params;
48463
- this.logger = logger38;
48755
+ this.logger = logger39;
48464
48756
  this.fields = fields;
48465
48757
  this._isResponseInArrayMode = _isResponseInArrayMode;
48466
48758
  this.customResultMapper = customResultMapper;
@@ -48564,11 +48856,11 @@ var init_session2 = __esm(() => {
48564
48856
  // ../../node_modules/.bun/drizzle-orm@0.42.0+f8aef3f54a4d48e2/node_modules/drizzle-orm/pglite/driver.js
48565
48857
  function construct(client, config2 = {}) {
48566
48858
  const dialect2 = new PgDialect({ casing: config2.casing });
48567
- let logger38;
48859
+ let logger39;
48568
48860
  if (config2.logger === true) {
48569
- logger38 = new DefaultLogger;
48861
+ logger39 = new DefaultLogger;
48570
48862
  } else if (config2.logger !== false) {
48571
- logger38 = config2.logger;
48863
+ logger39 = config2.logger;
48572
48864
  }
48573
48865
  let schema2;
48574
48866
  if (config2.schema) {
@@ -48579,7 +48871,7 @@ function construct(client, config2 = {}) {
48579
48871
  tableNamesMap: tablesConfig.tableNamesMap
48580
48872
  };
48581
48873
  }
48582
- const driver = new PgliteDriver(client, dialect2, { logger: logger38 });
48874
+ const driver = new PgliteDriver(client, dialect2, { logger: logger39 });
48583
48875
  const session2 = driver.createSession(schema2);
48584
48876
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
48585
48877
  db2.$client = client;
@@ -94793,8 +95085,8 @@ var init_currencies = __esm(() => {
94793
95085
  });
94794
95086
 
94795
95087
  // src/lib/logging/adapter.ts
94796
- function setLogger(logger38) {
94797
- customLogger = logger38;
95088
+ function setLogger(logger39) {
95089
+ customLogger = logger39;
94798
95090
  }
94799
95091
  function getLogger() {
94800
95092
  if (customLogger) {
@@ -94806,10 +95098,10 @@ function getLogger() {
94806
95098
  error: (msg) => console.error(msg)
94807
95099
  };
94808
95100
  }
94809
- var customLogger, logger38;
95101
+ var customLogger, logger39;
94810
95102
  var init_adapter = __esm(() => {
94811
95103
  init_config();
94812
- logger38 = {
95104
+ logger39 = {
94813
95105
  info: (msg) => {
94814
95106
  if (customLogger || !config.embedded) {
94815
95107
  getLogger().info(msg);
@@ -94899,7 +95191,7 @@ async function seedCoreGames(db2) {
94899
95191
  role: "owner"
94900
95192
  }).onConflictDoNothing();
94901
95193
  } catch (error2) {
94902
- logger38.error(`Error seeding core game '${gameData.slug}': ${error2}`);
95194
+ logger39.error(`Error seeding core game '${gameData.slug}': ${error2}`);
94903
95195
  }
94904
95196
  }
94905
95197
  }
@@ -94949,7 +95241,7 @@ async function seedCurrentProjectGame(db2, project) {
94949
95241
  }
94950
95242
  return newGame;
94951
95243
  } catch (error2) {
94952
- logger38.error(`❌ Error seeding project game: ${error2}`);
95244
+ logger39.error(`❌ Error seeding project game: ${error2}`);
94953
95245
  throw error2;
94954
95246
  }
94955
95247
  }
@@ -95742,7 +96034,7 @@ async function provisionLtiUser(db2, claims) {
95742
96034
  where: eq(users.id, existingAccount.userId)
95743
96035
  });
95744
96036
  if (user) {
95745
- logger39.info("Found user by LTI account", {
96037
+ logger40.info("Found user by LTI account", {
95746
96038
  userId: user.id,
95747
96039
  ltiTimebackId
95748
96040
  });
@@ -95771,13 +96063,13 @@ async function provisionLtiUser(db2, claims) {
95771
96063
  updatedAt: new Date
95772
96064
  }).returning({ id: accounts.id });
95773
96065
  if (!account) {
95774
- logger39.error("LTI account link insert returned no rows", {
96066
+ logger40.error("LTI account link insert returned no rows", {
95775
96067
  userId: existingUser.id,
95776
96068
  ltiTimebackId
95777
96069
  });
95778
96070
  throw new InternalError("Failed to link LTI account");
95779
96071
  }
95780
- logger39.info("Linked LTI account to existing user", {
96072
+ logger40.info("Linked LTI account to existing user", {
95781
96073
  userId: existingUser.id,
95782
96074
  ltiTimebackId
95783
96075
  });
@@ -95797,7 +96089,7 @@ async function provisionLtiUser(db2, claims) {
95797
96089
  updatedAt: new Date
95798
96090
  }).returning();
95799
96091
  if (!insertedUser) {
95800
- logger39.error("LTI user insert returned no rows", { email, ltiTimebackId });
96092
+ logger40.error("LTI user insert returned no rows", { email, ltiTimebackId });
95801
96093
  throw new InternalError("Failed to create user");
95802
96094
  }
95803
96095
  await tx.insert(accounts).values({
@@ -95812,7 +96104,7 @@ async function provisionLtiUser(db2, claims) {
95812
96104
  createdAt: new Date,
95813
96105
  updatedAt: new Date
95814
96106
  });
95815
- logger39.info("Provisioned new user from LTI", {
96107
+ logger40.info("Provisioned new user from LTI", {
95816
96108
  userId: insertedUser.id,
95817
96109
  ltiTimebackId
95818
96110
  });
@@ -95820,7 +96112,7 @@ async function provisionLtiUser(db2, claims) {
95820
96112
  });
95821
96113
  return createdUser;
95822
96114
  }
95823
- var logger39;
96115
+ var logger40;
95824
96116
  var init_lti_provisioning = __esm(() => {
95825
96117
  init_drizzle_orm();
95826
96118
  init_src();
@@ -95828,7 +96120,7 @@ var init_lti_provisioning = __esm(() => {
95828
96120
  init_src2();
95829
96121
  init_errors();
95830
96122
  init_lti_util();
95831
- logger39 = log.scope("LtiProvisioning");
96123
+ logger40 = log.scope("LtiProvisioning");
95832
96124
  });
95833
96125
 
95834
96126
  // ../api-core/src/utils/validation.util.ts
@@ -95876,21 +96168,21 @@ var init_utils11 = __esm(() => {
95876
96168
  });
95877
96169
 
95878
96170
  // ../api-core/src/controllers/achievement.controller.ts
95879
- var logger40, listCurrent, listHistory, postProgress, achievements3;
96171
+ var logger41, listCurrent, listHistory, postProgress, achievements3;
95880
96172
  var init_achievement_controller = __esm(() => {
95881
96173
  init_esm();
95882
96174
  init_schemas_index();
95883
96175
  init_src2();
95884
96176
  init_errors();
95885
96177
  init_utils11();
95886
- logger40 = log.scope("AchievementController");
96178
+ logger41 = log.scope("AchievementController");
95887
96179
  listCurrent = requireNonAnonymous(async (ctx) => {
95888
- logger40.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
96180
+ logger41.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
95889
96181
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
95890
96182
  });
95891
96183
  listHistory = requireNonAnonymous(async (ctx) => {
95892
96184
  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 });
96185
+ logger41.debug("Listing achievement history", { userId: ctx.user.id, limit });
95894
96186
  return ctx.services.achievement.listHistory(ctx.user, limit);
95895
96187
  });
95896
96188
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -95901,12 +96193,12 @@ var init_achievement_controller = __esm(() => {
95901
96193
  } catch (error2) {
95902
96194
  if (error2 instanceof exports_external.ZodError) {
95903
96195
  const details = formatZodError(error2);
95904
- logger40.warn("Submit achievement progress validation failed", { details });
96196
+ logger41.warn("Submit achievement progress validation failed", { details });
95905
96197
  throw ApiError.unprocessableEntity("Invalid request body", details);
95906
96198
  }
95907
96199
  throw ApiError.badRequest("Invalid JSON body");
95908
96200
  }
95909
- logger40.debug("Submitting progress", {
96201
+ logger41.debug("Submitting progress", {
95910
96202
  userId: ctx.user.id,
95911
96203
  achievementId: body2.achievementId
95912
96204
  });
@@ -95920,14 +96212,14 @@ var init_achievement_controller = __esm(() => {
95920
96212
  });
95921
96213
 
95922
96214
  // ../api-core/src/controllers/admin.controller.ts
95923
- var logger41, getAllowedOrigins;
96215
+ var logger42, getAllowedOrigins;
95924
96216
  var init_admin_controller = __esm(() => {
95925
96217
  init_src2();
95926
96218
  init_utils11();
95927
- logger41 = log.scope("AdminController");
96219
+ logger42 = log.scope("AdminController");
95928
96220
  getAllowedOrigins = requireAdmin(async (ctx) => {
95929
96221
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
95930
- logger41.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
96222
+ logger42.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
95931
96223
  if (shouldRefresh) {
95932
96224
  await ctx.providers.cache.refreshGameOrigins();
95933
96225
  }
@@ -95942,14 +96234,14 @@ var init_admin_controller = __esm(() => {
95942
96234
  });
95943
96235
 
95944
96236
  // ../api-core/src/controllers/bucket.controller.ts
95945
- var logger42, listFiles, getFile, putFile, deleteFile, initiateUpload;
96237
+ var logger43, listFiles, getFile, putFile, deleteFile, initiateUpload;
95946
96238
  var init_bucket_controller = __esm(() => {
95947
96239
  init_esm();
95948
96240
  init_schemas_index();
95949
96241
  init_src2();
95950
96242
  init_errors();
95951
96243
  init_utils11();
95952
- logger42 = log.scope("BucketController");
96244
+ logger43 = log.scope("BucketController");
95953
96245
  listFiles = requireDeveloper(async (ctx) => {
95954
96246
  const slug2 = ctx.params.slug;
95955
96247
  if (!slug2) {
@@ -95957,7 +96249,7 @@ var init_bucket_controller = __esm(() => {
95957
96249
  }
95958
96250
  const url2 = ctx.url;
95959
96251
  const prefix2 = url2.searchParams.get("prefix") || undefined;
95960
- logger42.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96252
+ logger43.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
95961
96253
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
95962
96254
  return { files };
95963
96255
  });
@@ -95967,7 +96259,7 @@ var init_bucket_controller = __esm(() => {
95967
96259
  if (!slug2 || !key) {
95968
96260
  throw ApiError.badRequest("Missing game slug or file key");
95969
96261
  }
95970
- logger42.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
96262
+ logger43.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
95971
96263
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
95972
96264
  return new Response(Buffer.from(object.body), {
95973
96265
  status: 200,
@@ -95986,7 +96278,7 @@ var init_bucket_controller = __esm(() => {
95986
96278
  const arrayBuffer = await ctx.request.arrayBuffer();
95987
96279
  const body2 = new Uint8Array(arrayBuffer);
95988
96280
  const contentType = ctx.request.headers.get("content-type") || undefined;
95989
- logger42.debug("Uploading file", {
96281
+ logger43.debug("Uploading file", {
95990
96282
  userId: ctx.user.id,
95991
96283
  slug: slug2,
95992
96284
  key,
@@ -96002,7 +96294,7 @@ var init_bucket_controller = __esm(() => {
96002
96294
  if (!slug2 || !key) {
96003
96295
  throw ApiError.badRequest("Missing game slug or file key");
96004
96296
  }
96005
- logger42.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
96297
+ logger43.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
96006
96298
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
96007
96299
  return { success: true, key };
96008
96300
  });
@@ -96014,12 +96306,12 @@ var init_bucket_controller = __esm(() => {
96014
96306
  } catch (error2) {
96015
96307
  if (error2 instanceof exports_external.ZodError) {
96016
96308
  const details = formatZodError(error2);
96017
- logger42.warn("Initiate upload validation failed", { details });
96309
+ logger43.warn("Initiate upload validation failed", { details });
96018
96310
  throw ApiError.unprocessableEntity("Validation failed", details);
96019
96311
  }
96020
96312
  throw ApiError.badRequest("Invalid JSON body");
96021
96313
  }
96022
- logger42.debug("Initiating multipart upload", {
96314
+ logger43.debug("Initiating multipart upload", {
96023
96315
  userId: ctx.user.id,
96024
96316
  gameId: body2.gameId,
96025
96317
  fileName: body2.fileName
@@ -96036,19 +96328,19 @@ async function listComponents(ctx) {
96036
96328
  if (!isNaN(parsed) && isFinite(parsed)) {
96037
96329
  level = Math.floor(Math.max(0, parsed));
96038
96330
  }
96039
- logger43.debug("Listing components", { level });
96331
+ logger44.debug("Listing components", { level });
96040
96332
  return ctx.services.character.listAvailableComponents(level);
96041
96333
  }
96042
- var logger43, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96334
+ var logger44, get, getByUserId, create, update2, equipAccessory, removeAccessory, character2;
96043
96335
  var init_character_controller = __esm(() => {
96044
96336
  init_esm();
96045
96337
  init_schemas_index();
96046
96338
  init_src2();
96047
96339
  init_errors();
96048
96340
  init_utils11();
96049
- logger43 = log.scope("CharacterController");
96341
+ logger44 = log.scope("CharacterController");
96050
96342
  get = requireNonAnonymous(async (ctx) => {
96051
- logger43.debug("Getting character", { userId: ctx.user.id });
96343
+ logger44.debug("Getting character", { userId: ctx.user.id });
96052
96344
  return ctx.services.character.getByUser(ctx.user);
96053
96345
  });
96054
96346
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -96056,7 +96348,7 @@ var init_character_controller = __esm(() => {
96056
96348
  if (!userId) {
96057
96349
  throw ApiError.badRequest("User ID is required in the URL path");
96058
96350
  }
96059
- logger43.debug("Getting character by user ID", { requestedUserId: userId });
96351
+ logger44.debug("Getting character by user ID", { requestedUserId: userId });
96060
96352
  return ctx.services.character.getByUserId(userId);
96061
96353
  });
96062
96354
  create = requireNonAnonymous(async (ctx) => {
@@ -96067,12 +96359,12 @@ var init_character_controller = __esm(() => {
96067
96359
  } catch (error2) {
96068
96360
  if (error2 instanceof exports_external.ZodError) {
96069
96361
  const details = formatZodError(error2);
96070
- logger43.warn("Create character validation failed", { details });
96362
+ logger44.warn("Create character validation failed", { details });
96071
96363
  throw ApiError.unprocessableEntity("Invalid request body", details);
96072
96364
  }
96073
96365
  throw ApiError.badRequest("Invalid JSON body");
96074
96366
  }
96075
- logger43.debug("Creating character", {
96367
+ logger44.debug("Creating character", {
96076
96368
  userId: ctx.user.id,
96077
96369
  bodyComponentId: body2.bodyComponentId,
96078
96370
  hairstyleComponentId: body2.hairstyleComponentId
@@ -96087,12 +96379,12 @@ var init_character_controller = __esm(() => {
96087
96379
  } catch (error2) {
96088
96380
  if (error2 instanceof exports_external.ZodError) {
96089
96381
  const details = formatZodError(error2);
96090
- logger43.warn("Update character validation failed", { details });
96382
+ logger44.warn("Update character validation failed", { details });
96091
96383
  throw ApiError.unprocessableEntity("Invalid request body", details);
96092
96384
  }
96093
96385
  throw ApiError.badRequest("Invalid JSON body");
96094
96386
  }
96095
- logger43.debug("Updating character", {
96387
+ logger44.debug("Updating character", {
96096
96388
  userId: ctx.user.id,
96097
96389
  bodyComponentId: body2.bodyComponentId,
96098
96390
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -96108,12 +96400,12 @@ var init_character_controller = __esm(() => {
96108
96400
  } catch (error2) {
96109
96401
  if (error2 instanceof exports_external.ZodError) {
96110
96402
  const details = formatZodError(error2);
96111
- logger43.warn("Equip accessory validation failed", { details });
96403
+ logger44.warn("Equip accessory validation failed", { details });
96112
96404
  throw ApiError.unprocessableEntity("Invalid request body", details);
96113
96405
  }
96114
96406
  throw ApiError.badRequest("Invalid JSON body");
96115
96407
  }
96116
- logger43.debug("Equipping accessory", {
96408
+ logger44.debug("Equipping accessory", {
96117
96409
  userId: ctx.user.id,
96118
96410
  slot: body2.slot,
96119
96411
  accessoryComponentId: body2.accessoryComponentId
@@ -96125,7 +96417,7 @@ var init_character_controller = __esm(() => {
96125
96417
  if (!slot) {
96126
96418
  throw ApiError.badRequest("Slot is required in the URL path");
96127
96419
  }
96128
- logger43.debug("Removing accessory", { userId: ctx.user.id, slot });
96420
+ logger44.debug("Removing accessory", { userId: ctx.user.id, slot });
96129
96421
  await ctx.services.character.removeAccessory(slot, ctx.user);
96130
96422
  return { success: true };
96131
96423
  });
@@ -96141,7 +96433,7 @@ var init_character_controller = __esm(() => {
96141
96433
  });
96142
96434
 
96143
96435
  // ../api-core/src/controllers/currency.controller.ts
96144
- var logger44, list, getById, create2, update3, remove, currencyController;
96436
+ var logger45, list, getById, create2, update3, remove, currencyController;
96145
96437
  var init_currency_controller = __esm(() => {
96146
96438
  init_esm();
96147
96439
  init_schemas_index();
@@ -96149,9 +96441,9 @@ var init_currency_controller = __esm(() => {
96149
96441
  init_src4();
96150
96442
  init_errors();
96151
96443
  init_utils11();
96152
- logger44 = log.scope("CurrencyController");
96444
+ logger45 = log.scope("CurrencyController");
96153
96445
  list = requireNonAnonymous(async (ctx) => {
96154
- logger44.debug("Listing currencies", { userId: ctx.user.id });
96446
+ logger45.debug("Listing currencies", { userId: ctx.user.id });
96155
96447
  return ctx.services.currency.list();
96156
96448
  });
96157
96449
  getById = requireNonAnonymous(async (ctx) => {
@@ -96162,7 +96454,7 @@ var init_currency_controller = __esm(() => {
96162
96454
  if (!isValidUUID(currencyId)) {
96163
96455
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96164
96456
  }
96165
- logger44.debug("Getting currency", { userId: ctx.user.id, currencyId });
96457
+ logger45.debug("Getting currency", { userId: ctx.user.id, currencyId });
96166
96458
  return ctx.services.currency.getById(currencyId);
96167
96459
  });
96168
96460
  create2 = requireAdmin(async (ctx) => {
@@ -96173,12 +96465,12 @@ var init_currency_controller = __esm(() => {
96173
96465
  } catch (error2) {
96174
96466
  if (error2 instanceof exports_external.ZodError) {
96175
96467
  const details = formatZodError(error2);
96176
- logger44.warn("Create currency validation failed", { details });
96468
+ logger45.warn("Create currency validation failed", { details });
96177
96469
  throw ApiError.unprocessableEntity("Validation failed", details);
96178
96470
  }
96179
96471
  throw ApiError.badRequest("Invalid JSON body");
96180
96472
  }
96181
- logger44.debug("Creating currency", {
96473
+ logger45.debug("Creating currency", {
96182
96474
  userId: ctx.user.id,
96183
96475
  symbol: body2.symbol,
96184
96476
  itemId: body2.itemId,
@@ -96201,12 +96493,12 @@ var init_currency_controller = __esm(() => {
96201
96493
  } catch (error2) {
96202
96494
  if (error2 instanceof exports_external.ZodError) {
96203
96495
  const details = formatZodError(error2);
96204
- logger44.warn("Update currency validation failed", { details });
96496
+ logger45.warn("Update currency validation failed", { details });
96205
96497
  throw ApiError.unprocessableEntity("Validation failed", details);
96206
96498
  }
96207
96499
  throw ApiError.badRequest("Invalid JSON body");
96208
96500
  }
96209
- logger44.debug("Updating currency", {
96501
+ logger45.debug("Updating currency", {
96210
96502
  userId: ctx.user.id,
96211
96503
  currencyId,
96212
96504
  symbol: body2.symbol,
@@ -96223,7 +96515,7 @@ var init_currency_controller = __esm(() => {
96223
96515
  if (!isValidUUID(currencyId)) {
96224
96516
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
96225
96517
  }
96226
- logger44.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96518
+ logger45.debug("Deleting currency", { userId: ctx.user.id, currencyId });
96227
96519
  await ctx.services.currency.delete(currencyId);
96228
96520
  });
96229
96521
  currencyController = {
@@ -96236,14 +96528,14 @@ var init_currency_controller = __esm(() => {
96236
96528
  });
96237
96529
 
96238
96530
  // ../api-core/src/controllers/database.controller.ts
96239
- var logger45, reset;
96531
+ var logger46, reset;
96240
96532
  var init_database_controller = __esm(() => {
96241
96533
  init_esm();
96242
96534
  init_schemas_index();
96243
96535
  init_src2();
96244
96536
  init_errors();
96245
96537
  init_utils11();
96246
- logger45 = log.scope("DatabaseController");
96538
+ logger46 = log.scope("DatabaseController");
96247
96539
  reset = requireDeveloper(async (ctx) => {
96248
96540
  const slug2 = ctx.params.slug;
96249
96541
  if (!slug2) {
@@ -96256,11 +96548,11 @@ var init_database_controller = __esm(() => {
96256
96548
  } catch (error2) {
96257
96549
  if (error2 instanceof exports_external.ZodError) {
96258
96550
  const details = formatZodError(error2);
96259
- logger45.warn("Database reset validation failed", { details });
96551
+ logger46.warn("Database reset validation failed", { details });
96260
96552
  throw ApiError.unprocessableEntity("Validation failed", details);
96261
96553
  }
96262
96554
  }
96263
- logger45.debug("Resetting database", {
96555
+ logger46.debug("Resetting database", {
96264
96556
  userId: ctx.user.id,
96265
96557
  slug: slug2,
96266
96558
  hasSchema: Boolean(body2.schema)
@@ -96278,7 +96570,7 @@ async function createJob(ctx) {
96278
96570
  let body2;
96279
96571
  try {
96280
96572
  const json4 = await ctx.request.json();
96281
- logger46.debug("Deploy request body", {
96573
+ logger47.debug("Deploy request body", {
96282
96574
  keys: Object.keys(json4 || {}),
96283
96575
  hasUploadToken: Boolean(json4?.uploadToken),
96284
96576
  hasCode: Boolean(json4?.code),
@@ -96288,7 +96580,7 @@ async function createJob(ctx) {
96288
96580
  } catch (error2) {
96289
96581
  if (error2 instanceof exports_external.ZodError) {
96290
96582
  const details = formatZodError(error2);
96291
- logger46.warn("Deploy validation failed", { details });
96583
+ logger47.warn("Deploy validation failed", { details });
96292
96584
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
96293
96585
  }
96294
96586
  throw ApiError.badRequest("Invalid JSON body");
@@ -96309,14 +96601,14 @@ async function getJob(ctx) {
96309
96601
  }
96310
96602
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
96311
96603
  }
96312
- var logger46, deploy;
96604
+ var logger47, deploy;
96313
96605
  var init_deploy_controller = __esm(() => {
96314
96606
  init_esm();
96315
96607
  init_schemas_index();
96316
96608
  init_src2();
96317
96609
  init_errors();
96318
96610
  init_utils11();
96319
- logger46 = log.scope("DeployController");
96611
+ logger47 = log.scope("DeployController");
96320
96612
  deploy = {
96321
96613
  createJob: requireDeveloper(createJob),
96322
96614
  getJob: requireDeveloper(getJob)
@@ -96324,17 +96616,17 @@ var init_deploy_controller = __esm(() => {
96324
96616
  });
96325
96617
 
96326
96618
  // ../api-core/src/controllers/developer.controller.ts
96327
- var logger47, apply, getStatus, developer;
96619
+ var logger48, apply, getStatus, developer;
96328
96620
  var init_developer_controller = __esm(() => {
96329
96621
  init_src2();
96330
96622
  init_utils11();
96331
- logger47 = log.scope("DeveloperController");
96623
+ logger48 = log.scope("DeveloperController");
96332
96624
  apply = requireNonAnonymous(async (ctx) => {
96333
- logger47.debug("Applying for developer status", { userId: ctx.user.id });
96625
+ logger48.debug("Applying for developer status", { userId: ctx.user.id });
96334
96626
  await ctx.services.developer.apply(ctx.user);
96335
96627
  });
96336
96628
  getStatus = requireNonAnonymous(async (ctx) => {
96337
- logger47.debug("Getting developer status", { userId: ctx.user.id });
96629
+ logger48.debug("Getting developer status", { userId: ctx.user.id });
96338
96630
  const status = await ctx.services.developer.getStatus(ctx.user.id);
96339
96631
  return { status };
96340
96632
  });
@@ -96345,7 +96637,7 @@ var init_developer_controller = __esm(() => {
96345
96637
  });
96346
96638
 
96347
96639
  // ../api-core/src/controllers/domain.controller.ts
96348
- var logger48, add, list2, getStatus2, remove2, domains2;
96640
+ var logger49, add, list2, getStatus2, remove2, domains2;
96349
96641
  var init_domain_controller = __esm(() => {
96350
96642
  init_esm();
96351
96643
  init_schemas_index();
@@ -96353,7 +96645,7 @@ var init_domain_controller = __esm(() => {
96353
96645
  init_config2();
96354
96646
  init_errors();
96355
96647
  init_utils11();
96356
- logger48 = log.scope("DomainController");
96648
+ logger49 = log.scope("DomainController");
96357
96649
  add = requireDeveloper(async (ctx) => {
96358
96650
  const slug2 = ctx.params.slug;
96359
96651
  if (!slug2) {
@@ -96366,13 +96658,13 @@ var init_domain_controller = __esm(() => {
96366
96658
  } catch (error2) {
96367
96659
  if (error2 instanceof exports_external.ZodError) {
96368
96660
  const details = formatZodError(error2);
96369
- logger48.warn("Add domain validation failed", { details });
96661
+ logger49.warn("Add domain validation failed", { details });
96370
96662
  throw ApiError.unprocessableEntity("Validation failed", details);
96371
96663
  }
96372
96664
  throw ApiError.badRequest("Invalid JSON body");
96373
96665
  }
96374
96666
  const environment = getPlatformEnvironment(ctx.config);
96375
- logger48.debug("Adding domain", {
96667
+ logger49.debug("Adding domain", {
96376
96668
  userId: ctx.user.id,
96377
96669
  slug: slug2,
96378
96670
  hostname: body2.hostname,
@@ -96386,7 +96678,7 @@ var init_domain_controller = __esm(() => {
96386
96678
  throw ApiError.badRequest("Missing game slug");
96387
96679
  }
96388
96680
  const environment = getPlatformEnvironment(ctx.config);
96389
- logger48.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96681
+ logger49.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
96390
96682
  const domains2 = await ctx.services.domain.list(slug2, environment, ctx.user);
96391
96683
  return { domains: domains2 };
96392
96684
  });
@@ -96401,7 +96693,7 @@ var init_domain_controller = __esm(() => {
96401
96693
  }
96402
96694
  const refresh = ctx.url.searchParams.get("refresh") === "true";
96403
96695
  const environment = getPlatformEnvironment(ctx.config);
96404
- logger48.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96696
+ logger49.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
96405
96697
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
96406
96698
  });
96407
96699
  remove2 = requireDeveloper(async (ctx) => {
@@ -96414,7 +96706,7 @@ var init_domain_controller = __esm(() => {
96414
96706
  throw ApiError.badRequest("Missing hostname");
96415
96707
  }
96416
96708
  const environment = getPlatformEnvironment(ctx.config);
96417
- logger48.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96709
+ logger49.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
96418
96710
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
96419
96711
  });
96420
96712
  domains2 = {
@@ -96426,7 +96718,7 @@ var init_domain_controller = __esm(() => {
96426
96718
  });
96427
96719
 
96428
96720
  // ../api-core/src/controllers/game.controller.ts
96429
- var logger49, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96721
+ var logger50, list3, listAccessible, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
96430
96722
  var init_game_controller = __esm(() => {
96431
96723
  init_esm();
96432
96724
  init_schemas_index();
@@ -96434,17 +96726,17 @@ var init_game_controller = __esm(() => {
96434
96726
  init_src4();
96435
96727
  init_errors();
96436
96728
  init_utils11();
96437
- logger49 = log.scope("GameController");
96729
+ logger50 = log.scope("GameController");
96438
96730
  list3 = requireNonAnonymous(async (ctx) => {
96439
- logger49.debug("Listing games", { userId: ctx.user.id });
96731
+ logger50.debug("Listing games", { userId: ctx.user.id });
96440
96732
  return ctx.services.game.list(ctx.user);
96441
96733
  });
96442
96734
  listAccessible = requireNonAnonymous(async (ctx) => {
96443
- logger49.debug("Listing accessible games", { userId: ctx.user.id });
96735
+ logger50.debug("Listing accessible games", { userId: ctx.user.id });
96444
96736
  return ctx.services.game.listAccessible(ctx.user);
96445
96737
  });
96446
96738
  getSubjects = requireNonAnonymous(async (ctx) => {
96447
- logger49.debug("Getting game subjects", { userId: ctx.user.id });
96739
+ logger50.debug("Getting game subjects", { userId: ctx.user.id });
96448
96740
  return ctx.services.game.getSubjects();
96449
96741
  });
96450
96742
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -96455,7 +96747,7 @@ var init_game_controller = __esm(() => {
96455
96747
  if (!isValidUUID(gameId)) {
96456
96748
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96457
96749
  }
96458
- logger49.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96750
+ logger50.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
96459
96751
  return ctx.services.game.getById(gameId, ctx.user);
96460
96752
  });
96461
96753
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -96463,7 +96755,7 @@ var init_game_controller = __esm(() => {
96463
96755
  if (!slug2) {
96464
96756
  throw ApiError.badRequest("Missing game slug");
96465
96757
  }
96466
- logger49.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96758
+ logger50.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
96467
96759
  return ctx.services.game.getBySlug(slug2, ctx.user);
96468
96760
  });
96469
96761
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -96474,7 +96766,7 @@ var init_game_controller = __esm(() => {
96474
96766
  if (!isValidUUID(gameId)) {
96475
96767
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96476
96768
  }
96477
- logger49.debug("Getting game manifest by ID", {
96769
+ logger50.debug("Getting game manifest by ID", {
96478
96770
  userId: ctx.user.id,
96479
96771
  gameId,
96480
96772
  launchId: ctx.launchId
@@ -96493,12 +96785,12 @@ var init_game_controller = __esm(() => {
96493
96785
  } catch (error2) {
96494
96786
  if (error2 instanceof exports_external.ZodError) {
96495
96787
  const details = formatZodError(error2);
96496
- logger49.warn("Upsert game validation failed", { details });
96788
+ logger50.warn("Upsert game validation failed", { details });
96497
96789
  throw ApiError.unprocessableEntity("Validation failed", details);
96498
96790
  }
96499
96791
  throw ApiError.badRequest("Invalid JSON body");
96500
96792
  }
96501
- logger49.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96793
+ logger50.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
96502
96794
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
96503
96795
  });
96504
96796
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -96509,7 +96801,7 @@ var init_game_controller = __esm(() => {
96509
96801
  if (!isValidUUID(gameId)) {
96510
96802
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96511
96803
  }
96512
- logger49.debug("Deleting game", { userId: ctx.user.id, gameId });
96804
+ logger50.debug("Deleting game", { userId: ctx.user.id, gameId });
96513
96805
  await ctx.services.game.delete(gameId, ctx.user);
96514
96806
  });
96515
96807
  games2 = {
@@ -96525,16 +96817,16 @@ var init_game_controller = __esm(() => {
96525
96817
  });
96526
96818
 
96527
96819
  // ../api-core/src/controllers/inventory.controller.ts
96528
- var logger50, list4, addItem, removeItem, inventory;
96820
+ var logger51, list4, addItem, removeItem, inventory;
96529
96821
  var init_inventory_controller = __esm(() => {
96530
96822
  init_esm();
96531
96823
  init_schemas_index();
96532
96824
  init_src2();
96533
96825
  init_errors();
96534
96826
  init_utils11();
96535
- logger50 = log.scope("InventoryController");
96827
+ logger51 = log.scope("InventoryController");
96536
96828
  list4 = requireNonAnonymous(async (ctx) => {
96537
- logger50.debug("Listing inventory", { userId: ctx.user.id });
96829
+ logger51.debug("Listing inventory", { userId: ctx.user.id });
96538
96830
  return ctx.services.inventory.list(ctx.user);
96539
96831
  });
96540
96832
  addItem = requireNonAnonymous(async (ctx) => {
@@ -96545,12 +96837,12 @@ var init_inventory_controller = __esm(() => {
96545
96837
  } catch (error2) {
96546
96838
  if (error2 instanceof exports_external.ZodError) {
96547
96839
  const details = formatZodError(error2);
96548
- logger50.warn("Add inventory item validation failed", { details });
96840
+ logger51.warn("Add inventory item validation failed", { details });
96549
96841
  throw ApiError.unprocessableEntity("Invalid request body", details);
96550
96842
  }
96551
96843
  throw ApiError.badRequest("Invalid JSON body");
96552
96844
  }
96553
- logger50.debug("Adding item", {
96845
+ logger51.debug("Adding item", {
96554
96846
  userId: ctx.user.id,
96555
96847
  itemId: body2.itemId,
96556
96848
  qty: body2.qty
@@ -96565,12 +96857,12 @@ var init_inventory_controller = __esm(() => {
96565
96857
  } catch (error2) {
96566
96858
  if (error2 instanceof exports_external.ZodError) {
96567
96859
  const details = formatZodError(error2);
96568
- logger50.warn("Remove inventory item validation failed", { details });
96860
+ logger51.warn("Remove inventory item validation failed", { details });
96569
96861
  throw ApiError.unprocessableEntity("Invalid request body", details);
96570
96862
  }
96571
96863
  throw ApiError.badRequest("Invalid JSON body");
96572
96864
  }
96573
- logger50.debug("Removing item", {
96865
+ logger51.debug("Removing item", {
96574
96866
  userId: ctx.user.id,
96575
96867
  itemId: body2.itemId,
96576
96868
  qty: body2.qty
@@ -96585,7 +96877,7 @@ var init_inventory_controller = __esm(() => {
96585
96877
  });
96586
96878
 
96587
96879
  // ../api-core/src/controllers/item.controller.ts
96588
- var logger51, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96880
+ var logger52, list5, getById3, resolve2, create3, update4, remove4, listByGame, createForGame, updateForGame, deleteForGame, items2;
96589
96881
  var init_item_controller = __esm(() => {
96590
96882
  init_esm();
96591
96883
  init_schemas_index();
@@ -96593,10 +96885,10 @@ var init_item_controller = __esm(() => {
96593
96885
  init_src4();
96594
96886
  init_errors();
96595
96887
  init_utils11();
96596
- logger51 = log.scope("ItemController");
96888
+ logger52 = log.scope("ItemController");
96597
96889
  list5 = requireNonAnonymous(async (ctx) => {
96598
96890
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
96599
- logger51.debug("Listing items", { userId: ctx.user.id, gameId });
96891
+ logger52.debug("Listing items", { userId: ctx.user.id, gameId });
96600
96892
  return ctx.services.item.list(gameId);
96601
96893
  });
96602
96894
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -96607,7 +96899,7 @@ var init_item_controller = __esm(() => {
96607
96899
  if (!isValidUUID(itemId)) {
96608
96900
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96609
96901
  }
96610
- logger51.debug("Getting item", { userId: ctx.user.id, itemId });
96902
+ logger52.debug("Getting item", { userId: ctx.user.id, itemId });
96611
96903
  return ctx.services.item.getById(itemId);
96612
96904
  });
96613
96905
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -96619,7 +96911,7 @@ var init_item_controller = __esm(() => {
96619
96911
  if (gameId && !isValidUUID(gameId)) {
96620
96912
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96621
96913
  }
96622
- logger51.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96914
+ logger52.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
96623
96915
  return ctx.services.item.resolveBySlug(slug2, gameId);
96624
96916
  });
96625
96917
  create3 = requireRole(["admin"], async (ctx) => {
@@ -96630,12 +96922,12 @@ var init_item_controller = __esm(() => {
96630
96922
  } catch (error2) {
96631
96923
  if (error2 instanceof exports_external.ZodError) {
96632
96924
  const details = formatZodError(error2);
96633
- logger51.warn("Create item validation failed", { details });
96925
+ logger52.warn("Create item validation failed", { details });
96634
96926
  throw ApiError.unprocessableEntity("Validation failed", details);
96635
96927
  }
96636
96928
  throw ApiError.badRequest("Invalid JSON body");
96637
96929
  }
96638
- logger51.debug("Creating item", {
96930
+ logger52.debug("Creating item", {
96639
96931
  userId: ctx.user.id,
96640
96932
  slug: body2.slug,
96641
96933
  displayName: body2.displayName
@@ -96657,7 +96949,7 @@ var init_item_controller = __esm(() => {
96657
96949
  } catch (error2) {
96658
96950
  if (error2 instanceof exports_external.ZodError) {
96659
96951
  const details = formatZodError(error2);
96660
- logger51.warn("Update item validation failed", { details });
96952
+ logger52.warn("Update item validation failed", { details });
96661
96953
  throw ApiError.unprocessableEntity("Validation failed", details);
96662
96954
  }
96663
96955
  throw ApiError.badRequest("Invalid JSON body");
@@ -96665,7 +96957,7 @@ var init_item_controller = __esm(() => {
96665
96957
  if (Object.keys(body2).length === 0) {
96666
96958
  throw ApiError.badRequest("No update data provided");
96667
96959
  }
96668
- logger51.debug("Updating item", {
96960
+ logger52.debug("Updating item", {
96669
96961
  userId: ctx.user.id,
96670
96962
  itemId,
96671
96963
  slug: body2.slug,
@@ -96682,7 +96974,7 @@ var init_item_controller = __esm(() => {
96682
96974
  if (!isValidUUID(itemId)) {
96683
96975
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96684
96976
  }
96685
- logger51.debug("Deleting item", { userId: ctx.user.id, itemId });
96977
+ logger52.debug("Deleting item", { userId: ctx.user.id, itemId });
96686
96978
  await ctx.services.item.delete(itemId);
96687
96979
  });
96688
96980
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -96693,7 +96985,7 @@ var init_item_controller = __esm(() => {
96693
96985
  if (!isValidUUID(gameId)) {
96694
96986
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
96695
96987
  }
96696
- logger51.debug("Listing game items", { userId: ctx.user.id, gameId });
96988
+ logger52.debug("Listing game items", { userId: ctx.user.id, gameId });
96697
96989
  return ctx.services.item.listByGame(gameId);
96698
96990
  });
96699
96991
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -96711,12 +97003,12 @@ var init_item_controller = __esm(() => {
96711
97003
  } catch (error2) {
96712
97004
  if (error2 instanceof exports_external.ZodError) {
96713
97005
  const details = formatZodError(error2);
96714
- logger51.warn("Create game item validation failed", { details });
97006
+ logger52.warn("Create game item validation failed", { details });
96715
97007
  throw ApiError.unprocessableEntity("Validation failed", details);
96716
97008
  }
96717
97009
  throw ApiError.badRequest("Invalid JSON body");
96718
97010
  }
96719
- logger51.debug("Creating game item", {
97011
+ logger52.debug("Creating game item", {
96720
97012
  userId: ctx.user.id,
96721
97013
  gameId,
96722
97014
  slug: body2.slug,
@@ -96743,7 +97035,7 @@ var init_item_controller = __esm(() => {
96743
97035
  } catch (error2) {
96744
97036
  if (error2 instanceof exports_external.ZodError) {
96745
97037
  const details = formatZodError(error2);
96746
- logger51.warn("Update game item validation failed", { details });
97038
+ logger52.warn("Update game item validation failed", { details });
96747
97039
  throw ApiError.unprocessableEntity("Validation failed", details);
96748
97040
  }
96749
97041
  throw ApiError.badRequest("Invalid JSON body");
@@ -96751,7 +97043,7 @@ var init_item_controller = __esm(() => {
96751
97043
  if (Object.keys(body2).length === 0) {
96752
97044
  throw ApiError.badRequest("No update data provided");
96753
97045
  }
96754
- logger51.debug("Updating game item", {
97046
+ logger52.debug("Updating game item", {
96755
97047
  userId: ctx.user.id,
96756
97048
  gameId,
96757
97049
  itemId,
@@ -96773,7 +97065,7 @@ var init_item_controller = __esm(() => {
96773
97065
  if (!isValidUUID(itemId)) {
96774
97066
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
96775
97067
  }
96776
- logger51.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
97068
+ logger52.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
96777
97069
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
96778
97070
  });
96779
97071
  items2 = {
@@ -96791,14 +97083,14 @@ var init_item_controller = __esm(() => {
96791
97083
  });
96792
97084
 
96793
97085
  // ../api-core/src/controllers/kv.controller.ts
96794
- var logger52, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
97086
+ var logger53, listKeys, getStats, seed, getValue2, setValue2, deleteValue, getMetadata, clear;
96795
97087
  var init_kv_controller = __esm(() => {
96796
97088
  init_esm();
96797
97089
  init_schemas_index();
96798
97090
  init_src2();
96799
97091
  init_errors();
96800
97092
  init_utils11();
96801
- logger52 = log.scope("KVController");
97093
+ logger53 = log.scope("KVController");
96802
97094
  listKeys = requireDeveloper(async (ctx) => {
96803
97095
  const slug2 = ctx.params.slug;
96804
97096
  if (!slug2) {
@@ -96806,7 +97098,7 @@ var init_kv_controller = __esm(() => {
96806
97098
  }
96807
97099
  const url2 = ctx.url;
96808
97100
  const prefix2 = url2.searchParams.get("prefix") || undefined;
96809
- logger52.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
97101
+ logger53.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
96810
97102
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
96811
97103
  return { keys };
96812
97104
  });
@@ -96815,7 +97107,7 @@ var init_kv_controller = __esm(() => {
96815
97107
  if (!slug2) {
96816
97108
  throw ApiError.badRequest("Missing game slug");
96817
97109
  }
96818
- logger52.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
97110
+ logger53.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
96819
97111
  return ctx.services.kv.getStats(slug2, ctx.user);
96820
97112
  });
96821
97113
  seed = requireDeveloper(async (ctx) => {
@@ -96830,12 +97122,12 @@ var init_kv_controller = __esm(() => {
96830
97122
  } catch (error2) {
96831
97123
  if (error2 instanceof exports_external.ZodError) {
96832
97124
  const details = formatZodError(error2);
96833
- logger52.warn("Seed validation failed", { details });
97125
+ logger53.warn("Seed validation failed", { details });
96834
97126
  throw ApiError.unprocessableEntity("Validation failed", details);
96835
97127
  }
96836
97128
  throw ApiError.badRequest("Invalid JSON body");
96837
97129
  }
96838
- logger52.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
97130
+ logger53.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
96839
97131
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
96840
97132
  return { success: true, count: body2.entries.length };
96841
97133
  });
@@ -96845,7 +97137,7 @@ var init_kv_controller = __esm(() => {
96845
97137
  if (!slug2 || !key) {
96846
97138
  throw ApiError.badRequest("Missing game slug or key");
96847
97139
  }
96848
- logger52.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
97140
+ logger53.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
96849
97141
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
96850
97142
  return { key, value };
96851
97143
  });
@@ -96859,7 +97151,7 @@ var init_kv_controller = __esm(() => {
96859
97151
  if (!value) {
96860
97152
  throw ApiError.badRequest("Missing value in request body");
96861
97153
  }
96862
- logger52.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
97154
+ logger53.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
96863
97155
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
96864
97156
  return { success: true, key };
96865
97157
  });
@@ -96869,7 +97161,7 @@ var init_kv_controller = __esm(() => {
96869
97161
  if (!slug2 || !key) {
96870
97162
  throw ApiError.badRequest("Missing game slug or key");
96871
97163
  }
96872
- logger52.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
97164
+ logger53.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
96873
97165
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
96874
97166
  return { success: true, key };
96875
97167
  });
@@ -96879,7 +97171,7 @@ var init_kv_controller = __esm(() => {
96879
97171
  if (!slug2 || !key) {
96880
97172
  throw ApiError.badRequest("Missing game slug or key");
96881
97173
  }
96882
- logger52.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
97174
+ logger53.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
96883
97175
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
96884
97176
  return { key, metadata: metadata2 };
96885
97177
  });
@@ -96888,21 +97180,21 @@ var init_kv_controller = __esm(() => {
96888
97180
  if (!slug2) {
96889
97181
  throw ApiError.badRequest("Missing game slug");
96890
97182
  }
96891
- logger52.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
97183
+ logger53.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
96892
97184
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
96893
97185
  return { success: true, deleted };
96894
97186
  });
96895
97187
  });
96896
97188
 
96897
97189
  // ../api-core/src/controllers/leaderboard.controller.ts
96898
- var logger53, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
97190
+ var logger54, submitScore, getGlobalLeaderboard, getLeaderboard, getUserRank, getUserAllScores, getUserScores, leaderboard;
96899
97191
  var init_leaderboard_controller = __esm(() => {
96900
97192
  init_esm();
96901
97193
  init_schemas_index();
96902
97194
  init_src2();
96903
97195
  init_errors();
96904
97196
  init_utils11();
96905
- logger53 = log.scope("LeaderboardController");
97197
+ logger54 = log.scope("LeaderboardController");
96906
97198
  submitScore = requireAuth(async (ctx) => {
96907
97199
  const gameId = ctx.params.gameId;
96908
97200
  if (!gameId) {
@@ -96915,12 +97207,12 @@ var init_leaderboard_controller = __esm(() => {
96915
97207
  } catch (error2) {
96916
97208
  if (error2 instanceof exports_external.ZodError) {
96917
97209
  const details = formatZodError(error2);
96918
- logger53.warn("Submit score validation failed", { details });
97210
+ logger54.warn("Submit score validation failed", { details });
96919
97211
  throw ApiError.unprocessableEntity("Validation failed", details);
96920
97212
  }
96921
97213
  throw ApiError.badRequest("Invalid JSON body");
96922
97214
  }
96923
- logger53.debug("Submitting score", {
97215
+ logger54.debug("Submitting score", {
96924
97216
  userId: ctx.user.id,
96925
97217
  gameId,
96926
97218
  score: body2.score
@@ -96943,12 +97235,12 @@ var init_leaderboard_controller = __esm(() => {
96943
97235
  } catch (error2) {
96944
97236
  if (error2 instanceof exports_external.ZodError) {
96945
97237
  const details = formatZodError(error2);
96946
- logger53.warn("Get global leaderboard query validation failed", { details });
97238
+ logger54.warn("Get global leaderboard query validation failed", { details });
96947
97239
  throw ApiError.badRequest("Invalid query parameters", details);
96948
97240
  }
96949
97241
  throw ApiError.badRequest("Invalid query parameters");
96950
97242
  }
96951
- logger53.debug("Getting global leaderboard", {
97243
+ logger54.debug("Getting global leaderboard", {
96952
97244
  userId: ctx.user.id,
96953
97245
  gameId,
96954
97246
  ...query
@@ -96971,12 +97263,12 @@ var init_leaderboard_controller = __esm(() => {
96971
97263
  } catch (error2) {
96972
97264
  if (error2 instanceof exports_external.ZodError) {
96973
97265
  const details = formatZodError(error2);
96974
- logger53.warn("Get leaderboard query validation failed", { details });
97266
+ logger54.warn("Get leaderboard query validation failed", { details });
96975
97267
  throw ApiError.badRequest("Invalid query parameters", details);
96976
97268
  }
96977
97269
  throw ApiError.badRequest("Invalid query parameters");
96978
97270
  }
96979
- logger53.debug("Getting leaderboard", {
97271
+ logger54.debug("Getting leaderboard", {
96980
97272
  userId: ctx.user.id,
96981
97273
  gameId,
96982
97274
  ...query
@@ -96988,7 +97280,7 @@ var init_leaderboard_controller = __esm(() => {
96988
97280
  if (!gameId || !userId) {
96989
97281
  throw ApiError.badRequest("Game ID and User ID are required");
96990
97282
  }
96991
- logger53.debug("Getting user rank", {
97283
+ logger54.debug("Getting user rank", {
96992
97284
  requesterId: ctx.user.id,
96993
97285
  gameId,
96994
97286
  targetUserId: userId
@@ -97003,7 +97295,7 @@ var init_leaderboard_controller = __esm(() => {
97003
97295
  const url2 = ctx.url;
97004
97296
  const limit = Math.min(Number(url2.searchParams.get("limit") || "50"), 100);
97005
97297
  const gameId = url2.searchParams.get("gameId") || undefined;
97006
- logger53.debug("Getting user all scores", {
97298
+ logger54.debug("Getting user all scores", {
97007
97299
  requesterId: ctx.user.id,
97008
97300
  targetUserId: userId,
97009
97301
  gameId,
@@ -97018,7 +97310,7 @@ var init_leaderboard_controller = __esm(() => {
97018
97310
  }
97019
97311
  const url2 = ctx.url;
97020
97312
  const limit = Math.min(Number(url2.searchParams.get("limit") || "10"), 100);
97021
- logger53.debug("Getting user scores", {
97313
+ logger54.debug("Getting user scores", {
97022
97314
  requesterId: ctx.user.id,
97023
97315
  gameId,
97024
97316
  targetUserId: userId,
@@ -97038,7 +97330,7 @@ var init_leaderboard_controller = __esm(() => {
97038
97330
 
97039
97331
  // ../api-core/src/controllers/level.controller.ts
97040
97332
  async function listConfigs(ctx) {
97041
- logger54.debug("Listing level configs");
97333
+ logger55.debug("Listing level configs");
97042
97334
  return ctx.services.level.listConfigs();
97043
97335
  }
97044
97336
  async function getConfig(ctx) {
@@ -97050,21 +97342,21 @@ async function getConfig(ctx) {
97050
97342
  if (isNaN(level) || level < 1) {
97051
97343
  throw ApiError.badRequest("Level must be a positive integer");
97052
97344
  }
97053
- logger54.debug("Getting level config", { level });
97345
+ logger55.debug("Getting level config", { level });
97054
97346
  return ctx.services.level.getConfig(level);
97055
97347
  }
97056
- var logger54, getByUser, getProgress, levels;
97348
+ var logger55, getByUser, getProgress, levels;
97057
97349
  var init_level_controller = __esm(() => {
97058
97350
  init_src2();
97059
97351
  init_errors();
97060
97352
  init_utils11();
97061
- logger54 = log.scope("LevelController");
97353
+ logger55 = log.scope("LevelController");
97062
97354
  getByUser = requireNonAnonymous(async (ctx) => {
97063
- logger54.debug("Getting user level", { userId: ctx.user.id });
97355
+ logger55.debug("Getting user level", { userId: ctx.user.id });
97064
97356
  return ctx.services.level.getByUser(ctx.user);
97065
97357
  });
97066
97358
  getProgress = requireNonAnonymous(async (ctx) => {
97067
- logger54.debug("Getting level progress", { userId: ctx.user.id });
97359
+ logger55.debug("Getting level progress", { userId: ctx.user.id });
97068
97360
  return ctx.services.level.getProgress(ctx.user);
97069
97361
  });
97070
97362
  levels = {
@@ -97076,12 +97368,12 @@ var init_level_controller = __esm(() => {
97076
97368
  });
97077
97369
 
97078
97370
  // ../api-core/src/controllers/logs.controller.ts
97079
- var logger55, generateToken, logs;
97371
+ var logger56, generateToken, logs;
97080
97372
  var init_logs_controller = __esm(() => {
97081
97373
  init_src2();
97082
97374
  init_errors();
97083
97375
  init_utils11();
97084
- logger55 = log.scope("LogsController");
97376
+ logger56 = log.scope("LogsController");
97085
97377
  generateToken = requireDeveloper(async (ctx) => {
97086
97378
  const slug2 = ctx.params.slug;
97087
97379
  if (!slug2) {
@@ -97100,7 +97392,7 @@ var init_logs_controller = __esm(() => {
97100
97392
  }
97101
97393
  throw ApiError.badRequest("Invalid JSON body");
97102
97394
  }
97103
- logger55.debug("Generating log stream token", {
97395
+ logger56.debug("Generating log stream token", {
97104
97396
  userId: ctx.user.id,
97105
97397
  slug: slug2,
97106
97398
  environment: body2.environment
@@ -97119,13 +97411,13 @@ var init_logs_controller = __esm(() => {
97119
97411
  });
97120
97412
 
97121
97413
  // ../api-core/src/controllers/lti.controller.ts
97122
- var logger56, getStatus3, lti;
97414
+ var logger57, getStatus3, lti;
97123
97415
  var init_lti_controller = __esm(() => {
97124
97416
  init_src2();
97125
97417
  init_utils11();
97126
- logger56 = log.scope("LtiController");
97418
+ logger57 = log.scope("LtiController");
97127
97419
  getStatus3 = requireNonAnonymous(async (ctx) => {
97128
- logger56.debug("Getting status", { userId: ctx.user.id });
97420
+ logger57.debug("Getting status", { userId: ctx.user.id });
97129
97421
  return ctx.services.lti.getStatus(ctx.user);
97130
97422
  });
97131
97423
  lti = {
@@ -97134,7 +97426,7 @@ var init_lti_controller = __esm(() => {
97134
97426
  });
97135
97427
 
97136
97428
  // ../api-core/src/controllers/map.controller.ts
97137
- var logger57, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97429
+ var logger58, getByIdentifier, getElements, getObjects, createObject, deleteObject, maps2;
97138
97430
  var init_map_controller = __esm(() => {
97139
97431
  init_esm();
97140
97432
  init_schemas_index();
@@ -97142,13 +97434,13 @@ var init_map_controller = __esm(() => {
97142
97434
  init_src4();
97143
97435
  init_errors();
97144
97436
  init_utils11();
97145
- logger57 = log.scope("MapController");
97437
+ logger58 = log.scope("MapController");
97146
97438
  getByIdentifier = requireNonAnonymous(async (ctx) => {
97147
97439
  const identifier = ctx.params.identifier;
97148
97440
  if (!identifier) {
97149
97441
  throw ApiError.badRequest("Missing map identifier");
97150
97442
  }
97151
- logger57.debug("Getting map", { userId: ctx.user.id, identifier });
97443
+ logger58.debug("Getting map", { userId: ctx.user.id, identifier });
97152
97444
  return ctx.services.map.getByIdentifier(identifier);
97153
97445
  });
97154
97446
  getElements = requireNonAnonymous(async (ctx) => {
@@ -97159,7 +97451,7 @@ var init_map_controller = __esm(() => {
97159
97451
  if (!isValidUUID(mapId)) {
97160
97452
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97161
97453
  }
97162
- logger57.debug("Getting map elements", { userId: ctx.user.id, mapId });
97454
+ logger58.debug("Getting map elements", { userId: ctx.user.id, mapId });
97163
97455
  return ctx.services.map.getElements(mapId);
97164
97456
  });
97165
97457
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -97170,7 +97462,7 @@ var init_map_controller = __esm(() => {
97170
97462
  if (!isValidUUID(mapId)) {
97171
97463
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
97172
97464
  }
97173
- logger57.debug("Getting map objects", { userId: ctx.user.id, mapId });
97465
+ logger58.debug("Getting map objects", { userId: ctx.user.id, mapId });
97174
97466
  return ctx.services.map.getObjects(mapId, ctx.user.id);
97175
97467
  });
97176
97468
  createObject = requireNonAnonymous(async (ctx) => {
@@ -97192,12 +97484,12 @@ var init_map_controller = __esm(() => {
97192
97484
  } catch (error2) {
97193
97485
  if (error2 instanceof exports_external.ZodError) {
97194
97486
  const details = formatZodError(error2);
97195
- logger57.warn("Create map object validation failed", { details });
97487
+ logger58.warn("Create map object validation failed", { details });
97196
97488
  throw ApiError.unprocessableEntity("Validation failed", details);
97197
97489
  }
97198
97490
  throw ApiError.badRequest("Invalid JSON body");
97199
97491
  }
97200
- logger57.debug("Creating map object", {
97492
+ logger58.debug("Creating map object", {
97201
97493
  userId: ctx.user.id,
97202
97494
  mapId,
97203
97495
  itemId: body2.itemId,
@@ -97221,7 +97513,7 @@ var init_map_controller = __esm(() => {
97221
97513
  if (!isValidUUID(objectId)) {
97222
97514
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
97223
97515
  }
97224
- logger57.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97516
+ logger58.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
97225
97517
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
97226
97518
  });
97227
97519
  maps2 = {
@@ -97234,14 +97526,14 @@ var init_map_controller = __esm(() => {
97234
97526
  });
97235
97527
 
97236
97528
  // ../api-core/src/controllers/notification.controller.ts
97237
- var logger58, list6, updateStatus, getStats2, create4, deliver, notifications2;
97529
+ var logger59, list6, updateStatus, getStats2, create4, deliver, notifications2;
97238
97530
  var init_notification_controller = __esm(() => {
97239
97531
  init_esm();
97240
97532
  init_schemas_index();
97241
97533
  init_src2();
97242
97534
  init_errors();
97243
97535
  init_utils11();
97244
- logger58 = log.scope("NotificationController");
97536
+ logger59 = log.scope("NotificationController");
97245
97537
  list6 = requireNonAnonymous(async (ctx) => {
97246
97538
  const query = {
97247
97539
  status: ctx.url.searchParams.get("status") || undefined,
@@ -97252,10 +97544,10 @@ var init_notification_controller = __esm(() => {
97252
97544
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
97253
97545
  if (!result.success) {
97254
97546
  const details = formatZodError(result.error);
97255
- logger58.warn("List notifications query validation failed", { details });
97547
+ logger59.warn("List notifications query validation failed", { details });
97256
97548
  throw ApiError.badRequest("Invalid query parameters", details);
97257
97549
  }
97258
- logger58.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97550
+ logger59.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
97259
97551
  return ctx.services.notification.list(ctx.user, result.data);
97260
97552
  });
97261
97553
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -97270,12 +97562,12 @@ var init_notification_controller = __esm(() => {
97270
97562
  } catch (error2) {
97271
97563
  if (error2 instanceof exports_external.ZodError) {
97272
97564
  const details = formatZodError(error2);
97273
- logger58.warn("Update notification status validation failed", { details });
97565
+ logger59.warn("Update notification status validation failed", { details });
97274
97566
  throw ApiError.unprocessableEntity("Invalid request body", details);
97275
97567
  }
97276
97568
  throw ApiError.badRequest("Invalid JSON body");
97277
97569
  }
97278
- logger58.debug("Updating status", {
97570
+ logger59.debug("Updating status", {
97279
97571
  userId: ctx.user.id,
97280
97572
  notificationId,
97281
97573
  status: body2.status
@@ -97285,7 +97577,7 @@ var init_notification_controller = __esm(() => {
97285
97577
  getStats2 = requireNonAnonymous(async (ctx) => {
97286
97578
  const startDate = ctx.url.searchParams.get("startDate");
97287
97579
  const endDate = ctx.url.searchParams.get("endDate");
97288
- logger58.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97580
+ logger59.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
97289
97581
  return ctx.services.notification.getStats(ctx.user, {
97290
97582
  startDate: startDate ? new Date(startDate) : undefined,
97291
97583
  endDate: endDate ? new Date(endDate) : undefined
@@ -97299,12 +97591,12 @@ var init_notification_controller = __esm(() => {
97299
97591
  } catch (error2) {
97300
97592
  if (error2 instanceof exports_external.ZodError) {
97301
97593
  const details = formatZodError(error2);
97302
- logger58.warn("Create notification validation failed", { details });
97594
+ logger59.warn("Create notification validation failed", { details });
97303
97595
  throw ApiError.unprocessableEntity("Invalid request body", details);
97304
97596
  }
97305
97597
  throw ApiError.badRequest("Invalid JSON body");
97306
97598
  }
97307
- logger58.debug("Creating notification", {
97599
+ logger59.debug("Creating notification", {
97308
97600
  userId: ctx.user.id,
97309
97601
  targetUserId: body2.userId,
97310
97602
  type: body2.type
@@ -97322,12 +97614,12 @@ var init_notification_controller = __esm(() => {
97322
97614
  });
97323
97615
  });
97324
97616
  deliver = requireNonAnonymous(async (ctx) => {
97325
- logger58.debug("Delivering notifications", { userId: ctx.user.id });
97617
+ logger59.debug("Delivering notifications", { userId: ctx.user.id });
97326
97618
  try {
97327
97619
  await ctx.services.notification.deliverPending(ctx.user.id);
97328
97620
  return { success: true };
97329
97621
  } catch (error2) {
97330
- logger58.error("Failed to deliver notifications", { error: error2 });
97622
+ logger59.error("Failed to deliver notifications", { error: error2 });
97331
97623
  throw ApiError.internal("Failed to deliver notifications");
97332
97624
  }
97333
97625
  });
@@ -97341,14 +97633,14 @@ var init_notification_controller = __esm(() => {
97341
97633
  });
97342
97634
 
97343
97635
  // ../api-core/src/controllers/realtime.controller.ts
97344
- var logger59, generateToken2, realtime;
97636
+ var logger60, generateToken2, realtime;
97345
97637
  var init_realtime_controller = __esm(() => {
97346
97638
  init_src2();
97347
97639
  init_utils11();
97348
- logger59 = log.scope("RealtimeController");
97640
+ logger60 = log.scope("RealtimeController");
97349
97641
  generateToken2 = requireNonAnonymous(async (ctx) => {
97350
97642
  const gameIdOrSlug = ctx.params.gameId;
97351
- logger59.debug("Generating token", {
97643
+ logger60.debug("Generating token", {
97352
97644
  userId: ctx.user.id,
97353
97645
  gameId: gameIdOrSlug || "global",
97354
97646
  launchId: ctx.launchId
@@ -97361,20 +97653,20 @@ var init_realtime_controller = __esm(() => {
97361
97653
  });
97362
97654
 
97363
97655
  // ../api-core/src/controllers/secrets.controller.ts
97364
- var logger60, listKeys2, setSecrets, deleteSecret, secrets;
97656
+ var logger61, listKeys2, setSecrets, deleteSecret, secrets;
97365
97657
  var init_secrets_controller = __esm(() => {
97366
97658
  init_esm();
97367
97659
  init_schemas_index();
97368
97660
  init_src2();
97369
97661
  init_errors();
97370
97662
  init_utils11();
97371
- logger60 = log.scope("SecretsController");
97663
+ logger61 = log.scope("SecretsController");
97372
97664
  listKeys2 = requireDeveloper(async (ctx) => {
97373
97665
  const slug2 = ctx.params.slug;
97374
97666
  if (!slug2) {
97375
97667
  throw ApiError.badRequest("Missing game slug");
97376
97668
  }
97377
- logger60.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97669
+ logger61.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
97378
97670
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
97379
97671
  return { keys };
97380
97672
  });
@@ -97390,12 +97682,12 @@ var init_secrets_controller = __esm(() => {
97390
97682
  } catch (error2) {
97391
97683
  if (error2 instanceof exports_external.ZodError) {
97392
97684
  const details = formatZodError(error2);
97393
- logger60.warn("Set secrets validation failed", { details });
97685
+ logger61.warn("Set secrets validation failed", { details });
97394
97686
  throw ApiError.unprocessableEntity("Validation failed", details);
97395
97687
  }
97396
97688
  throw ApiError.badRequest("Invalid JSON body");
97397
97689
  }
97398
- logger60.debug("Setting secrets", {
97690
+ logger61.debug("Setting secrets", {
97399
97691
  userId: ctx.user.id,
97400
97692
  slug: slug2,
97401
97693
  keyCount: Object.keys(body2).length
@@ -97412,7 +97704,7 @@ var init_secrets_controller = __esm(() => {
97412
97704
  if (!key) {
97413
97705
  throw ApiError.badRequest("Missing secret key");
97414
97706
  }
97415
- logger60.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97707
+ logger61.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
97416
97708
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
97417
97709
  return { success: true };
97418
97710
  });
@@ -97424,14 +97716,14 @@ var init_secrets_controller = __esm(() => {
97424
97716
  });
97425
97717
 
97426
97718
  // ../api-core/src/controllers/seed.controller.ts
97427
- var logger61, seed2;
97719
+ var logger62, seed2;
97428
97720
  var init_seed_controller = __esm(() => {
97429
97721
  init_esm();
97430
97722
  init_schemas_index();
97431
97723
  init_src2();
97432
97724
  init_errors();
97433
97725
  init_utils11();
97434
- logger61 = log.scope("SeedController");
97726
+ logger62 = log.scope("SeedController");
97435
97727
  seed2 = requireDeveloper(async (ctx) => {
97436
97728
  const slug2 = ctx.params.slug;
97437
97729
  if (!slug2) {
@@ -97444,12 +97736,12 @@ var init_seed_controller = __esm(() => {
97444
97736
  } catch (error2) {
97445
97737
  if (error2 instanceof exports_external.ZodError) {
97446
97738
  const details = formatZodError(error2);
97447
- logger61.warn("Seed database validation failed", { details });
97739
+ logger62.warn("Seed database validation failed", { details });
97448
97740
  throw ApiError.unprocessableEntity("Validation failed", details);
97449
97741
  }
97450
97742
  throw ApiError.badRequest("Invalid JSON body");
97451
97743
  }
97452
- logger61.debug("Seeding database", {
97744
+ logger62.debug("Seeding database", {
97453
97745
  userId: ctx.user.id,
97454
97746
  slug: slug2,
97455
97747
  codeLength: body2.code.length,
@@ -97460,19 +97752,19 @@ var init_seed_controller = __esm(() => {
97460
97752
  });
97461
97753
 
97462
97754
  // ../api-core/src/controllers/session.controller.ts
97463
- var logger62, start2, end, mintToken, sessions2;
97755
+ var logger63, start2, end, mintToken, sessions2;
97464
97756
  var init_session_controller = __esm(() => {
97465
97757
  init_src2();
97466
97758
  init_tunnel();
97467
97759
  init_errors();
97468
97760
  init_utils11();
97469
- logger62 = log.scope("SessionController");
97761
+ logger63 = log.scope("SessionController");
97470
97762
  start2 = requireAuth(async (ctx) => {
97471
97763
  const gameIdOrSlug = ctx.params.gameId;
97472
97764
  if (!gameIdOrSlug) {
97473
97765
  throw ApiError.badRequest("Missing game ID or slug");
97474
97766
  }
97475
- logger62.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97767
+ logger63.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97476
97768
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
97477
97769
  });
97478
97770
  end = requireAuth(async (ctx) => {
@@ -97484,7 +97776,7 @@ var init_session_controller = __esm(() => {
97484
97776
  if (!sessionId) {
97485
97777
  throw ApiError.badRequest("Missing session ID");
97486
97778
  }
97487
- logger62.debug("Ending session", {
97779
+ logger63.debug("Ending session", {
97488
97780
  userId: ctx.user.id,
97489
97781
  gameIdOrSlug,
97490
97782
  sessionId,
@@ -97497,7 +97789,7 @@ var init_session_controller = __esm(() => {
97497
97789
  if (!gameIdOrSlug) {
97498
97790
  throw ApiError.badRequest("Missing game ID or slug");
97499
97791
  }
97500
- logger62.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97792
+ logger63.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
97501
97793
  const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
97502
97794
  let baseUrl;
97503
97795
  if (ctx.config.isLocal) {
@@ -97515,13 +97807,13 @@ var init_session_controller = __esm(() => {
97515
97807
  });
97516
97808
 
97517
97809
  // ../api-core/src/controllers/shop.controller.ts
97518
- var logger63, getShopView, shop;
97810
+ var logger64, getShopView, shop;
97519
97811
  var init_shop_controller = __esm(() => {
97520
97812
  init_src2();
97521
97813
  init_utils11();
97522
- logger63 = log.scope("ShopController");
97814
+ logger64 = log.scope("ShopController");
97523
97815
  getShopView = requireNonAnonymous(async (ctx) => {
97524
- logger63.debug("Getting shop view", { userId: ctx.user.id });
97816
+ logger64.debug("Getting shop view", { userId: ctx.user.id });
97525
97817
  return ctx.services.shop.getShopView(ctx.user);
97526
97818
  });
97527
97819
  shop = {
@@ -97530,7 +97822,7 @@ var init_shop_controller = __esm(() => {
97530
97822
  });
97531
97823
 
97532
97824
  // ../api-core/src/controllers/shop-listing.controller.ts
97533
- var logger64, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97825
+ var logger65, list7, getById4, create5, update5, remove5, listByGame2, getByGameItem, createForGameItem, updateForGameItem, deleteForGameItem, shopListings2;
97534
97826
  var init_shop_listing_controller = __esm(() => {
97535
97827
  init_esm();
97536
97828
  init_schemas_index();
@@ -97538,9 +97830,9 @@ var init_shop_listing_controller = __esm(() => {
97538
97830
  init_src4();
97539
97831
  init_errors();
97540
97832
  init_utils11();
97541
- logger64 = log.scope("ShopListingController");
97833
+ logger65 = log.scope("ShopListingController");
97542
97834
  list7 = requireAdmin(async (ctx) => {
97543
- logger64.debug("Listing shop listings", { userId: ctx.user.id });
97835
+ logger65.debug("Listing shop listings", { userId: ctx.user.id });
97544
97836
  return ctx.services.shopListing.list();
97545
97837
  });
97546
97838
  getById4 = requireAdmin(async (ctx) => {
@@ -97551,7 +97843,7 @@ var init_shop_listing_controller = __esm(() => {
97551
97843
  if (!isValidUUID(listingId)) {
97552
97844
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97553
97845
  }
97554
- logger64.debug("Getting listing", { userId: ctx.user.id, listingId });
97846
+ logger65.debug("Getting listing", { userId: ctx.user.id, listingId });
97555
97847
  return ctx.services.shopListing.getById(listingId);
97556
97848
  });
97557
97849
  create5 = requireAdmin(async (ctx) => {
@@ -97562,12 +97854,12 @@ var init_shop_listing_controller = __esm(() => {
97562
97854
  } catch (error2) {
97563
97855
  if (error2 instanceof exports_external.ZodError) {
97564
97856
  const details = formatZodError(error2);
97565
- logger64.warn("Create shop listing validation failed", { details });
97857
+ logger65.warn("Create shop listing validation failed", { details });
97566
97858
  throw ApiError.unprocessableEntity("Validation failed", details);
97567
97859
  }
97568
97860
  throw ApiError.badRequest("Invalid JSON body");
97569
97861
  }
97570
- logger64.debug("Creating listing", {
97862
+ logger65.debug("Creating listing", {
97571
97863
  userId: ctx.user.id,
97572
97864
  itemId: body2.itemId,
97573
97865
  currencyId: body2.currencyId,
@@ -97590,12 +97882,12 @@ var init_shop_listing_controller = __esm(() => {
97590
97882
  } catch (error2) {
97591
97883
  if (error2 instanceof exports_external.ZodError) {
97592
97884
  const details = formatZodError(error2);
97593
- logger64.warn("Update shop listing validation failed", { details });
97885
+ logger65.warn("Update shop listing validation failed", { details });
97594
97886
  throw ApiError.unprocessableEntity("Validation failed", details);
97595
97887
  }
97596
97888
  throw ApiError.badRequest("Invalid JSON body");
97597
97889
  }
97598
- logger64.debug("Updating listing", {
97890
+ logger65.debug("Updating listing", {
97599
97891
  userId: ctx.user.id,
97600
97892
  listingId,
97601
97893
  price: body2.price,
@@ -97612,7 +97904,7 @@ var init_shop_listing_controller = __esm(() => {
97612
97904
  if (!isValidUUID(listingId)) {
97613
97905
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
97614
97906
  }
97615
- logger64.debug("Deleting listing", { userId: ctx.user.id, listingId });
97907
+ logger65.debug("Deleting listing", { userId: ctx.user.id, listingId });
97616
97908
  await ctx.services.shopListing.delete(listingId);
97617
97909
  });
97618
97910
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -97623,7 +97915,7 @@ var init_shop_listing_controller = __esm(() => {
97623
97915
  if (!isValidUUID(gameId)) {
97624
97916
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
97625
97917
  }
97626
- logger64.debug("Listing game listings", { userId: ctx.user.id, gameId });
97918
+ logger65.debug("Listing game listings", { userId: ctx.user.id, gameId });
97627
97919
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
97628
97920
  });
97629
97921
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -97638,7 +97930,7 @@ var init_shop_listing_controller = __esm(() => {
97638
97930
  if (!isValidUUID(itemId)) {
97639
97931
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97640
97932
  }
97641
- logger64.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97933
+ logger65.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
97642
97934
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
97643
97935
  });
97644
97936
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -97660,12 +97952,12 @@ var init_shop_listing_controller = __esm(() => {
97660
97952
  } catch (error2) {
97661
97953
  if (error2 instanceof exports_external.ZodError) {
97662
97954
  const details = formatZodError(error2);
97663
- logger64.warn("Create game item listing validation failed", { details });
97955
+ logger65.warn("Create game item listing validation failed", { details });
97664
97956
  throw ApiError.unprocessableEntity("Validation failed", details);
97665
97957
  }
97666
97958
  throw ApiError.badRequest("Invalid JSON body");
97667
97959
  }
97668
- logger64.debug("Creating game item listing", {
97960
+ logger65.debug("Creating game item listing", {
97669
97961
  userId: ctx.user.id,
97670
97962
  gameId,
97671
97963
  itemId,
@@ -97693,12 +97985,12 @@ var init_shop_listing_controller = __esm(() => {
97693
97985
  } catch (error2) {
97694
97986
  if (error2 instanceof exports_external.ZodError) {
97695
97987
  const details = formatZodError(error2);
97696
- logger64.warn("Update game item listing validation failed", { details });
97988
+ logger65.warn("Update game item listing validation failed", { details });
97697
97989
  throw ApiError.unprocessableEntity("Validation failed", details);
97698
97990
  }
97699
97991
  throw ApiError.badRequest("Invalid JSON body");
97700
97992
  }
97701
- logger64.debug("Updating game item listing", {
97993
+ logger65.debug("Updating game item listing", {
97702
97994
  userId: ctx.user.id,
97703
97995
  gameId,
97704
97996
  itemId,
@@ -97720,7 +98012,7 @@ var init_shop_listing_controller = __esm(() => {
97720
98012
  if (!isValidUUID(itemId)) {
97721
98013
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
97722
98014
  }
97723
- logger64.debug("Deleting game item listing", {
98015
+ logger65.debug("Deleting game item listing", {
97724
98016
  userId: ctx.user.id,
97725
98017
  gameId,
97726
98018
  itemId
@@ -97747,21 +98039,21 @@ async function getBySlug2(ctx) {
97747
98039
  if (!slug2) {
97748
98040
  throw ApiError.badRequest("Template slug is required");
97749
98041
  }
97750
- logger65.debug("Getting sprite by slug", { slug: slug2 });
98042
+ logger66.debug("Getting sprite by slug", { slug: slug2 });
97751
98043
  return ctx.services.sprite.getBySlug(slug2);
97752
98044
  }
97753
- var logger65, sprites;
98045
+ var logger66, sprites;
97754
98046
  var init_sprite_controller = __esm(() => {
97755
98047
  init_src2();
97756
98048
  init_errors();
97757
- logger65 = log.scope("SpriteController");
98049
+ logger66 = log.scope("SpriteController");
97758
98050
  sprites = {
97759
98051
  getBySlug: getBySlug2
97760
98052
  };
97761
98053
  });
97762
98054
 
97763
98055
  // ../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;
98056
+ 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
98057
  var init_timeback_controller = __esm(() => {
97766
98058
  init_esm();
97767
98059
  init_schemas_index();
@@ -97769,15 +98061,15 @@ var init_timeback_controller = __esm(() => {
97769
98061
  init_src4();
97770
98062
  init_errors();
97771
98063
  init_utils11();
97772
- logger66 = log.scope("TimebackController");
98064
+ logger67 = log.scope("TimebackController");
97773
98065
  getTodayXp = requireNonAnonymous(async (ctx) => {
97774
98066
  const date4 = ctx.url.searchParams.get("date") || undefined;
97775
98067
  const tz = ctx.url.searchParams.get("tz") || undefined;
97776
- logger66.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
98068
+ logger67.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
97777
98069
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
97778
98070
  });
97779
98071
  getTotalXp = requireNonAnonymous(async (ctx) => {
97780
- logger66.debug("Getting total XP", { userId: ctx.user.id });
98072
+ logger67.debug("Getting total XP", { userId: ctx.user.id });
97781
98073
  return ctx.services.timeback.getTotalXp(ctx.user.id);
97782
98074
  });
97783
98075
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -97788,18 +98080,18 @@ var init_timeback_controller = __esm(() => {
97788
98080
  } catch (error2) {
97789
98081
  if (error2 instanceof exports_external.ZodError) {
97790
98082
  const details = formatZodError(error2);
97791
- logger66.warn("Update today XP validation failed", { details });
98083
+ logger67.warn("Update today XP validation failed", { details });
97792
98084
  throw ApiError.unprocessableEntity("Validation failed", details);
97793
98085
  }
97794
98086
  throw ApiError.badRequest("Invalid JSON body");
97795
98087
  }
97796
- logger66.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
98088
+ logger67.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
97797
98089
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
97798
98090
  });
97799
98091
  getXpHistory = requireNonAnonymous(async (ctx) => {
97800
98092
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
97801
98093
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
97802
- logger66.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
98094
+ logger67.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
97803
98095
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
97804
98096
  });
97805
98097
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -97810,18 +98102,18 @@ var init_timeback_controller = __esm(() => {
97810
98102
  } catch (error2) {
97811
98103
  if (error2 instanceof exports_external.ZodError) {
97812
98104
  const details = formatZodError(error2);
97813
- logger66.warn("Populate student validation failed", { details });
98105
+ logger67.warn("Populate student validation failed", { details });
97814
98106
  throw ApiError.unprocessableEntity("Validation failed", details);
97815
98107
  }
97816
98108
  }
97817
- logger66.debug("Populating student", {
98109
+ logger67.debug("Populating student", {
97818
98110
  userId: ctx.user.id,
97819
98111
  hasProvidedNames: Boolean(providedNames)
97820
98112
  });
97821
98113
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
97822
98114
  });
97823
98115
  getUser = requireNonAnonymous(async (ctx) => {
97824
- logger66.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
98116
+ logger67.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
97825
98117
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
97826
98118
  });
97827
98119
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -97829,7 +98121,7 @@ var init_timeback_controller = __esm(() => {
97829
98121
  if (!timebackId) {
97830
98122
  throw ApiError.badRequest("Missing timebackId parameter");
97831
98123
  }
97832
- logger66.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
98124
+ logger67.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
97833
98125
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
97834
98126
  });
97835
98127
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -97840,12 +98132,12 @@ var init_timeback_controller = __esm(() => {
97840
98132
  } catch (error2) {
97841
98133
  if (error2 instanceof exports_external.ZodError) {
97842
98134
  const details = formatZodError(error2);
97843
- logger66.warn("Setup integration validation failed", { details });
98135
+ logger67.warn("Setup integration validation failed", { details });
97844
98136
  throw ApiError.unprocessableEntity("Validation failed", details);
97845
98137
  }
97846
98138
  throw ApiError.badRequest("Invalid JSON body");
97847
98139
  }
97848
- logger66.debug("Setting up integration", {
98140
+ logger67.debug("Setting up integration", {
97849
98141
  userId: ctx.user.id,
97850
98142
  gameId: body2.gameId
97851
98143
  });
@@ -97859,9 +98151,37 @@ var init_timeback_controller = __esm(() => {
97859
98151
  if (!isValidUUID(gameId)) {
97860
98152
  throw ApiError.unprocessableEntity("Invalid gameId format");
97861
98153
  }
97862
- logger66.debug("Getting integrations", { userId: ctx.user.id, gameId });
98154
+ logger67.debug("Getting integrations", { userId: ctx.user.id, gameId });
97863
98155
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
97864
98156
  });
98157
+ updateIntegration = requireDeveloper(async (ctx) => {
98158
+ const { gameId, courseId } = ctx.params;
98159
+ if (!gameId || !courseId) {
98160
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98161
+ }
98162
+ if (!isValidUUID(gameId)) {
98163
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98164
+ }
98165
+ const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
98166
+ logger67.debug("Updating integration", {
98167
+ userId: ctx.user.id,
98168
+ gameId,
98169
+ courseId,
98170
+ fields: Object.keys(body2)
98171
+ });
98172
+ return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
98173
+ });
98174
+ getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
98175
+ const { gameId, courseId } = ctx.params;
98176
+ if (!gameId || !courseId) {
98177
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
98178
+ }
98179
+ if (!isValidUUID(gameId)) {
98180
+ throw ApiError.unprocessableEntity("Invalid gameId format");
98181
+ }
98182
+ logger67.debug("Getting integration config", { userId: ctx.user.id, gameId, courseId });
98183
+ return ctx.services.timeback.getIntegrationConfig(gameId, courseId, ctx.user);
98184
+ });
97865
98185
  verifyIntegration = requireDeveloper(async (ctx) => {
97866
98186
  const gameId = ctx.params.gameId;
97867
98187
  if (!gameId) {
@@ -97870,7 +98190,7 @@ var init_timeback_controller = __esm(() => {
97870
98190
  if (!isValidUUID(gameId)) {
97871
98191
  throw ApiError.unprocessableEntity("Invalid gameId format");
97872
98192
  }
97873
- logger66.debug("Verifying integration", { userId: ctx.user.id, gameId });
98193
+ logger67.debug("Verifying integration", { userId: ctx.user.id, gameId });
97874
98194
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
97875
98195
  });
97876
98196
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -97881,7 +98201,7 @@ var init_timeback_controller = __esm(() => {
97881
98201
  if (!isValidUUID(gameId)) {
97882
98202
  throw ApiError.unprocessableEntity("Invalid gameId format");
97883
98203
  }
97884
- logger66.debug("Getting config", { userId: ctx.user.id, gameId });
98204
+ logger67.debug("Getting config", { userId: ctx.user.id, gameId });
97885
98205
  return ctx.services.timeback.getConfig(gameId, ctx.user);
97886
98206
  });
97887
98207
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -97892,7 +98212,7 @@ var init_timeback_controller = __esm(() => {
97892
98212
  if (!isValidUUID(gameId)) {
97893
98213
  throw ApiError.unprocessableEntity("Invalid gameId format");
97894
98214
  }
97895
- logger66.debug("Deleting integrations", { userId: ctx.user.id, gameId });
98215
+ logger67.debug("Deleting integrations", { userId: ctx.user.id, gameId });
97896
98216
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
97897
98217
  });
97898
98218
  endActivity = requireDeveloper(async (ctx) => {
@@ -97903,7 +98223,7 @@ var init_timeback_controller = __esm(() => {
97903
98223
  } catch (error2) {
97904
98224
  if (error2 instanceof exports_external.ZodError) {
97905
98225
  const details = formatZodError(error2);
97906
- logger66.warn("End activity validation failed", { details });
98226
+ logger67.warn("End activity validation failed", { details });
97907
98227
  throw ApiError.unprocessableEntity("Validation failed", details);
97908
98228
  }
97909
98229
  throw ApiError.badRequest("Invalid JSON body");
@@ -97921,7 +98241,7 @@ var init_timeback_controller = __esm(() => {
97921
98241
  masteredUnits,
97922
98242
  extensions
97923
98243
  } = body2;
97924
- logger66.debug("Ending activity", { userId: ctx.user.id, gameId });
98244
+ logger67.debug("Ending activity", { userId: ctx.user.id, gameId });
97925
98245
  return ctx.services.timeback.endActivity({
97926
98246
  gameId,
97927
98247
  studentId,
@@ -97945,7 +98265,7 @@ var init_timeback_controller = __esm(() => {
97945
98265
  } catch (error2) {
97946
98266
  if (error2 instanceof exports_external.ZodError) {
97947
98267
  const details = formatZodError(error2);
97948
- logger66.warn("Heartbeat validation failed", { details });
98268
+ logger67.warn("Heartbeat validation failed", { details });
97949
98269
  throw ApiError.unprocessableEntity("Validation failed", details);
97950
98270
  }
97951
98271
  throw ApiError.badRequest("Invalid JSON body");
@@ -97961,7 +98281,7 @@ var init_timeback_controller = __esm(() => {
97961
98281
  windowSequence,
97962
98282
  isFinal
97963
98283
  } = body2;
97964
- logger66.debug("Recording heartbeat", {
98284
+ logger67.debug("Recording heartbeat", {
97965
98285
  userId: ctx.user.id,
97966
98286
  gameId,
97967
98287
  runId,
@@ -97986,7 +98306,7 @@ var init_timeback_controller = __esm(() => {
97986
98306
  });
97987
98307
  advanceCourse = requireDeveloper(async (ctx) => {
97988
98308
  const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
97989
- logger66.debug("Advancing student manually", {
98309
+ logger67.debug("Advancing student manually", {
97990
98310
  userId: ctx.user.id,
97991
98311
  gameId: body2.gameId,
97992
98312
  studentId: body2.studentId,
@@ -98027,7 +98347,7 @@ var init_timeback_controller = __esm(() => {
98027
98347
  perCourse: includeOptions.includes("percourse"),
98028
98348
  today: includeOptions.includes("today")
98029
98349
  };
98030
- logger66.debug("Getting student XP", {
98350
+ logger67.debug("Getting student XP", {
98031
98351
  requesterId: ctx.user.id,
98032
98352
  timebackId,
98033
98353
  gameId,
@@ -98049,7 +98369,7 @@ var init_timeback_controller = __esm(() => {
98049
98369
  if (!gameId || !courseId) {
98050
98370
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98051
98371
  }
98052
- logger66.debug("Getting course roster", {
98372
+ logger67.debug("Getting course roster", {
98053
98373
  requesterId: ctx.user.id,
98054
98374
  gameId,
98055
98375
  courseId,
@@ -98066,7 +98386,7 @@ var init_timeback_controller = __esm(() => {
98066
98386
  if (!timebackId || !gameId) {
98067
98387
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
98068
98388
  }
98069
- logger66.debug("Getting student overview", {
98389
+ logger67.debug("Getting student overview", {
98070
98390
  requesterId: ctx.user.id,
98071
98391
  timebackId,
98072
98392
  gameId,
@@ -98080,7 +98400,7 @@ var init_timeback_controller = __esm(() => {
98080
98400
  if (!gameId || !timebackId) {
98081
98401
  throw ApiError.badRequest("Missing gameId or timebackId path parameter");
98082
98402
  }
98083
- logger66.debug("Getting game metrics", {
98403
+ logger67.debug("Getting game metrics", {
98084
98404
  requesterId: ctx.user.id,
98085
98405
  gameId,
98086
98406
  timebackId
@@ -98098,7 +98418,7 @@ var init_timeback_controller = __esm(() => {
98098
98418
  if (!timebackId || !courseId || !gameId) {
98099
98419
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
98100
98420
  }
98101
- logger66.debug("Getting student activity", {
98421
+ logger67.debug("Getting student activity", {
98102
98422
  requesterId: ctx.user.id,
98103
98423
  timebackId,
98104
98424
  courseId,
@@ -98123,7 +98443,7 @@ var init_timeback_controller = __esm(() => {
98123
98443
  if (!timebackId || !courseId || !activityId || !gameId) {
98124
98444
  throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
98125
98445
  }
98126
- logger66.debug("Getting activity detail", {
98446
+ logger67.debug("Getting activity detail", {
98127
98447
  requesterId: ctx.user.id,
98128
98448
  timebackId,
98129
98449
  courseId,
@@ -98141,7 +98461,7 @@ var init_timeback_controller = __esm(() => {
98141
98461
  });
98142
98462
  grantXp = requireDeveloper(async (ctx) => {
98143
98463
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
98144
- logger66.debug("Granting manual XP", {
98464
+ logger67.debug("Granting manual XP", {
98145
98465
  requesterId: ctx.user.id,
98146
98466
  gameId: body2.gameId,
98147
98467
  courseId: body2.courseId,
@@ -98153,7 +98473,7 @@ var init_timeback_controller = __esm(() => {
98153
98473
  });
98154
98474
  adjustTime = requireDeveloper(async (ctx) => {
98155
98475
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
98156
- logger66.debug("Adjusting time spent", {
98476
+ logger67.debug("Adjusting time spent", {
98157
98477
  requesterId: ctx.user.id,
98158
98478
  gameId: body2.gameId,
98159
98479
  courseId: body2.courseId,
@@ -98165,7 +98485,7 @@ var init_timeback_controller = __esm(() => {
98165
98485
  });
98166
98486
  adjustMastery = requireDeveloper(async (ctx) => {
98167
98487
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
98168
- logger66.debug("Adjusting mastered units", {
98488
+ logger67.debug("Adjusting mastered units", {
98169
98489
  requesterId: ctx.user.id,
98170
98490
  gameId: body2.gameId,
98171
98491
  courseId: body2.courseId,
@@ -98175,6 +98495,22 @@ var init_timeback_controller = __esm(() => {
98175
98495
  });
98176
98496
  return ctx.services.timebackAdmin.adjustMasteredUnits(body2, ctx.user);
98177
98497
  });
98498
+ reconcileMasteryForConfigChange = requireDeveloper(async (ctx) => {
98499
+ const body2 = await parseRequestBody(ctx.request, ReconcileMasteryForConfigChangeSchema);
98500
+ logger67.debug("Reconciling mastery completion for config change", {
98501
+ requesterId: ctx.user.id,
98502
+ gameId: body2.gameId,
98503
+ courseId: body2.courseId,
98504
+ oldMasterableUnits: body2.oldMasterableUnits,
98505
+ newMasterableUnits: body2.newMasterableUnits,
98506
+ affectedCount: body2.affectedStudentIds.length
98507
+ });
98508
+ return ctx.services.timebackAdmin.reconcileMasteryForConfigChange(body2.gameId, body2.courseId, ctx.user, {
98509
+ oldMasterableUnits: body2.oldMasterableUnits,
98510
+ newMasterableUnits: body2.newMasterableUnits,
98511
+ affectedStudentIds: body2.affectedStudentIds
98512
+ });
98513
+ });
98178
98514
  searchStudents = requireGameManagementAccess(async (ctx) => {
98179
98515
  const gameId = ctx.params.gameId;
98180
98516
  const courseId = ctx.params.courseId;
@@ -98182,7 +98518,7 @@ var init_timeback_controller = __esm(() => {
98182
98518
  if (!gameId || !courseId) {
98183
98519
  throw ApiError.badRequest("Missing gameId or courseId parameter");
98184
98520
  }
98185
- logger66.debug("Searching students for enrollment", {
98521
+ logger67.debug("Searching students for enrollment", {
98186
98522
  requesterId: ctx.user.id,
98187
98523
  gameId,
98188
98524
  courseId,
@@ -98192,7 +98528,7 @@ var init_timeback_controller = __esm(() => {
98192
98528
  });
98193
98529
  enrollStudent = requireGameManagementAccess(async (ctx) => {
98194
98530
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
98195
- logger66.debug("Enrolling student", {
98531
+ logger67.debug("Enrolling student", {
98196
98532
  requesterId: ctx.user.id,
98197
98533
  gameId: body2.gameId,
98198
98534
  courseId: body2.courseId,
@@ -98202,7 +98538,7 @@ var init_timeback_controller = __esm(() => {
98202
98538
  });
98203
98539
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
98204
98540
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
98205
- logger66.debug("Unenrolling student", {
98541
+ logger67.debug("Unenrolling student", {
98206
98542
  requesterId: ctx.user.id,
98207
98543
  gameId: body2.gameId,
98208
98544
  courseId: body2.courseId,
@@ -98212,7 +98548,7 @@ var init_timeback_controller = __esm(() => {
98212
98548
  });
98213
98549
  reactivateEnrollment = requireGameManagementAccess(async (ctx) => {
98214
98550
  const body2 = await parseRequestBody(ctx.request, ReactivateEnrollmentRequestSchema);
98215
- logger66.debug("Reactivating enrollment", {
98551
+ logger67.debug("Reactivating enrollment", {
98216
98552
  requesterId: ctx.user.id,
98217
98553
  gameId: body2.gameId,
98218
98554
  courseId: body2.courseId,
@@ -98352,6 +98688,8 @@ var init_timeback_controller = __esm(() => {
98352
98688
  getUserById,
98353
98689
  setupIntegration,
98354
98690
  getIntegrations,
98691
+ updateIntegration,
98692
+ getIntegrationConfig,
98355
98693
  verifyIntegration,
98356
98694
  getConfig: getConfig2,
98357
98695
  deleteIntegrations,
@@ -98367,6 +98705,7 @@ var init_timeback_controller = __esm(() => {
98367
98705
  grantXp,
98368
98706
  adjustTime,
98369
98707
  adjustMastery,
98708
+ reconcileMasteryForConfigChange,
98370
98709
  searchStudents,
98371
98710
  enrollStudent,
98372
98711
  unenrollStudent,
@@ -98388,14 +98727,14 @@ var init_timeback_controller = __esm(() => {
98388
98727
  });
98389
98728
 
98390
98729
  // ../api-core/src/controllers/upload.controller.ts
98391
- var logger67, initiate;
98730
+ var logger68, initiate;
98392
98731
  var init_upload_controller = __esm(() => {
98393
98732
  init_esm();
98394
98733
  init_schemas_index();
98395
98734
  init_src2();
98396
98735
  init_errors();
98397
98736
  init_utils11();
98398
- logger67 = log.scope("UploadController");
98737
+ logger68 = log.scope("UploadController");
98399
98738
  initiate = requireDeveloper(async (ctx) => {
98400
98739
  let body2;
98401
98740
  try {
@@ -98404,34 +98743,34 @@ var init_upload_controller = __esm(() => {
98404
98743
  } catch (error2) {
98405
98744
  if (error2 instanceof exports_external.ZodError) {
98406
98745
  const details = formatZodError(error2);
98407
- logger67.warn("Initiate upload validation failed", { details });
98746
+ logger68.warn("Initiate upload validation failed", { details });
98408
98747
  throw ApiError.unprocessableEntity("Validation failed", details);
98409
98748
  }
98410
98749
  throw ApiError.badRequest("Invalid JSON body");
98411
98750
  }
98412
- logger67.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98751
+ logger68.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
98413
98752
  return ctx.services.upload.initiate(body2, ctx.user);
98414
98753
  });
98415
98754
  });
98416
98755
 
98417
98756
  // ../api-core/src/controllers/user.controller.ts
98418
- var logger68, getMe, getDemoProfile, updateDemoProfile, users2;
98757
+ var logger69, getMe, getDemoProfile, updateDemoProfile, users2;
98419
98758
  var init_user_controller = __esm(() => {
98420
98759
  init_schemas_index();
98421
98760
  init_src2();
98422
98761
  init_utils11();
98423
- logger68 = log.scope("UserController");
98762
+ logger69 = log.scope("UserController");
98424
98763
  getMe = requireNonAnonymous(async (ctx) => {
98425
- logger68.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98764
+ logger69.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
98426
98765
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
98427
98766
  });
98428
98767
  getDemoProfile = requireAnonymous(async (ctx) => {
98429
- logger68.debug("Getting demo profile", { userId: ctx.user.id });
98768
+ logger69.debug("Getting demo profile", { userId: ctx.user.id });
98430
98769
  return ctx.services.user.getDemoProfile(ctx.user.id);
98431
98770
  });
98432
98771
  updateDemoProfile = requireAnonymous(async (ctx) => {
98433
98772
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
98434
- logger68.debug("Updating demo profile", {
98773
+ logger69.debug("Updating demo profile", {
98435
98774
  userId: ctx.user.id,
98436
98775
  displayName: body2.displayName
98437
98776
  });
@@ -98445,13 +98784,13 @@ var init_user_controller = __esm(() => {
98445
98784
  });
98446
98785
 
98447
98786
  // ../api-core/src/controllers/verify.controller.ts
98448
- var logger69;
98787
+ var logger70;
98449
98788
  var init_verify_controller = __esm(() => {
98450
98789
  init_schemas_index();
98451
98790
  init_src2();
98452
98791
  init_errors();
98453
98792
  init_utils11();
98454
- logger69 = log.scope("VerifyController");
98793
+ logger70 = log.scope("VerifyController");
98455
98794
  });
98456
98795
 
98457
98796
  // ../api-core/src/controllers/index.ts
@@ -98765,7 +99104,7 @@ var init_uploads = __esm(() => {
98765
99104
  });
98766
99105
 
98767
99106
  // src/routes/platform/games/deploy.ts
98768
- var logger70, gameDeployRouter;
99107
+ var logger71, gameDeployRouter;
98769
99108
  var init_deploy = __esm(() => {
98770
99109
  init_drizzle_orm();
98771
99110
  init_dist4();
@@ -98775,7 +99114,7 @@ var init_deploy = __esm(() => {
98775
99114
  init_src2();
98776
99115
  init_api();
98777
99116
  init_uploads();
98778
- logger70 = log.scope("SandboxDeploy");
99117
+ logger71 = log.scope("SandboxDeploy");
98779
99118
  gameDeployRouter = new Hono2;
98780
99119
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
98781
99120
  const user = c2.get("user");
@@ -98901,7 +99240,7 @@ var init_deploy = __esm(() => {
98901
99240
  completedAt: now2
98902
99241
  };
98903
99242
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
98904
- logger70.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
99243
+ logger71.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
98905
99244
  return c2.json({
98906
99245
  id: insertedJob.id,
98907
99246
  status: "succeeded",
@@ -99548,7 +99887,7 @@ function verifyMockToken(idToken) {
99548
99887
  throw new Error("Invalid LTI token format");
99549
99888
  }
99550
99889
  }
99551
- var logger71, ltiRouter;
99890
+ var logger72, ltiRouter;
99552
99891
  var init_lti = __esm(() => {
99553
99892
  init_drizzle_orm();
99554
99893
  init_dist4();
@@ -99558,7 +99897,7 @@ var init_lti = __esm(() => {
99558
99897
  init_src2();
99559
99898
  init_constants();
99560
99899
  init_api();
99561
- logger71 = log.scope("SandboxLti");
99900
+ logger72 = log.scope("SandboxLti");
99562
99901
  ltiRouter = new Hono2;
99563
99902
  ltiRouter.post("/launch", async (c2) => {
99564
99903
  const db2 = c2.get("db");
@@ -99576,7 +99915,7 @@ var init_lti = __esm(() => {
99576
99915
  claims = verifyMockToken(idToken);
99577
99916
  } catch (error2) {
99578
99917
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99579
- logger71.error("LTI token verification failed", { error: errorMessage });
99918
+ logger72.error("LTI token verification failed", { error: errorMessage });
99580
99919
  return c2.json({
99581
99920
  error: "invalid_token",
99582
99921
  message: errorMessage
@@ -99584,7 +99923,7 @@ var init_lti = __esm(() => {
99584
99923
  }
99585
99924
  const validationError = validateLtiClaims(claims);
99586
99925
  if (validationError) {
99587
- logger71.warn("LTI claims validation failed", {
99926
+ logger72.warn("LTI claims validation failed", {
99588
99927
  error: validationError,
99589
99928
  sub: claims.sub
99590
99929
  });
@@ -99604,7 +99943,7 @@ var init_lti = __esm(() => {
99604
99943
  createdAt: new Date,
99605
99944
  updatedAt: new Date
99606
99945
  });
99607
- logger71.info("LTI launch successful", { userId: user.id });
99946
+ logger72.info("LTI launch successful", { userId: user.id });
99608
99947
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
99609
99948
  const currentHost = new URL(c2.req.url).hostname;
99610
99949
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -99612,7 +99951,7 @@ var init_lti = __esm(() => {
99612
99951
  return c2.redirect(redirectPath);
99613
99952
  } catch (error2) {
99614
99953
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
99615
- logger71.error("Unexpected error during LTI launch", { error: errorMessage });
99954
+ logger72.error("Unexpected error during LTI launch", { error: errorMessage });
99616
99955
  return c2.json({
99617
99956
  error: "unexpected_error",
99618
99957
  message: "An unexpected error occurred during LTI launch"