@playcademy/sandbox 0.3.18 → 0.3.19-beta.1

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