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