@playcademy/vite-plugin 0.2.34-beta.2 → 0.2.35-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 (2) hide show
  1. package/dist/index.js +853 -502
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -25353,7 +25353,7 @@ var package_default;
25353
25353
  var init_package = __esm(() => {
25354
25354
  package_default = {
25355
25355
  name: "@playcademy/sandbox",
25356
- version: "0.3.18-beta.2",
25356
+ version: "0.3.19-beta.1",
25357
25357
  description: "Local development server for Playcademy game development",
25358
25358
  type: "module",
25359
25359
  exports: {
@@ -54361,6 +54361,22 @@ var init_pure = __esm(() => {
54361
54361
  var init_src4 = __esm(() => {
54362
54362
  init_pure();
54363
54363
  });
54364
+ function formatGradeLabel(grade) {
54365
+ switch (grade) {
54366
+ case -1: {
54367
+ return "Pre-K";
54368
+ }
54369
+ case 0: {
54370
+ return "Kindergarten";
54371
+ }
54372
+ case 13: {
54373
+ return "AP";
54374
+ }
54375
+ default: {
54376
+ return `Grade ${grade}`;
54377
+ }
54378
+ }
54379
+ }
54364
54380
  function isColumnType(column2, columnTypes) {
54365
54381
  return columnTypes.includes(column2.columnType);
54366
54382
  }
@@ -55109,6 +55125,8 @@ var TIMEBACK_SUBJECTS4;
55109
55125
  var TimebackGradeSchema;
55110
55126
  var TimebackSubjectSchema;
55111
55127
  var UpdateTimebackXpRequestSchema;
55128
+ var CourseGoalsSchema;
55129
+ var UpdateGameTimebackIntegrationRequestSchema;
55112
55130
  var TimebackActivityDataSchema;
55113
55131
  var EndActivityRequestSchema;
55114
55132
  var GameActivityMetricsSchema;
@@ -55122,9 +55140,13 @@ var TimebackBaseConfigSchema;
55122
55140
  var PlatformTimebackSetupRequestSchema;
55123
55141
  var AdminTimebackMutationBaseSchema;
55124
55142
  var AdminAttributionDateSchema;
55143
+ var ADMIN_GRANT_XP_MIN = -1e5;
55144
+ var ADMIN_GRANT_XP_MAX = 1e5;
55145
+ var ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE;
55125
55146
  var GrantTimebackXpRequestSchema;
55126
55147
  var AdjustTimebackTimeRequestSchema;
55127
55148
  var AdjustTimebackMasteryRequestSchema;
55149
+ var ReconcileMasteryForConfigChangeSchema;
55128
55150
  var EnrollStudentRequestSchema;
55129
55151
  var UnenrollStudentRequestSchema;
55130
55152
  var ReactivateEnrollmentRequestSchema;
@@ -55156,6 +55178,24 @@ var init_schemas11 = __esm(() => {
55156
55178
  xp: exports_external.number().min(0, "XP must be a non-negative number"),
55157
55179
  userTimestamp: exports_external.string().datetime().optional()
55158
55180
  });
55181
+ CourseGoalsSchema = exports_external.object({
55182
+ dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
55183
+ dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
55184
+ dailyActiveMinutes: exports_external.number().int().nonnegative().nullable().optional(),
55185
+ dailyAccuracy: exports_external.number().int().min(0).max(100).nullable().optional(),
55186
+ dailyMasteredUnits: exports_external.number().int().nonnegative().nullable().optional()
55187
+ });
55188
+ UpdateGameTimebackIntegrationRequestSchema = exports_external.object({
55189
+ title: exports_external.string().trim().min(1).optional(),
55190
+ courseCode: exports_external.string().trim().min(1).optional(),
55191
+ subject: TimebackSubjectSchema.optional(),
55192
+ totalXp: exports_external.number().int().nonnegative().nullable().optional(),
55193
+ masterableUnits: exports_external.number().int().nonnegative().nullable().optional(),
55194
+ goals: CourseGoalsSchema.optional(),
55195
+ publishStatus: exports_external.enum(["draft", "testing", "published", "deactivated"]).nullable().optional(),
55196
+ isSupplemental: exports_external.boolean().optional(),
55197
+ timebackVisible: exports_external.boolean().nullable().optional()
55198
+ });
55159
55199
  TimebackActivityDataSchema = exports_external.object({
55160
55200
  activityId: exports_external.string().min(1),
55161
55201
  activityName: exports_external.string().optional(),
@@ -55309,8 +55349,9 @@ var init_schemas11 = __esm(() => {
55309
55349
  });
55310
55350
  }
55311
55351
  });
55352
+ ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE = `Amount must be between ${ADMIN_GRANT_XP_MIN} and ${ADMIN_GRANT_XP_MAX}`;
55312
55353
  GrantTimebackXpRequestSchema = AdminTimebackMutationBaseSchema.extend({
55313
- xp: exports_external.number().min(-100, "Amount must be between -100 and 100").max(100, "Amount must be between -100 and 100").refine((value) => value !== 0, { message: "Amount cannot be 0" }),
55354
+ xp: exports_external.number().min(ADMIN_GRANT_XP_MIN, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE).max(ADMIN_GRANT_XP_MAX, ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE).refine((value) => value !== 0, { message: "Amount cannot be 0" }),
55314
55355
  date: AdminAttributionDateSchema.optional(),
55315
55356
  useCurrentTime: exports_external.boolean().optional()
55316
55357
  });
@@ -55324,6 +55365,13 @@ var init_schemas11 = __esm(() => {
55324
55365
  date: AdminAttributionDateSchema.optional(),
55325
55366
  useCurrentTime: exports_external.boolean().optional()
55326
55367
  });
55368
+ ReconcileMasteryForConfigChangeSchema = exports_external.object({
55369
+ gameId: exports_external.string().uuid(),
55370
+ courseId: exports_external.string().min(1),
55371
+ oldMasterableUnits: exports_external.number().int().positive(),
55372
+ newMasterableUnits: exports_external.number().int().positive(),
55373
+ affectedStudentIds: exports_external.array(exports_external.string().min(1)).min(1).max(500)
55374
+ });
55327
55375
  EnrollStudentRequestSchema = exports_external.object({
55328
55376
  gameId: exports_external.string().uuid(),
55329
55377
  courseId: exports_external.string().min(1),
@@ -55472,6 +55520,63 @@ function compareEnrollmentsByRecency(a, b) {
55472
55520
  var init_timeback_admin_util = __esm(() => {
55473
55521
  init_errors();
55474
55522
  });
55523
+ async function upsertMasteryCompletionEntry(params) {
55524
+ const { client, courseId, studentId, appName, action } = params;
55525
+ const ids = deriveSourcedIds(courseId);
55526
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
55527
+ const resultId = `${lineItemId}:${studentId}:completion`;
55528
+ if (action === "complete") {
55529
+ await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
55530
+ sourcedId: lineItemId,
55531
+ title: "Mastery Completion",
55532
+ status: ONEROSTER_STATUS.active,
55533
+ course: { sourcedId: ids.course },
55534
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
55535
+ });
55536
+ await client.oneroster.assessmentResults.upsert(resultId, {
55537
+ sourcedId: resultId,
55538
+ status: ONEROSTER_STATUS.active,
55539
+ assessmentLineItem: { sourcedId: lineItemId },
55540
+ student: { sourcedId: studentId },
55541
+ score: 100,
55542
+ scoreDate: new Date().toISOString(),
55543
+ scoreStatus: SCORE_STATUS.fullyGraded,
55544
+ inProgress: "false",
55545
+ metadata: {
55546
+ isMasteryCompletion: true,
55547
+ adminAction: true,
55548
+ appName
55549
+ }
55550
+ });
55551
+ } else {
55552
+ try {
55553
+ await client.oneroster.assessmentResults.upsert(resultId, {
55554
+ sourcedId: resultId,
55555
+ status: ONEROSTER_STATUS.active,
55556
+ assessmentLineItem: { sourcedId: lineItemId },
55557
+ student: { sourcedId: studentId },
55558
+ score: 0,
55559
+ scoreDate: new Date().toISOString(),
55560
+ scoreStatus: SCORE_STATUS.notSubmitted,
55561
+ inProgress: "true",
55562
+ metadata: {
55563
+ isMasteryCompletion: true,
55564
+ adminAction: true,
55565
+ appName
55566
+ }
55567
+ });
55568
+ } catch {
55569
+ logger17.debug("No completion entry to revoke", { studentId, courseId });
55570
+ }
55571
+ }
55572
+ }
55573
+ var logger17;
55574
+ var init_timeback_mastery_completion_util = __esm(() => {
55575
+ init_src2();
55576
+ init_constants4();
55577
+ init_utils6();
55578
+ logger17 = log.scope("timeback-mastery-completion");
55579
+ });
55475
55580
  function isRecord2(value) {
55476
55581
  return typeof value === "object" && value !== null;
55477
55582
  }
@@ -55827,7 +55932,7 @@ class TimebackAdminService {
55827
55932
  }
55828
55933
  requireClient() {
55829
55934
  if (!this.deps.timeback) {
55830
- logger17.error("Timeback client not available in context");
55935
+ logger18.error("Timeback client not available in context");
55831
55936
  throw new ValidationError("Timeback integration not available in this environment");
55832
55937
  }
55833
55938
  return this.deps.timeback;
@@ -56047,7 +56152,7 @@ class TimebackAdminService {
56047
56152
  });
56048
56153
  return [enrollmentId, this.summarizeAnalyticsFacts(analytics.facts)];
56049
56154
  } catch (error) {
56050
- logger17.warn("Failed to load enrollment analytics summary", {
56155
+ logger18.warn("Failed to load enrollment analytics summary", {
56051
56156
  enrollmentId,
56052
56157
  error: error instanceof Error ? error.message : String(error)
56053
56158
  });
@@ -56077,7 +56182,7 @@ class TimebackAdminService {
56077
56182
  const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
56078
56183
  return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
56079
56184
  } catch (error) {
56080
- logger17.warn("Failed to load recent Caliper activity", {
56185
+ logger18.warn("Failed to load recent Caliper activity", {
56081
56186
  studentId,
56082
56187
  gameId: source.gameId,
56083
56188
  sourceMode: source.sourceMode,
@@ -56256,7 +56361,7 @@ class TimebackAdminService {
56256
56361
  }) : undefined;
56257
56362
  return {
56258
56363
  courseId: integration.courseId,
56259
- title: enrollment?.course.title || `${integration.subject} Grade ${integration.grade}`,
56364
+ title: enrollment?.course.title || `${integration.subject} ${formatGradeLabel(integration.grade)}`,
56260
56365
  grade: integration.grade,
56261
56366
  subject: integration.subject,
56262
56367
  enrollmentId: enrollment?.id || null,
@@ -56403,60 +56508,44 @@ class TimebackAdminService {
56403
56508
  const wasMastered = currentMastered >= masterableUnits;
56404
56509
  const willBeMastered = currentMastered + data.units >= masterableUnits;
56405
56510
  if (wasMastered !== willBeMastered) {
56406
- const ids = deriveSourcedIds(data.courseId);
56407
- const lineItemId = `${ids.course}-mastery-completion-assessment`;
56408
- const resultId = `${lineItemId}:${data.studentId}:completion`;
56409
- if (willBeMastered) {
56410
- await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
56411
- sourcedId: lineItemId,
56412
- title: "Mastery Completion",
56413
- status: ONEROSTER_STATUS.active,
56414
- course: { sourcedId: ids.course },
56415
- ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
56416
- });
56417
- await client.oneroster.assessmentResults.upsert(resultId, {
56418
- sourcedId: resultId,
56419
- status: ONEROSTER_STATUS.active,
56420
- assessmentLineItem: { sourcedId: lineItemId },
56421
- student: { sourcedId: data.studentId },
56422
- score: 100,
56423
- scoreDate: new Date().toISOString(),
56424
- scoreStatus: SCORE_STATUS.fullyGraded,
56425
- inProgress: "false",
56426
- metadata: {
56427
- isMasteryCompletion: true,
56428
- adminAction: true,
56429
- appName
56430
- }
56431
- });
56432
- } else {
56433
- try {
56434
- await client.oneroster.assessmentResults.upsert(resultId, {
56435
- sourcedId: resultId,
56436
- status: ONEROSTER_STATUS.active,
56437
- assessmentLineItem: { sourcedId: lineItemId },
56438
- student: { sourcedId: data.studentId },
56439
- score: 0,
56440
- scoreDate: new Date().toISOString(),
56441
- scoreStatus: SCORE_STATUS.notSubmitted,
56442
- inProgress: "true",
56443
- metadata: {
56444
- isMasteryCompletion: true,
56445
- adminAction: true,
56446
- appName
56447
- }
56448
- });
56449
- } catch {
56450
- logger17.debug("No completion entry to revoke", {
56451
- studentId: data.studentId,
56452
- courseId: data.courseId
56453
- });
56454
- }
56455
- }
56511
+ await upsertMasteryCompletionEntry({
56512
+ client,
56513
+ courseId: data.courseId,
56514
+ studentId: data.studentId,
56515
+ appName,
56516
+ action: willBeMastered ? "complete" : "revoke"
56517
+ });
56456
56518
  }
56457
56519
  }
56458
56520
  return { status: "ok" };
56459
56521
  }
56522
+ async reconcileMasteryForConfigChange(gameId, courseId, user, context) {
56523
+ const { client, appName } = await this.resolveAdminMutationContext(gameId, courseId, user);
56524
+ const action = context.newMasterableUnits < context.oldMasterableUnits ? "complete" : "revoke";
56525
+ const failed = [];
56526
+ let processed = 0;
56527
+ await TimebackAdminService.runWithConcurrency(context.affectedStudentIds, 8, async (studentId) => {
56528
+ try {
56529
+ await upsertMasteryCompletionEntry({
56530
+ client,
56531
+ courseId,
56532
+ studentId,
56533
+ appName,
56534
+ action
56535
+ });
56536
+ processed++;
56537
+ } catch (error) {
56538
+ logger18.warn("Failed to reconcile mastery completion for student", {
56539
+ studentId,
56540
+ courseId,
56541
+ action,
56542
+ error: error instanceof Error ? error.message : String(error)
56543
+ });
56544
+ failed.push(studentId);
56545
+ }
56546
+ });
56547
+ return { processed, failed };
56548
+ }
56460
56549
  async searchStudentsForEnrollment(gameId, courseId, query, user) {
56461
56550
  const client = this.requireClient();
56462
56551
  await this.deps.validateGameManagementAccess(user, gameId);
@@ -56483,7 +56572,7 @@ class TimebackAdminService {
56483
56572
  const response = await client["request"](endpoint, "GET");
56484
56573
  allUsers = response.users || [];
56485
56574
  } catch (error) {
56486
- logger17.warn("Failed to search OneRoster users", {
56575
+ logger18.warn("Failed to search OneRoster users", {
56487
56576
  query: trimmedQuery,
56488
56577
  error: error instanceof Error ? error.message : String(error)
56489
56578
  });
@@ -56628,7 +56717,7 @@ class TimebackAdminService {
56628
56717
  return results;
56629
56718
  }
56630
56719
  }
56631
- var logger17;
56720
+ var logger18;
56632
56721
  var init_timeback_admin_service = __esm(() => {
56633
56722
  init_drizzle_orm();
56634
56723
  init_src();
@@ -56641,8 +56730,9 @@ var init_timeback_admin_service = __esm(() => {
56641
56730
  init_errors();
56642
56731
  init_timeback_admin_metrics_util();
56643
56732
  init_timeback_admin_util();
56733
+ init_timeback_mastery_completion_util();
56644
56734
  init_timeback_util();
56645
- logger17 = log.scope("TimebackAdminService");
56735
+ logger18 = log.scope("TimebackAdminService");
56646
56736
  });
56647
56737
  var __esm5 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
56648
56738
  var TIMEBACK_API_URLS4;
@@ -56915,7 +57005,7 @@ class TimebackAssessmentsService {
56915
57005
  isActive: row.bankActive
56916
57006
  };
56917
57007
  } catch {
56918
- logger18.warn("Failed to fetch QTI test metadata", {
57008
+ logger19.warn("Failed to fetch QTI test metadata", {
56919
57009
  identifier: row.qtiTestIdentifier
56920
57010
  });
56921
57011
  return {
@@ -56969,7 +57059,7 @@ class TimebackAssessmentsService {
56969
57059
  });
56970
57060
  } catch (error) {
56971
57061
  if (error instanceof TimebackApiError && error.status === 409) {
56972
- logger18.info("QTI test already exists (idempotent retry)", {
57062
+ logger19.info("QTI test already exists (idempotent retry)", {
56973
57063
  qtiTestIdentifier: input.qtiTestIdentifier
56974
57064
  });
56975
57065
  } else {
@@ -56982,7 +57072,7 @@ class TimebackAssessmentsService {
56982
57072
  qtiTestIdentifier: input.qtiTestIdentifier,
56983
57073
  sortOrder: maxSortOrder + 1
56984
57074
  }).returning();
56985
- logger18.info("Assessment created", {
57075
+ logger19.info("Assessment created", {
56986
57076
  integrationId,
56987
57077
  qtiTestIdentifier: input.qtiTestIdentifier
56988
57078
  });
@@ -57004,13 +57094,13 @@ class TimebackAssessmentsService {
57004
57094
  }
57005
57095
  await client.qti.tests.delete(qtiTestIdentifier);
57006
57096
  } catch (error) {
57007
- logger18.warn("Partial QTI cleanup during assessment deletion", {
57097
+ logger19.warn("Partial QTI cleanup during assessment deletion", {
57008
57098
  qtiTestIdentifier,
57009
57099
  error: error instanceof Error ? error.message : String(error)
57010
57100
  });
57011
57101
  }
57012
57102
  await this.deps.db.delete(gameTimebackAssessmentTests).where(eq(gameTimebackAssessmentTests.id, row.id));
57013
- logger18.info("Assessment deleted", { integrationId, qtiTestIdentifier });
57103
+ logger19.info("Assessment deleted", { integrationId, qtiTestIdentifier });
57014
57104
  }
57015
57105
  async reorderAssessments(integrationId, identifiers) {
57016
57106
  await this.requireIntegration(integrationId);
@@ -57055,7 +57145,7 @@ class TimebackAssessmentsService {
57055
57145
  return client.qti.tests.reorderItems(qtiTestIdentifier, partId, sectionId, items2);
57056
57146
  }
57057
57147
  async activateAssessment(integrationId, qtiTestIdentifier) {
57058
- logger18.debug("Activating assessment", { integrationId, qtiTestIdentifier });
57148
+ logger19.debug("Activating assessment", { integrationId, qtiTestIdentifier });
57059
57149
  const client = this.requireClient();
57060
57150
  const integration = await this.requireIntegration(integrationId);
57061
57151
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -57093,7 +57183,7 @@ class TimebackAssessmentsService {
57093
57183
  });
57094
57184
  }
57095
57185
  } catch {
57096
- logger18.warn("Failed to reactivate existing child resource, will create new", {
57186
+ logger19.warn("Failed to reactivate existing child resource, will create new", {
57097
57187
  childResourceId
57098
57188
  });
57099
57189
  childResourceId = null;
@@ -57106,7 +57196,7 @@ class TimebackAssessmentsService {
57106
57196
  const resourceUrl = resource.metadata?.url;
57107
57197
  if (resourceUrl === qtiTestUrl) {
57108
57198
  childResourceId = resourceId;
57109
- logger18.info("Found existing child Resource for QTI test (idempotent retry)", {
57199
+ logger19.info("Found existing child Resource for QTI test (idempotent retry)", {
57110
57200
  qtiTestIdentifier,
57111
57201
  childResourceId
57112
57202
  });
@@ -57115,7 +57205,7 @@ class TimebackAssessmentsService {
57115
57205
  } catch {}
57116
57206
  }
57117
57207
  if (!childResourceId) {
57118
- logger18.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
57208
+ logger19.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
57119
57209
  const childResult = await client.oneroster.resources.create({
57120
57210
  resource: {
57121
57211
  status: "active",
@@ -57143,10 +57233,10 @@ class TimebackAssessmentsService {
57143
57233
  }
57144
57234
  }
57145
57235
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: childResourceId, bankActive: true }).where(eq(gameTimebackAssessmentTests.id, row.id));
57146
- logger18.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
57236
+ logger19.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
57147
57237
  }
57148
57238
  async deactivateAssessment(integrationId, qtiTestIdentifier) {
57149
- logger18.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
57239
+ logger19.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
57150
57240
  const client = this.requireClient();
57151
57241
  const integration = await this.requireIntegration(integrationId);
57152
57242
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -57159,7 +57249,7 @@ class TimebackAssessmentsService {
57159
57249
  const childResourceId = row.bankResourceId;
57160
57250
  const bankIds = deriveAssessmentBankIds(integration.courseId);
57161
57251
  try {
57162
- logger18.debug("Reading parent resource for deactivation", {
57252
+ logger19.debug("Reading parent resource for deactivation", {
57163
57253
  resourceId: bankIds.resource
57164
57254
  });
57165
57255
  const parentResource = await client.oneroster.resources.get(bankIds.resource);
@@ -57178,22 +57268,22 @@ class TimebackAssessmentsService {
57178
57268
  });
57179
57269
  }
57180
57270
  } catch (error) {
57181
- logger18.warn("Failed to update parent resource during deactivation", {
57271
+ logger19.warn("Failed to update parent resource during deactivation", {
57182
57272
  bankResourceId: bankIds.resource,
57183
57273
  error: error instanceof Error ? error.message : String(error)
57184
57274
  });
57185
57275
  }
57186
57276
  try {
57187
- logger18.debug("Deleting child resource", { childResourceId });
57277
+ logger19.debug("Deleting child resource", { childResourceId });
57188
57278
  await client.oneroster.resources.delete(childResourceId);
57189
57279
  } catch (error) {
57190
- logger18.warn("Failed to delete child resource (may already be deleted)", {
57280
+ logger19.warn("Failed to delete child resource (may already be deleted)", {
57191
57281
  childResourceId,
57192
57282
  error: error instanceof Error ? error.message : String(error)
57193
57283
  });
57194
57284
  }
57195
57285
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankActive: false }).where(eq(gameTimebackAssessmentTests.id, row.id));
57196
- logger18.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
57286
+ logger19.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
57197
57287
  }
57198
57288
  isAssessmentActive(row) {
57199
57289
  return row.bankActive;
@@ -57224,14 +57314,14 @@ class TimebackAssessmentsService {
57224
57314
  status: "tobedeleted"
57225
57315
  });
57226
57316
  } catch (error) {
57227
- logger18.warn("Failed to delete component resource", { error });
57317
+ logger19.warn("Failed to delete component resource", { error });
57228
57318
  }
57229
57319
  for (const row of activeRows) {
57230
57320
  if (row.bankResourceId) {
57231
57321
  try {
57232
57322
  await client.oneroster.resources.delete(row.bankResourceId);
57233
57323
  } catch (error) {
57234
- logger18.warn("Failed to delete child resource", {
57324
+ logger19.warn("Failed to delete child resource", {
57235
57325
  childResourceId: row.bankResourceId,
57236
57326
  error
57237
57327
  });
@@ -57241,17 +57331,17 @@ class TimebackAssessmentsService {
57241
57331
  try {
57242
57332
  await client.oneroster.resources.delete(bankIds.resource);
57243
57333
  } catch (error) {
57244
- logger18.warn("Failed to delete parent resource", { error });
57334
+ logger19.warn("Failed to delete parent resource", { error });
57245
57335
  }
57246
57336
  try {
57247
57337
  await client.oneroster.courseComponents.update(bankIds.component, {
57248
57338
  status: "tobedeleted"
57249
57339
  });
57250
57340
  } catch (error) {
57251
- logger18.warn("Failed to delete course component", { error });
57341
+ logger19.warn("Failed to delete course component", { error });
57252
57342
  }
57253
57343
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: null, bankActive: false }).where(eq(gameTimebackAssessmentTests.integrationId, integrationId));
57254
- logger18.info("Bank destroyed", { integrationId });
57344
+ logger19.info("Bank destroyed", { integrationId });
57255
57345
  }
57256
57346
  requireClient() {
57257
57347
  if (!this.deps.timeback) {
@@ -57280,7 +57370,7 @@ class TimebackAssessmentsService {
57280
57370
  async ensureBank(integration) {
57281
57371
  const client = this.requireClient();
57282
57372
  const bankIds = deriveAssessmentBankIds(integration.courseId);
57283
- logger18.debug("Ensuring assessment bank hierarchy", {
57373
+ logger19.debug("Ensuring assessment bank hierarchy", {
57284
57374
  courseId: integration.courseId,
57285
57375
  bankIds
57286
57376
  });
@@ -57297,7 +57387,7 @@ class TimebackAssessmentsService {
57297
57387
  });
57298
57388
  } catch (error) {
57299
57389
  if (error instanceof TimebackApiError && error.status === 409) {
57300
- logger18.debug("Course component already exists", { sourcedId: bankIds.component });
57390
+ logger19.debug("Course component already exists", { sourcedId: bankIds.component });
57301
57391
  } else {
57302
57392
  throw error;
57303
57393
  }
@@ -57321,7 +57411,7 @@ class TimebackAssessmentsService {
57321
57411
  });
57322
57412
  } catch (error) {
57323
57413
  if (error instanceof TimebackApiError && error.status === 409) {
57324
- logger18.debug("Parent resource already exists", { sourcedId: bankIds.resource });
57414
+ logger19.debug("Parent resource already exists", { sourcedId: bankIds.resource });
57325
57415
  } else {
57326
57416
  throw error;
57327
57417
  }
@@ -57341,14 +57431,14 @@ class TimebackAssessmentsService {
57341
57431
  });
57342
57432
  } catch (error) {
57343
57433
  if (error instanceof TimebackApiError && error.status === 409) {
57344
- logger18.debug("Component resource already exists", {
57434
+ logger19.debug("Component resource already exists", {
57345
57435
  sourcedId: bankIds.componentResource
57346
57436
  });
57347
57437
  } else {
57348
57438
  throw error;
57349
57439
  }
57350
57440
  }
57351
- logger18.info("Assessment bank hierarchy created", {
57441
+ logger19.info("Assessment bank hierarchy created", {
57352
57442
  courseId: integration.courseId
57353
57443
  });
57354
57444
  }
@@ -57363,7 +57453,7 @@ class TimebackAssessmentsService {
57363
57453
  return Math.max(...rows.map((r) => r.sortOrder));
57364
57454
  }
57365
57455
  }
57366
- var logger18;
57456
+ var logger19;
57367
57457
  var init_timeback_assessments_service = __esm(() => {
57368
57458
  init_drizzle_orm();
57369
57459
  init_tables_index();
@@ -57372,7 +57462,7 @@ var init_timeback_assessments_service = __esm(() => {
57372
57462
  init_errors4();
57373
57463
  init_utils6();
57374
57464
  init_errors();
57375
- logger18 = log.scope("TimebackAssessmentsService");
57465
+ logger19 = log.scope("TimebackAssessmentsService");
57376
57466
  });
57377
57467
  async function promoteCompletedCourse({
57378
57468
  db: db2,
@@ -57386,7 +57476,7 @@ async function promoteCompletedCourse({
57386
57476
  });
57387
57477
  const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
57388
57478
  if (!nextIntegration) {
57389
- logger19.debug("Skipping promotion because no next course is configured", {
57479
+ logger20.debug("Skipping promotion because no next course is configured", {
57390
57480
  gameId: currentIntegration.gameId,
57391
57481
  studentId,
57392
57482
  grade: currentIntegration.grade,
@@ -57403,7 +57493,7 @@ async function promoteCompletedCourse({
57403
57493
  const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
57404
57494
  if (!currentEnrollment) {
57405
57495
  if (nextEnrollment) {
57406
- logger19.debug("Skipping promotion because student is already on the next course", {
57496
+ logger20.debug("Skipping promotion because student is already on the next course", {
57407
57497
  gameId: currentIntegration.gameId,
57408
57498
  studentId,
57409
57499
  grade: currentIntegration.grade,
@@ -57417,7 +57507,7 @@ async function promoteCompletedCourse({
57417
57507
  nextCourseId: nextIntegration.courseId
57418
57508
  };
57419
57509
  }
57420
- logger19.debug("Skipping promotion because student is not enrolled in the current course", {
57510
+ logger20.debug("Skipping promotion because student is not enrolled in the current course", {
57421
57511
  gameId: currentIntegration.gameId,
57422
57512
  studentId,
57423
57513
  grade: currentIntegration.grade,
@@ -57448,7 +57538,7 @@ async function promoteCompletedCourse({
57448
57538
  client.invalidateEnrollments(studentId);
57449
57539
  }
57450
57540
  }
57451
- logger19.info("Promoted student to next course", {
57541
+ logger20.info("Promoted student to next course", {
57452
57542
  gameId: currentIntegration.gameId,
57453
57543
  studentId,
57454
57544
  subject: currentIntegration.subject,
@@ -57463,14 +57553,14 @@ async function promoteCompletedCourse({
57463
57553
  nextCourseId: nextIntegration.courseId
57464
57554
  };
57465
57555
  }
57466
- var logger19;
57556
+ var logger20;
57467
57557
  var init_timeback_promotion_util = __esm(() => {
57468
57558
  init_drizzle_orm();
57469
57559
  init_tables_index();
57470
57560
  init_src2();
57471
- logger19 = log.scope("TimebackPromotion");
57561
+ logger20 = log.scope("TimebackPromotion");
57472
57562
  });
57473
- var logger20;
57563
+ var logger21;
57474
57564
  var TimebackService;
57475
57565
  var init_timeback_service = __esm(() => {
57476
57566
  init_drizzle_orm();
@@ -57482,7 +57572,7 @@ var init_timeback_service = __esm(() => {
57482
57572
  init_errors();
57483
57573
  init_timeback_promotion_util();
57484
57574
  init_timeback_util();
57485
- logger20 = log.scope("TimebackService");
57575
+ logger21 = log.scope("TimebackService");
57486
57576
  TimebackService = class TimebackService2 {
57487
57577
  static HEARTBEAT_DEDUPE_TTL_MS = 300000;
57488
57578
  static processedHeartbeatWindows = new Map;
@@ -57528,7 +57618,7 @@ var init_timeback_service = __esm(() => {
57528
57618
  }
57529
57619
  requireClient() {
57530
57620
  if (!this.deps.timeback) {
57531
- logger20.error("Timeback client not available in context");
57621
+ logger21.error("Timeback client not available in context");
57532
57622
  throw new ValidationError("Timeback integration not available in this environment");
57533
57623
  }
57534
57624
  return this.deps.timeback;
@@ -57581,7 +57671,7 @@ var init_timeback_service = __esm(() => {
57581
57671
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
57582
57672
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
57583
57673
  if (!result) {
57584
- logger20.error("Daily XP upsert returned no rows", { userId, date: targetDate });
57674
+ logger21.error("Daily XP upsert returned no rows", { userId, date: targetDate });
57585
57675
  throw new InternalError("Failed to update daily XP record");
57586
57676
  }
57587
57677
  return { xp: result.xp, date: result.date.toISOString() };
@@ -57612,7 +57702,7 @@ var init_timeback_service = __esm(() => {
57612
57702
  columns: { id: true, timebackId: true }
57613
57703
  });
57614
57704
  if (dbUser?.timebackId) {
57615
- logger20.info("Student already onboarded", { userId: user.id });
57705
+ logger21.info("Student already onboarded", { userId: user.id });
57616
57706
  return { status: "already_populated" };
57617
57707
  }
57618
57708
  let timebackId;
@@ -57621,7 +57711,7 @@ var init_timeback_service = __esm(() => {
57621
57711
  const existingUser = await client.oneroster.users.findByEmail(user.email);
57622
57712
  timebackId = existingUser.sourcedId;
57623
57713
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
57624
- logger20.info("Found existing student in OneRoster", {
57714
+ logger21.info("Found existing student in OneRoster", {
57625
57715
  userId: user.id,
57626
57716
  timebackId
57627
57717
  });
@@ -57650,7 +57740,7 @@ var init_timeback_service = __esm(() => {
57650
57740
  }
57651
57741
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
57652
57742
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
57653
- logger20.info("Created student in OneRoster", { userId: user.id, timebackId });
57743
+ logger21.info("Created student in OneRoster", { userId: user.id, timebackId });
57654
57744
  }
57655
57745
  const assessments = await this.fetchAssessments(timebackId);
57656
57746
  await db2.transaction(async (tx) => {
@@ -57684,7 +57774,7 @@ var init_timeback_service = __esm(() => {
57684
57774
  }
57685
57775
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
57686
57776
  if (!updated) {
57687
- logger20.error("User Timeback ID update returned no rows", {
57777
+ logger21.error("User Timeback ID update returned no rows", {
57688
57778
  userId: user.id,
57689
57779
  timebackId
57690
57780
  });
@@ -57708,13 +57798,13 @@ var init_timeback_service = __esm(() => {
57708
57798
  }
57709
57799
  offset += limit;
57710
57800
  }
57711
- logger20.debug("Fetched assessments", {
57801
+ logger21.debug("Fetched assessments", {
57712
57802
  studentSourcedId,
57713
57803
  totalCount: allAssessments.length
57714
57804
  });
57715
57805
  return allAssessments;
57716
57806
  } catch (error) {
57717
- logger20.warn("Failed to fetch assessments", { studentSourcedId, error });
57807
+ logger21.warn("Failed to fetch assessments", { studentSourcedId, error });
57718
57808
  return [];
57719
57809
  }
57720
57810
  }
@@ -57827,7 +57917,7 @@ var init_timeback_service = __esm(() => {
57827
57917
  masterableUnits: derivedMasterableUnits
57828
57918
  } = courseConfig;
57829
57919
  if (!isTimebackSubject(subjectInput)) {
57830
- logger20.warn("Invalid Timeback subject in course config", {
57920
+ logger21.warn("Invalid Timeback subject in course config", {
57831
57921
  subject: subjectInput,
57832
57922
  courseCode,
57833
57923
  title
@@ -57835,7 +57925,7 @@ var init_timeback_service = __esm(() => {
57835
57925
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
57836
57926
  }
57837
57927
  if (!isTimebackGrade(grade)) {
57838
- logger20.warn("Invalid Timeback grade in course config", {
57928
+ logger21.warn("Invalid Timeback grade in course config", {
57839
57929
  grade,
57840
57930
  courseCode,
57841
57931
  title
@@ -57847,7 +57937,7 @@ var init_timeback_service = __esm(() => {
57847
57937
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
57848
57938
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
57849
57939
  if (typeof totalXp !== "number") {
57850
- logger20.warn("Course missing totalXp in Timeback config", {
57940
+ logger21.warn("Course missing totalXp in Timeback config", {
57851
57941
  courseCode,
57852
57942
  title
57853
57943
  });
@@ -57863,7 +57953,9 @@ var init_timeback_service = __esm(() => {
57863
57953
  courseCode,
57864
57954
  level,
57865
57955
  gradingScheme: "STANDARD",
57866
- metadata: metadata2
57956
+ metadata: TimebackService2.patchCourseMetadata(metadata2, totalXp, {
57957
+ masterableUnits: masterableUnits ?? null
57958
+ })
57867
57959
  },
57868
57960
  component: {
57869
57961
  ...baseConfig.component,
@@ -57888,7 +57980,7 @@ var init_timeback_service = __esm(() => {
57888
57980
  const existingIntegration = existing.find((i2) => i2.grade === grade && i2.subject === subject);
57889
57981
  if (existingIntegration) {
57890
57982
  await client.update(existingIntegration.courseId, fullConfig);
57891
- const [updated] = await db2.update(gameTimebackIntegrations).set({ totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
57983
+ const [updated] = await db2.update(gameTimebackIntegrations).set({ subject, totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
57892
57984
  if (updated) {
57893
57985
  integrations.push(this.toGameTimebackIntegration(updated));
57894
57986
  }
@@ -57913,6 +58005,60 @@ var init_timeback_service = __esm(() => {
57913
58005
  });
57914
58006
  return rows.map((row) => this.toGameTimebackIntegration(row));
57915
58007
  }
58008
+ async getIntegrationConfig(gameId, courseId, user) {
58009
+ const client = this.requireClient();
58010
+ await this.deps.validateGameManagementAccess(user, gameId);
58011
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
58012
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
58013
+ });
58014
+ if (!integration) {
58015
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
58016
+ }
58017
+ const config2 = await client.getConfig(courseId);
58018
+ return this.toGameTimebackIntegrationConfig(integration, config2);
58019
+ }
58020
+ async updateIntegration(gameId, courseId, user, patch) {
58021
+ const client = this.requireClient();
58022
+ await this.deps.validateDeveloperAccess(user, gameId);
58023
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
58024
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
58025
+ });
58026
+ if (!integration) {
58027
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
58028
+ }
58029
+ const timebackConfig = await client.getConfig(courseId);
58030
+ const liveSubject = timebackConfig.course.subjects[0];
58031
+ const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
58032
+ if (!isTimebackSubject(subject)) {
58033
+ throw new ValidationError(`Invalid subject "${subject}"`);
58034
+ }
58035
+ if (subject !== integration.subject) {
58036
+ const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
58037
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
58038
+ });
58039
+ if (subjectConflict && subjectConflict.id !== integration.id) {
58040
+ throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
58041
+ }
58042
+ }
58043
+ const totalXp = "totalXp" in patch ? patch.totalXp ?? null : TimebackService2.getTotalXpFromConfig(timebackConfig) ?? integration.totalXp ?? null;
58044
+ const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : TimebackService2.getMasterableUnitsFromConfig(timebackConfig);
58045
+ await client.update(courseId, TimebackService2.patchTimebackConfig(timebackConfig, {
58046
+ ...patch,
58047
+ subject,
58048
+ totalXp,
58049
+ masterableUnits,
58050
+ grade: integration.grade
58051
+ }));
58052
+ const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
58053
+ subject,
58054
+ totalXp,
58055
+ updatedAt: new Date
58056
+ }).where(eq(gameTimebackIntegrations.id, integration.id)).returning();
58057
+ if (!updated) {
58058
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
58059
+ }
58060
+ return this.toGameTimebackIntegration(updated);
58061
+ }
57916
58062
  async verifyIntegration(gameId, user) {
57917
58063
  const client = this.requireClient();
57918
58064
  const db2 = this.deps.db;
@@ -57970,6 +58116,146 @@ var init_timeback_service = __esm(() => {
57970
58116
  }
57971
58117
  await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
57972
58118
  }
58119
+ static getMasterableUnitsFromConfig(config2) {
58120
+ const playcademyMetadata = config2.resource.metadata?.playcademy;
58121
+ if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
58122
+ return null;
58123
+ }
58124
+ return playcademyMetadata?.mastery?.masterableUnits ?? null;
58125
+ }
58126
+ static getTotalXpFromConfig(config2) {
58127
+ const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
58128
+ if (typeof courseMetadata?.metrics?.totalXp === "number") {
58129
+ return courseMetadata.metrics.totalXp;
58130
+ }
58131
+ const resourceMetadata = config2.resource.metadata;
58132
+ if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
58133
+ return resourceMetadata.xp;
58134
+ }
58135
+ return null;
58136
+ }
58137
+ static patchCourseMetadata(metadata2, totalXp, options) {
58138
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
58139
+ const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
58140
+ const metrics = { ...currentMetrics };
58141
+ if (totalXp === null) {
58142
+ delete metrics.totalXp;
58143
+ } else {
58144
+ metrics.totalXp = totalXp;
58145
+ }
58146
+ if (options?.masterableUnits !== undefined) {
58147
+ if (options.masterableUnits === null) {
58148
+ delete metrics.totalLessons;
58149
+ } else {
58150
+ metrics.totalLessons = options.masterableUnits;
58151
+ }
58152
+ }
58153
+ metrics.totalGrades = 1;
58154
+ if (Object.keys(metrics).length > 0) {
58155
+ nextMetadata.metrics = metrics;
58156
+ } else {
58157
+ delete nextMetadata.metrics;
58158
+ }
58159
+ const goals = options?.goals;
58160
+ if (goals !== undefined) {
58161
+ if (goals === null) {
58162
+ delete nextMetadata.goals;
58163
+ } else {
58164
+ const currentGoals = isRecord2(nextMetadata.goals) ? nextMetadata.goals : {};
58165
+ const nextGoals = { ...currentGoals };
58166
+ for (const [key, value] of Object.entries(goals)) {
58167
+ if (value === null) {
58168
+ delete nextGoals[key];
58169
+ } else if (value !== undefined) {
58170
+ nextGoals[key] = value;
58171
+ }
58172
+ }
58173
+ if (Object.keys(nextGoals).length > 0) {
58174
+ nextMetadata.goals = nextGoals;
58175
+ } else {
58176
+ delete nextMetadata.goals;
58177
+ }
58178
+ }
58179
+ }
58180
+ if (options?.publishStatus !== undefined) {
58181
+ if (options.publishStatus === null) {
58182
+ delete nextMetadata.publishStatus;
58183
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
58184
+ delete alphaLearn.publishStatus;
58185
+ if (Object.keys(alphaLearn).length > 0) {
58186
+ nextMetadata.AlphaLearn = alphaLearn;
58187
+ } else {
58188
+ delete nextMetadata.AlphaLearn;
58189
+ }
58190
+ } else {
58191
+ nextMetadata.publishStatus = options.publishStatus;
58192
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
58193
+ alphaLearn.publishStatus = options.publishStatus === "published" ? "active" : options.publishStatus;
58194
+ nextMetadata.AlphaLearn = alphaLearn;
58195
+ }
58196
+ }
58197
+ if (options?.isSupplemental !== undefined) {
58198
+ nextMetadata.isSupplemental = options.isSupplemental;
58199
+ }
58200
+ if (options?.timebackVisible !== undefined) {
58201
+ if (options.timebackVisible === null) {
58202
+ delete nextMetadata.timebackVisible;
58203
+ } else {
58204
+ nextMetadata.timebackVisible = options.timebackVisible;
58205
+ }
58206
+ }
58207
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
58208
+ }
58209
+ static patchResourceMetadata(metadata2, options) {
58210
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
58211
+ const playcademyMetadata = isRecord2(nextMetadata.playcademy) ? { ...nextMetadata.playcademy } : {};
58212
+ const masteryMetadata = isRecord2(playcademyMetadata.mastery) ? { ...playcademyMetadata.mastery } : {};
58213
+ nextMetadata.subject = options.subject;
58214
+ nextMetadata.grades = [options.grade];
58215
+ if (options.totalXp === null) {
58216
+ delete nextMetadata.xp;
58217
+ } else {
58218
+ nextMetadata.xp = options.totalXp;
58219
+ }
58220
+ if (options.masterableUnits === null) {
58221
+ delete masteryMetadata.masterableUnits;
58222
+ } else {
58223
+ masteryMetadata.masterableUnits = options.masterableUnits;
58224
+ }
58225
+ if (Object.keys(masteryMetadata).length > 0) {
58226
+ playcademyMetadata.mastery = masteryMetadata;
58227
+ } else {
58228
+ delete playcademyMetadata.mastery;
58229
+ }
58230
+ if (Object.keys(playcademyMetadata).length > 0) {
58231
+ nextMetadata.playcademy = playcademyMetadata;
58232
+ } else {
58233
+ delete nextMetadata.playcademy;
58234
+ }
58235
+ return nextMetadata;
58236
+ }
58237
+ static patchTimebackConfig(config2, patch) {
58238
+ return {
58239
+ ...config2,
58240
+ course: {
58241
+ ...config2.course,
58242
+ title: patch.title ?? config2.course.title,
58243
+ courseCode: patch.courseCode ?? config2.course.courseCode,
58244
+ subjects: [patch.subject],
58245
+ metadata: TimebackService2.patchCourseMetadata(config2.course.metadata, patch.totalXp, {
58246
+ masterableUnits: patch.masterableUnits,
58247
+ goals: patch.goals,
58248
+ publishStatus: patch.publishStatus,
58249
+ isSupplemental: patch.isSupplemental,
58250
+ timebackVisible: patch.timebackVisible
58251
+ })
58252
+ },
58253
+ resource: {
58254
+ ...config2.resource,
58255
+ metadata: TimebackService2.patchResourceMetadata(config2.resource.metadata, patch)
58256
+ }
58257
+ };
58258
+ }
57973
58259
  toGameTimebackIntegration(integration) {
57974
58260
  return {
57975
58261
  id: integration.id,
@@ -57983,6 +58269,21 @@ var init_timeback_service = __esm(() => {
57983
58269
  lastVerifiedAt: integration.lastVerifiedAt ?? null
57984
58270
  };
57985
58271
  }
58272
+ toGameTimebackIntegrationConfig(integration, config2) {
58273
+ const subject = config2.course.subjects[0] ?? integration.subject;
58274
+ if (!isTimebackSubject(subject)) {
58275
+ throw new ValidationError(`Invalid subject "${subject}"`);
58276
+ }
58277
+ return {
58278
+ integration: this.toGameTimebackIntegration(integration),
58279
+ title: config2.course.title,
58280
+ courseCode: config2.course.courseCode,
58281
+ subject,
58282
+ totalXp: TimebackService2.getTotalXpFromConfig(config2) ?? integration.totalXp ?? null,
58283
+ masterableUnits: TimebackService2.getMasterableUnitsFromConfig(config2),
58284
+ metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
58285
+ };
58286
+ }
57986
58287
  async endActivity({
57987
58288
  gameId,
57988
58289
  studentId,
@@ -58048,7 +58349,7 @@ var init_timeback_service = __esm(() => {
58048
58349
  ...runId ? { runId } : {}
58049
58350
  });
58050
58351
  }
58051
- logger20.info("Recorded activity completion", {
58352
+ logger21.info("Recorded activity completion", {
58052
58353
  gameId,
58053
58354
  courseId: integration.courseId,
58054
58355
  studentId,
@@ -58102,7 +58403,7 @@ var init_timeback_service = __esm(() => {
58102
58403
  masteredUnits: masteryStatus.masteredUnits,
58103
58404
  masterableUnits: masteryStatus.masterableUnits
58104
58405
  };
58105
- logger20.debug("Skipping course advancement because mastery is incomplete", {
58406
+ logger21.debug("Skipping course advancement because mastery is incomplete", {
58106
58407
  gameId,
58107
58408
  studentId,
58108
58409
  subject: currentIntegration.subject,
@@ -58120,7 +58421,7 @@ var init_timeback_service = __esm(() => {
58120
58421
  studentId,
58121
58422
  enrollments
58122
58423
  });
58123
- logger20.info("Manually advanced student", {
58424
+ logger21.info("Manually advanced student", {
58124
58425
  gameId,
58125
58426
  studentId,
58126
58427
  subject: currentIntegration.subject,
@@ -58153,7 +58454,7 @@ var init_timeback_service = __esm(() => {
58153
58454
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
58154
58455
  const effectiveResumeId = resumeId ?? runId;
58155
58456
  if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
58156
- logger20.debug("Skipping duplicate heartbeat window", {
58457
+ logger21.debug("Skipping duplicate heartbeat window", {
58157
58458
  gameId,
58158
58459
  studentId,
58159
58460
  runId,
@@ -58166,7 +58467,7 @@ var init_timeback_service = __esm(() => {
58166
58467
  await this.deps.validateDeveloperAccess(user, gameId);
58167
58468
  const inFlightHeartbeat = TimebackService2.getInFlightHeartbeatWindow(heartbeatWindowKey);
58168
58469
  if (inFlightHeartbeat) {
58169
- logger20.debug("Joining in-flight heartbeat window", {
58470
+ logger21.debug("Joining in-flight heartbeat window", {
58170
58471
  gameId,
58171
58472
  studentId,
58172
58473
  runId,
@@ -58203,7 +58504,7 @@ var init_timeback_service = __esm(() => {
58203
58504
  });
58204
58505
  }
58205
58506
  TimebackService2.markHeartbeatWindowProcessed(heartbeatWindowKey);
58206
- logger20.debug("Recorded heartbeat", {
58507
+ logger21.debug("Recorded heartbeat", {
58207
58508
  gameId,
58208
58509
  courseId: integration.courseId,
58209
58510
  studentId,
@@ -58238,7 +58539,7 @@ var init_timeback_service = __esm(() => {
58238
58539
  });
58239
58540
  courseIds = integrations.map((i2) => i2.courseId);
58240
58541
  if (courseIds.length === 0) {
58241
- logger20.debug("No integrations found for game, returning 0 XP", {
58542
+ logger21.debug("No integrations found for game, returning 0 XP", {
58242
58543
  timebackId,
58243
58544
  gameId: options.gameId,
58244
58545
  grade: options.grade,
@@ -58255,7 +58556,7 @@ var init_timeback_service = __esm(() => {
58255
58556
  courseIds: courseIds.length > 0 ? courseIds : undefined,
58256
58557
  include: options?.include
58257
58558
  });
58258
- logger20.debug("Retrieved student XP", {
58559
+ logger21.debug("Retrieved student XP", {
58259
58560
  timebackId,
58260
58561
  gameId: options?.gameId,
58261
58562
  grade: options?.grade,
@@ -58283,15 +58584,15 @@ class UploadService {
58283
58584
  const { fileName, gameId } = request;
58284
58585
  const bucketName = this.deps.uploadBucket;
58285
58586
  if (!bucketName) {
58286
- logger21.error("Upload bucket not configured in environment");
58587
+ logger22.error("Upload bucket not configured in environment");
58287
58588
  throw new ValidationError("Upload bucket not configured");
58288
58589
  }
58289
58590
  await this.deps.validateDeveloperAccess(user, gameId);
58290
58591
  const version2 = ulid();
58291
58592
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
58292
- logger21.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
58593
+ logger22.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
58293
58594
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
58294
- logger21.info("Presigned URL generated", {
58595
+ logger22.info("Presigned URL generated", {
58295
58596
  userId: user.id,
58296
58597
  gameId,
58297
58598
  version: version2
@@ -58304,12 +58605,12 @@ class UploadService {
58304
58605
  };
58305
58606
  }
58306
58607
  }
58307
- var logger21;
58608
+ var logger22;
58308
58609
  var init_upload_service = __esm(() => {
58309
58610
  init_node();
58310
58611
  init_src2();
58311
58612
  init_errors();
58312
- logger21 = log.scope("UploadService");
58613
+ logger22 = log.scope("UploadService");
58313
58614
  });
58314
58615
  function createPlatformServices(deps) {
58315
58616
  const {
@@ -58610,7 +58911,7 @@ class AchievementService {
58610
58911
  results.push(result);
58611
58912
  }
58612
58913
  }
58613
- logger22.debug("Listed current achievements", { userId: user.id, count: results.length });
58914
+ logger23.debug("Listed current achievements", { userId: user.id, count: results.length });
58614
58915
  return results;
58615
58916
  }
58616
58917
  async listHistory(user, limit) {
@@ -58628,14 +58929,14 @@ class AchievementService {
58628
58929
  createdAt: c.createdAt,
58629
58930
  scopeKey: c.scopeKey
58630
58931
  }));
58631
- logger22.debug("Listed achievement history", { userId: user.id, count: results.length });
58932
+ logger23.debug("Listed achievement history", { userId: user.id, count: results.length });
58632
58933
  return results;
58633
58934
  }
58634
58935
  async submitProgress(achievementId, user) {
58635
58936
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
58636
58937
  broadcast: false
58637
58938
  });
58638
- logger22.debug("Submitted progress", {
58939
+ logger23.debug("Submitted progress", {
58639
58940
  userId: user.id,
58640
58941
  achievementId,
58641
58942
  wasNewClaim
@@ -58667,7 +58968,7 @@ class AchievementService {
58667
58968
  rewardCredits
58668
58969
  }).returning();
58669
58970
  if (!newClaim) {
58670
- logger22.error("Achievement claim insert returned no rows", {
58971
+ logger23.error("Achievement claim insert returned no rows", {
58671
58972
  userId,
58672
58973
  achievementId,
58673
58974
  scopeKey
@@ -58676,7 +58977,7 @@ class AchievementService {
58676
58977
  }
58677
58978
  await this.deps.addCredits(userId, rewardCredits);
58678
58979
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
58679
- logger22.info("Awarded achievement", {
58980
+ logger23.info("Awarded achievement", {
58680
58981
  userId,
58681
58982
  achievementId,
58682
58983
  scopeKey,
@@ -58713,7 +59014,7 @@ class AchievementService {
58713
59014
  return { title, body: body2 };
58714
59015
  }
58715
59016
  }
58716
- var logger22;
59017
+ var logger23;
58717
59018
  var init_achievement_service = __esm(() => {
58718
59019
  init_drizzle_orm();
58719
59020
  init_tables_index();
@@ -58722,7 +59023,7 @@ var init_achievement_service = __esm(() => {
58722
59023
  init_errors();
58723
59024
  init_leaderboard_util();
58724
59025
  init_scope_util();
58725
- logger22 = log.scope("AchievementService");
59026
+ logger23 = log.scope("AchievementService");
58726
59027
  });
58727
59028
 
58728
59029
  class InventoryService {
@@ -58749,7 +59050,7 @@ class InventoryService {
58749
59050
  },
58750
59051
  updatedAt: inventoryItems.updatedAt
58751
59052
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
58752
- logger23.debug("Listed inventory", { userId: user.id, count: inventory.length });
59053
+ logger24.debug("Listed inventory", { userId: user.id, count: inventory.length });
58753
59054
  return inventory;
58754
59055
  }
58755
59056
  async addItem(itemId, quantity, user) {
@@ -58766,7 +59067,7 @@ class InventoryService {
58766
59067
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
58767
59068
  return inserted?.quantity ?? 0;
58768
59069
  });
58769
- logger23.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
59070
+ logger24.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
58770
59071
  return { newTotal };
58771
59072
  }
58772
59073
  async removeItem(itemId, quantity, user) {
@@ -58777,7 +59078,7 @@ class InventoryService {
58777
59078
  }
58778
59079
  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);
58779
59080
  if (!currentItem) {
58780
- logger23.warn("Insufficient inventory for removal", {
59081
+ logger24.warn("Insufficient inventory for removal", {
58781
59082
  userId: user.id,
58782
59083
  itemId,
58783
59084
  requestedQuantity: quantity
@@ -58787,13 +59088,13 @@ class InventoryService {
58787
59088
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
58788
59089
  return updated?.quantity ?? 0;
58789
59090
  });
58790
- logger23.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
59091
+ logger24.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
58791
59092
  return { newTotal };
58792
59093
  }
58793
59094
  async addCredits(userId, amount) {
58794
59095
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
58795
59096
  if (!creditsItem) {
58796
- logger23.error("Primary currency not found", {
59097
+ logger24.error("Primary currency not found", {
58797
59098
  userId,
58798
59099
  amount
58799
59100
  });
@@ -58806,17 +59107,17 @@ class InventoryService {
58806
59107
  updatedAt: new Date
58807
59108
  }
58808
59109
  });
58809
- logger23.debug("Added credits", { userId, amount });
59110
+ logger24.debug("Added credits", { userId, amount });
58810
59111
  }
58811
59112
  }
58812
- var logger23;
59113
+ var logger24;
58813
59114
  var init_inventory_service = __esm(() => {
58814
59115
  init_drizzle_orm();
58815
59116
  init_src();
58816
59117
  init_tables_index();
58817
59118
  init_src2();
58818
59119
  init_errors();
58819
- logger23 = log.scope("InventoryService");
59120
+ logger24 = log.scope("InventoryService");
58820
59121
  });
58821
59122
 
58822
59123
  class LeaderboardService {
@@ -58847,7 +59148,7 @@ class LeaderboardService {
58847
59148
  sessionId
58848
59149
  }).returning();
58849
59150
  if (!newScore) {
58850
- logger24.error("Score insert returned no rows", { userId, gameId, score: input.score });
59151
+ logger25.error("Score insert returned no rows", { userId, gameId, score: input.score });
58851
59152
  throw new InternalError("Failed to insert score");
58852
59153
  }
58853
59154
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -58875,7 +59176,7 @@ class LeaderboardService {
58875
59176
  movedUpWithinTop3
58876
59177
  });
58877
59178
  }
58878
- logger24.info("Score submitted", {
59179
+ logger25.info("Score submitted", {
58879
59180
  gameId,
58880
59181
  userId,
58881
59182
  isAnonymousUser,
@@ -58952,7 +59253,7 @@ class LeaderboardService {
58952
59253
  });
58953
59254
  }
58954
59255
  } catch (error) {
58955
- logger24.warn("Failed to publish notification", { error });
59256
+ logger25.warn("Failed to publish notification", { error });
58956
59257
  }
58957
59258
  }
58958
59259
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -59071,7 +59372,7 @@ class LeaderboardService {
59071
59372
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
59072
59373
  }
59073
59374
  }
59074
- var logger24;
59375
+ var logger25;
59075
59376
  var init_leaderboard_service = __esm(() => {
59076
59377
  init_drizzle_orm();
59077
59378
  init_src();
@@ -59081,7 +59382,7 @@ var init_leaderboard_service = __esm(() => {
59081
59382
  init_notification();
59082
59383
  init_errors();
59083
59384
  init_leaderboard_util();
59084
- logger24 = log.scope("LeaderboardService");
59385
+ logger25 = log.scope("LeaderboardService");
59085
59386
  });
59086
59387
 
59087
59388
  class LevelService {
@@ -59096,9 +59397,9 @@ class LevelService {
59096
59397
  for (const config2 of configs) {
59097
59398
  levelConfigCache.set(config2.level, config2);
59098
59399
  }
59099
- logger25.info("Cache pre-warmed", { count: configs.length });
59400
+ logger26.info("Cache pre-warmed", { count: configs.length });
59100
59401
  } catch (error) {
59101
- logger25.error("Cache pre-warm failed", { error });
59402
+ logger26.error("Cache pre-warm failed", { error });
59102
59403
  }
59103
59404
  }
59104
59405
  async getConfig(level) {
@@ -59132,7 +59433,7 @@ class LevelService {
59132
59433
  totalXP
59133
59434
  }).returning();
59134
59435
  if (!newUserLevel) {
59135
- logger25.error("User level insert returned no rows", { userId: user.id });
59436
+ logger26.error("User level insert returned no rows", { userId: user.id });
59136
59437
  throw new InternalError("Failed to create user level cache record");
59137
59438
  }
59138
59439
  userLevel = newUserLevel;
@@ -59147,7 +59448,7 @@ class LevelService {
59147
59448
  userLevel = updatedUserLevel;
59148
59449
  }
59149
59450
  }
59150
- logger25.debug("Retrieved user level", {
59451
+ logger26.debug("Retrieved user level", {
59151
59452
  userId: user.id,
59152
59453
  totalXP,
59153
59454
  currentLevel,
@@ -59158,7 +59459,7 @@ class LevelService {
59158
59459
  async getProgress(user) {
59159
59460
  const userLevel = await this.getByUser(user);
59160
59461
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
59161
- logger25.debug("Retrieved progress", { userId: user.id });
59462
+ logger26.debug("Retrieved progress", { userId: user.id });
59162
59463
  return {
59163
59464
  level: userLevel.currentLevel,
59164
59465
  currentXp: userLevel.currentXp,
@@ -59192,7 +59493,7 @@ class LevelService {
59192
59493
  if (leveledUp && previousUserLevel) {
59193
59494
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
59194
59495
  }
59195
- logger25.info("Synced from Timeback", {
59496
+ logger26.info("Synced from Timeback", {
59196
59497
  userId,
59197
59498
  totalXP,
59198
59499
  currentLevel,
@@ -59233,7 +59534,7 @@ class LevelService {
59233
59534
  }
59234
59535
  if (totalCredits > 0) {
59235
59536
  await this.deps.addCredits(userId, totalCredits);
59236
- logger25.info("Awarded level-up credits", {
59537
+ logger26.info("Awarded level-up credits", {
59237
59538
  userId,
59238
59539
  fromLevel,
59239
59540
  toLevel,
@@ -59271,7 +59572,7 @@ class LevelService {
59271
59572
  };
59272
59573
  }
59273
59574
  }
59274
- var logger25;
59575
+ var logger26;
59275
59576
  var levelConfigCache = null;
59276
59577
  var init_level_service = __esm(() => {
59277
59578
  init_drizzle_orm();
@@ -59279,7 +59580,7 @@ var init_level_service = __esm(() => {
59279
59580
  init_tables_index();
59280
59581
  init_src2();
59281
59582
  init_errors();
59282
- logger25 = log.scope("LevelService");
59583
+ logger26 = log.scope("LevelService");
59283
59584
  });
59284
59585
  var init_events = () => {};
59285
59586
  function convertWebSocketUrlToHttp(wsUrl) {
@@ -59296,13 +59597,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
59296
59597
  });
59297
59598
  if (!res.ok) {
59298
59599
  const text3 = await res.text().catch(() => "");
59299
- logger26.warn("Failed to publish to user", {
59600
+ logger27.warn("Failed to publish to user", {
59300
59601
  status: res.status,
59301
59602
  body: text3
59302
59603
  });
59303
59604
  }
59304
59605
  } catch (error) {
59305
- logger26.error("Publish to user error", { error });
59606
+ logger27.error("Publish to user error", { error });
59306
59607
  }
59307
59608
  }
59308
59609
 
@@ -59324,7 +59625,7 @@ class NotificationService {
59324
59625
  conditions2.push(eq(notifications.type, type));
59325
59626
  }
59326
59627
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
59327
- logger26.debug("Listed notifications", { userId: user.id, count: results.length });
59628
+ logger27.debug("Listed notifications", { userId: user.id, count: results.length });
59328
59629
  return results;
59329
59630
  }
59330
59631
  async updateStatus(notificationId, status, method) {
@@ -59343,7 +59644,7 @@ class NotificationService {
59343
59644
  if (!updated) {
59344
59645
  throw new NotFoundError("Notification", notificationId);
59345
59646
  }
59346
- logger26.debug("Updated status", { notificationId, status });
59647
+ logger27.debug("Updated status", { notificationId, status });
59347
59648
  return updated;
59348
59649
  }
59349
59650
  async getStats(user, options) {
@@ -59369,7 +59670,7 @@ class NotificationService {
59369
59670
  const clicked = statsMap.clicked || 0;
59370
59671
  const dismissed = statsMap.dismissed || 0;
59371
59672
  const expired = statsMap.expired || 0;
59372
- logger26.debug("Retrieved stats", { userId: user.id, total });
59673
+ logger27.debug("Retrieved stats", { userId: user.id, total });
59373
59674
  return {
59374
59675
  total,
59375
59676
  delivered,
@@ -59418,7 +59719,7 @@ class NotificationService {
59418
59719
  options: { data, clickUrl, metadata: metadata2 }
59419
59720
  });
59420
59721
  }
59421
- logger26.debug("Created notification", {
59722
+ logger27.debug("Created notification", {
59422
59723
  userId,
59423
59724
  type,
59424
59725
  id: notificationId,
@@ -59426,7 +59727,7 @@ class NotificationService {
59426
59727
  });
59427
59728
  return notificationId;
59428
59729
  } catch (error) {
59429
- logger26.error("Failed to create notification", { userId, type, error });
59730
+ logger27.error("Failed to create notification", { userId, type, error });
59430
59731
  return null;
59431
59732
  }
59432
59733
  }
@@ -59440,7 +59741,7 @@ class NotificationService {
59440
59741
  }) {
59441
59742
  const realtimeConfig = this.deps.realtime;
59442
59743
  if (!realtimeConfig) {
59443
- logger26.warn("No realtime config for publish");
59744
+ logger27.warn("No realtime config for publish");
59444
59745
  return;
59445
59746
  }
59446
59747
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -59482,13 +59783,13 @@ class NotificationService {
59482
59783
  metadata: data.metadata || {}
59483
59784
  }).returning();
59484
59785
  if (!notification) {
59485
- logger26.error("Notification insert returned no rows", {
59786
+ logger27.error("Notification insert returned no rows", {
59486
59787
  userId: data.userId,
59487
59788
  type: data.type
59488
59789
  });
59489
59790
  throw new InternalError("Failed to create notification");
59490
59791
  }
59491
- logger26.info("Inserted notification", {
59792
+ logger27.info("Inserted notification", {
59492
59793
  notificationId: notification.id,
59493
59794
  userId: notification.userId,
59494
59795
  type: notification.type
@@ -59498,7 +59799,7 @@ class NotificationService {
59498
59799
  async deliverPending(userId) {
59499
59800
  const realtimeConfig = this.deps.realtime;
59500
59801
  if (!realtimeConfig) {
59501
- logger26.warn("No realtime config for delivery");
59802
+ logger27.warn("No realtime config for delivery");
59502
59803
  return;
59503
59804
  }
59504
59805
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -59525,13 +59826,13 @@ class NotificationService {
59525
59826
  metadata: notification.metadata,
59526
59827
  clickUrl: notification.clickUrl
59527
59828
  });
59528
- logger26.info("Delivered notification", {
59829
+ logger27.info("Delivered notification", {
59529
59830
  notificationId: notification.id,
59530
59831
  userId,
59531
59832
  type: notification.type
59532
59833
  });
59533
59834
  } catch (error) {
59534
- logger26.warn("Failed to deliver", {
59835
+ logger27.warn("Failed to deliver", {
59535
59836
  notificationId: notification.id,
59536
59837
  error
59537
59838
  });
@@ -59539,7 +59840,7 @@ class NotificationService {
59539
59840
  }
59540
59841
  }
59541
59842
  }
59542
- var logger26;
59843
+ var logger27;
59543
59844
  var init_notification_service = __esm(() => {
59544
59845
  init_drizzle_orm();
59545
59846
  init_src();
@@ -59548,7 +59849,7 @@ var init_notification_service = __esm(() => {
59548
59849
  init_events();
59549
59850
  init_notification();
59550
59851
  init_errors();
59551
- logger26 = log.scope("NotificationService");
59852
+ logger27 = log.scope("NotificationService");
59552
59853
  });
59553
59854
  function createPlayerServices(deps) {
59554
59855
  const { db: db2, realtime } = deps;
@@ -59667,7 +59968,7 @@ class CharacterService {
59667
59968
  createdAt: characterComponents.createdAt,
59668
59969
  updatedAt: characterComponents.updatedAt
59669
59970
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
59670
- logger27.debug("Listed available components", {
59971
+ logger28.debug("Listed available components", {
59671
59972
  level,
59672
59973
  count: components.length
59673
59974
  });
@@ -59685,7 +59986,7 @@ class CharacterService {
59685
59986
  }
59686
59987
  }
59687
59988
  });
59688
- logger27.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
59989
+ logger28.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
59689
59990
  return pc3 ?? null;
59690
59991
  }
59691
59992
  async getByUserId(userId) {
@@ -59700,7 +60001,7 @@ class CharacterService {
59700
60001
  }
59701
60002
  }
59702
60003
  });
59703
- logger27.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
60004
+ logger28.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
59704
60005
  return pc3 ?? null;
59705
60006
  }
59706
60007
  async create(input, user) {
@@ -59715,13 +60016,13 @@ class CharacterService {
59715
60016
  }
59716
60017
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
59717
60018
  if (!characterRow) {
59718
- logger27.error("Character insert returned no rows", { userId: user.id });
60019
+ logger28.error("Character insert returned no rows", { userId: user.id });
59719
60020
  throw new InternalError("Failed to create character in database");
59720
60021
  }
59721
60022
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
59722
60023
  return characterRow;
59723
60024
  });
59724
- logger27.info("Created character", { userId: user.id, characterId: result.id });
60025
+ logger28.info("Created character", { userId: user.id, characterId: result.id });
59725
60026
  return result;
59726
60027
  }
59727
60028
  async update(input, user) {
@@ -59733,7 +60034,7 @@ class CharacterService {
59733
60034
  if (!row) {
59734
60035
  throw new NotFoundError("Player character");
59735
60036
  }
59736
- logger27.info("Updated character", {
60037
+ logger28.info("Updated character", {
59737
60038
  userId: user.id,
59738
60039
  characterId: row.id,
59739
60040
  updatedFields: Object.keys(input)
@@ -59755,7 +60056,7 @@ class CharacterService {
59755
60056
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
59756
60057
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
59757
60058
  if (!validation.isValid) {
59758
- logger27.warn("Accessory validation failed", {
60059
+ logger28.warn("Accessory validation failed", {
59759
60060
  userId: user.id,
59760
60061
  slot,
59761
60062
  accessoryComponentId,
@@ -59771,14 +60072,14 @@ class CharacterService {
59771
60072
  slot
59772
60073
  }).returning();
59773
60074
  if (!result) {
59774
- logger27.error("Accessory insert returned no rows", {
60075
+ logger28.error("Accessory insert returned no rows", {
59775
60076
  userId: user.id,
59776
60077
  slot,
59777
60078
  accessoryComponentId
59778
60079
  });
59779
60080
  throw new InternalError("Failed to equip accessory");
59780
60081
  }
59781
- logger27.info("Equipped accessory", {
60082
+ logger28.info("Equipped accessory", {
59782
60083
  userId: user.id,
59783
60084
  slot,
59784
60085
  accessoryComponentId
@@ -59799,7 +60100,7 @@ class CharacterService {
59799
60100
  const playerLevel = userLevel?.currentLevel ?? 1;
59800
60101
  const validation = validateAccessoryRemoval(slot, playerLevel);
59801
60102
  if (!validation.isValid) {
59802
- logger27.warn("Accessory removal validation failed", {
60103
+ logger28.warn("Accessory removal validation failed", {
59803
60104
  userId: user.id,
59804
60105
  slot,
59805
60106
  playerLevel,
@@ -59808,17 +60109,17 @@ class CharacterService {
59808
60109
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
59809
60110
  }
59810
60111
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
59811
- logger27.info("Removed accessory", { userId: user.id, slot });
60112
+ logger28.info("Removed accessory", { userId: user.id, slot });
59812
60113
  }
59813
60114
  }
59814
- var logger27;
60115
+ var logger28;
59815
60116
  var init_character_service = __esm(() => {
59816
60117
  init_drizzle_orm();
59817
60118
  init_tables_index();
59818
60119
  init_src2();
59819
60120
  init_errors();
59820
60121
  init_accessory_util();
59821
- logger27 = log.scope("CharacterService");
60122
+ logger28 = log.scope("CharacterService");
59822
60123
  });
59823
60124
 
59824
60125
  class CurrencyService {
@@ -59829,7 +60130,7 @@ class CurrencyService {
59829
60130
  async list() {
59830
60131
  const db2 = this.deps.db;
59831
60132
  const allCurrencies = await db2.query.currencies.findMany();
59832
- logger28.debug("Listed currencies", { count: allCurrencies.length });
60133
+ logger29.debug("Listed currencies", { count: allCurrencies.length });
59833
60134
  return allCurrencies;
59834
60135
  }
59835
60136
  async getById(currencyId) {
@@ -59840,7 +60141,7 @@ class CurrencyService {
59840
60141
  if (!currency) {
59841
60142
  throw new NotFoundError("Currency", currencyId);
59842
60143
  }
59843
- logger28.debug("Retrieved currency", { currencyId });
60144
+ logger29.debug("Retrieved currency", { currencyId });
59844
60145
  return currency;
59845
60146
  }
59846
60147
  async create(data) {
@@ -59848,13 +60149,13 @@ class CurrencyService {
59848
60149
  try {
59849
60150
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
59850
60151
  if (!newCurrency) {
59851
- logger28.error("Currency insert returned no rows", {
60152
+ logger29.error("Currency insert returned no rows", {
59852
60153
  itemId: data.itemId,
59853
60154
  symbol: data.symbol
59854
60155
  });
59855
60156
  throw new InternalError("Failed to create currency");
59856
60157
  }
59857
- logger28.info("Created currency", {
60158
+ logger29.info("Created currency", {
59858
60159
  currencyId: newCurrency.id,
59859
60160
  itemId: newCurrency.itemId,
59860
60161
  symbol: newCurrency.symbol,
@@ -59883,7 +60184,7 @@ class CurrencyService {
59883
60184
  if (!updatedCurrency) {
59884
60185
  throw new NotFoundError("Currency", currencyId);
59885
60186
  }
59886
- logger28.info("Updated currency", {
60187
+ logger29.info("Updated currency", {
59887
60188
  currencyId: updatedCurrency.id,
59888
60189
  updatedFields: Object.keys(data)
59889
60190
  });
@@ -59909,16 +60210,16 @@ class CurrencyService {
59909
60210
  if (result.length === 0) {
59910
60211
  throw new NotFoundError("Currency", currencyId);
59911
60212
  }
59912
- logger28.info("Deleted currency", { currencyId });
60213
+ logger29.info("Deleted currency", { currencyId });
59913
60214
  }
59914
60215
  }
59915
- var logger28;
60216
+ var logger29;
59916
60217
  var init_currency_service = __esm(() => {
59917
60218
  init_drizzle_orm();
59918
60219
  init_tables_index();
59919
60220
  init_src2();
59920
60221
  init_errors();
59921
- logger28 = log.scope("CurrencyService");
60222
+ logger29 = log.scope("CurrencyService");
59922
60223
  });
59923
60224
 
59924
60225
  class LogsService {
@@ -59936,11 +60237,11 @@ class LogsService {
59936
60237
  if (!game) {
59937
60238
  throw new NotFoundError("Game", slug2);
59938
60239
  }
59939
- logger29.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
60240
+ logger30.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
59940
60241
  } else {
59941
60242
  const isApprovedDev = user.developerStatus === "approved";
59942
60243
  if (!isApprovedDev) {
59943
- logger29.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
60244
+ logger30.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
59944
60245
  throw new AccessDeniedError("Must be an approved developer");
59945
60246
  }
59946
60247
  const game = await db2.query.games.findFirst({
@@ -59955,7 +60256,7 @@ class LogsService {
59955
60256
  columns: { id: true }
59956
60257
  });
59957
60258
  if (!membership) {
59958
- logger29.warn("Developer attempted access to unowned game logs", {
60259
+ logger30.warn("Developer attempted access to unowned game logs", {
59959
60260
  userId: user.id,
59960
60261
  slug: slug2
59961
60262
  });
@@ -59964,7 +60265,7 @@ class LogsService {
59964
60265
  }
59965
60266
  const workerId = getDeploymentId(slug2, sstStage);
59966
60267
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
59967
- logger29.debug("Generated log stream token", {
60268
+ logger30.debug("Generated log stream token", {
59968
60269
  userId: user.id,
59969
60270
  slug: slug2,
59970
60271
  workerId
@@ -59972,14 +60273,14 @@ class LogsService {
59972
60273
  return { token, workerId };
59973
60274
  }
59974
60275
  }
59975
- var logger29;
60276
+ var logger30;
59976
60277
  var init_logs_service = __esm(() => {
59977
60278
  init_drizzle_orm();
59978
60279
  init_tables_index();
59979
60280
  init_src2();
59980
60281
  init_errors();
59981
60282
  init_deployment_util();
59982
- logger29 = log.scope("LogsService");
60283
+ logger30 = log.scope("LogsService");
59983
60284
  });
59984
60285
 
59985
60286
  class LtiService {
@@ -60030,7 +60331,7 @@ class MapService {
60030
60331
  if (!mapDetails) {
60031
60332
  throw new NotFoundError("Map", identifier);
60032
60333
  }
60033
- logger30.debug("Retrieved map", { identifier });
60334
+ logger31.debug("Retrieved map", { identifier });
60034
60335
  return mapDetails;
60035
60336
  }
60036
60337
  async getElements(mapId) {
@@ -60046,7 +60347,7 @@ class MapService {
60046
60347
  }
60047
60348
  }
60048
60349
  });
60049
- logger30.debug("Retrieved elements", { mapId, count: elements.length });
60350
+ logger31.debug("Retrieved elements", { mapId, count: elements.length });
60050
60351
  return elements;
60051
60352
  }
60052
60353
  async getObjects(mapId, userId) {
@@ -60067,7 +60368,7 @@ class MapService {
60067
60368
  }
60068
60369
  }
60069
60370
  });
60070
- logger30.debug("Retrieved objects", { mapId, userId, count: objects.length });
60371
+ logger31.debug("Retrieved objects", { mapId, userId, count: objects.length });
60071
60372
  return objects.map((object) => this.formatMapObjectWithItem(object));
60072
60373
  }
60073
60374
  async createObject(mapId, data, user) {
@@ -60094,7 +60395,7 @@ class MapService {
60094
60395
  throw new NotFoundError("Item", data.itemId);
60095
60396
  }
60096
60397
  if (!item.isPlaceable) {
60097
- logger30.warn("Attempted to place non-placeable item", {
60398
+ logger31.warn("Attempted to place non-placeable item", {
60098
60399
  userId: user.id,
60099
60400
  itemId: data.itemId,
60100
60401
  mapId
@@ -60108,7 +60409,7 @@ class MapService {
60108
60409
  };
60109
60410
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
60110
60411
  if (!createdObject) {
60111
- logger30.error("Map object insert returned no rows", {
60412
+ logger31.error("Map object insert returned no rows", {
60112
60413
  userId: user.id,
60113
60414
  mapId,
60114
60415
  itemId: data.itemId
@@ -60132,12 +60433,12 @@ class MapService {
60132
60433
  }
60133
60434
  });
60134
60435
  if (!objectWithItem) {
60135
- logger30.error("Map object query after insert returned no rows", {
60436
+ logger31.error("Map object query after insert returned no rows", {
60136
60437
  objectId: createdObject.id
60137
60438
  });
60138
60439
  throw new InternalError("Failed to retrieve created object");
60139
60440
  }
60140
- logger30.info("Created object", {
60441
+ logger31.info("Created object", {
60141
60442
  userId: user.id,
60142
60443
  mapId,
60143
60444
  objectId: createdObject.id,
@@ -60161,7 +60462,7 @@ class MapService {
60161
60462
  if (result.length === 0) {
60162
60463
  throw new NotFoundError("MapObject", objectId);
60163
60464
  }
60164
- logger30.info("Deleted object", {
60465
+ logger31.info("Deleted object", {
60165
60466
  userId: user.id,
60166
60467
  mapId,
60167
60468
  objectId
@@ -60190,13 +60491,13 @@ class MapService {
60190
60491
  };
60191
60492
  }
60192
60493
  }
60193
- var logger30;
60494
+ var logger31;
60194
60495
  var init_map_service = __esm(() => {
60195
60496
  init_drizzle_orm();
60196
60497
  init_tables_index();
60197
60498
  init_src2();
60198
60499
  init_errors();
60199
- logger30 = log.scope("MapService");
60500
+ logger31 = log.scope("MapService");
60200
60501
  });
60201
60502
 
60202
60503
  class RealtimeService {
@@ -60230,20 +60531,20 @@ class RealtimeService {
60230
60531
  }
60231
60532
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
60232
60533
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
60233
- logger31.info("Generated token", {
60534
+ logger32.info("Generated token", {
60234
60535
  userId: user.id,
60235
60536
  gameId: resolvedGameId || "global"
60236
60537
  });
60237
60538
  return { token };
60238
60539
  }
60239
60540
  }
60240
- var logger31;
60541
+ var logger32;
60241
60542
  var init_realtime_service = __esm(() => {
60242
60543
  init_drizzle_orm();
60243
60544
  init_tables_index();
60244
60545
  init_src2();
60245
60546
  init_errors();
60246
- logger31 = log.scope("RealtimeService");
60547
+ logger32 = log.scope("RealtimeService");
60247
60548
  });
60248
60549
 
60249
60550
  class SessionService {
@@ -60277,10 +60578,10 @@ class SessionService {
60277
60578
  };
60278
60579
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
60279
60580
  if (!newSession?.sessionId) {
60280
- logger32.error("Game session insert returned no rows", { userId, gameId });
60581
+ logger33.error("Game session insert returned no rows", { userId, gameId });
60281
60582
  throw new InternalError("Failed to create game session");
60282
60583
  }
60283
- logger32.info("Started new session", {
60584
+ logger33.info("Started new session", {
60284
60585
  sessionId: newSession.sessionId,
60285
60586
  gameId,
60286
60587
  userId
@@ -60301,23 +60602,23 @@ class SessionService {
60301
60602
  return { success: true, message: "Session already ended" };
60302
60603
  }
60303
60604
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
60304
- logger32.info("Ended session", { sessionId, gameId, userId });
60605
+ logger33.info("Ended session", { sessionId, gameId, userId });
60305
60606
  return { success: true };
60306
60607
  }
60307
60608
  async mintToken(gameIdOrSlug, userId) {
60308
60609
  const gameId = await this.resolveGameId(gameIdOrSlug);
60309
60610
  const result = await this.deps.mintGameToken(gameId, userId);
60310
- logger32.debug("Minted game token", { gameId, userId });
60611
+ logger33.debug("Minted game token", { gameId, userId });
60311
60612
  return result;
60312
60613
  }
60313
60614
  }
60314
- var logger32;
60615
+ var logger33;
60315
60616
  var init_session_service = __esm(() => {
60316
60617
  init_drizzle_orm();
60317
60618
  init_tables_index();
60318
60619
  init_src2();
60319
60620
  init_errors();
60320
- logger32 = log.scope("SessionService");
60621
+ logger33 = log.scope("SessionService");
60321
60622
  });
60322
60623
 
60323
60624
  class ShopService {
@@ -60362,7 +60663,7 @@ class ShopService {
60362
60663
  const shopItems = [];
60363
60664
  for (const listing of listingsWithRelations) {
60364
60665
  if (!listing.item || !listing.currency) {
60365
- logger33.warn("Listing missing item or currency, skipping", {
60666
+ logger34.warn("Listing missing item or currency, skipping", {
60366
60667
  listingId: listing.id
60367
60668
  });
60368
60669
  } else {
@@ -60379,7 +60680,7 @@ class ShopService {
60379
60680
  });
60380
60681
  }
60381
60682
  }
60382
- logger33.debug("Retrieved shop view", {
60683
+ logger34.debug("Retrieved shop view", {
60383
60684
  userId: user.id,
60384
60685
  itemCount: shopItems.length,
60385
60686
  currencyCount: shopCurrencies.length
@@ -60390,12 +60691,12 @@ class ShopService {
60390
60691
  };
60391
60692
  }
60392
60693
  }
60393
- var logger33;
60694
+ var logger34;
60394
60695
  var init_shop_service = __esm(() => {
60395
60696
  init_drizzle_orm();
60396
60697
  init_tables_index();
60397
60698
  init_src2();
60398
- logger33 = log.scope("ShopService");
60699
+ logger34 = log.scope("ShopService");
60399
60700
  });
60400
60701
 
60401
60702
  class SpriteService {
@@ -60411,17 +60712,17 @@ class SpriteService {
60411
60712
  if (!template) {
60412
60713
  throw new NotFoundError("SpriteTemplate", slug2);
60413
60714
  }
60414
- logger34.debug("Retrieved sprite", { slug: slug2 });
60715
+ logger35.debug("Retrieved sprite", { slug: slug2 });
60415
60716
  return template;
60416
60717
  }
60417
60718
  }
60418
- var logger34;
60719
+ var logger35;
60419
60720
  var init_sprite_service = __esm(() => {
60420
60721
  init_drizzle_orm();
60421
60722
  init_tables_index();
60422
60723
  init_src2();
60423
60724
  init_errors();
60424
- logger34 = log.scope("SpriteService");
60725
+ logger35 = log.scope("SpriteService");
60425
60726
  });
60426
60727
 
60427
60728
  class UserService {
@@ -60435,12 +60736,12 @@ class UserService {
60435
60736
  where: eq(users.id, user.id)
60436
60737
  });
60437
60738
  if (!userData) {
60438
- logger35.error("User not found", { userId: user.id });
60739
+ logger36.error("User not found", { userId: user.id });
60439
60740
  throw new NotFoundError("User", user.id);
60440
60741
  }
60441
60742
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
60442
60743
  if (gameId) {
60443
- logger35.debug("Fetched user profile (game context)", { userId: user.id, gameId });
60744
+ logger36.debug("Fetched user profile (game context)", { userId: user.id, gameId });
60444
60745
  return {
60445
60746
  id: userData.id,
60446
60747
  name: userData.name,
@@ -60453,7 +60754,7 @@ class UserService {
60453
60754
  const timebackAccount = await db2.query.accounts.findFirst({
60454
60755
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
60455
60756
  });
60456
- logger35.debug("Fetched user profile (platform context)", { userId: user.id });
60757
+ logger36.debug("Fetched user profile (platform context)", { userId: user.id });
60457
60758
  return {
60458
60759
  id: userData.id,
60459
60760
  name: userData.name,
@@ -60476,7 +60777,7 @@ class UserService {
60476
60777
  columns: { name: true }
60477
60778
  });
60478
60779
  if (!userData) {
60479
- logger35.error("Demo user not found", { userId });
60780
+ logger36.error("Demo user not found", { userId });
60480
60781
  throw new NotFoundError("User", userId);
60481
60782
  }
60482
60783
  return {
@@ -60490,10 +60791,10 @@ class UserService {
60490
60791
  updatedAt: new Date
60491
60792
  }).where(eq(users.id, userId)).returning({ name: users.name });
60492
60793
  if (!updatedUser) {
60493
- logger35.error("Demo user not found for profile update", { userId });
60794
+ logger36.error("Demo user not found for profile update", { userId });
60494
60795
  throw new NotFoundError("User", userId);
60495
60796
  }
60496
- logger35.debug("Updated demo profile", { userId, displayName });
60797
+ logger36.debug("Updated demo profile", { userId, displayName });
60497
60798
  return {
60498
60799
  displayName: updatedUser.name,
60499
60800
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -60506,7 +60807,7 @@ class UserService {
60506
60807
  ]);
60507
60808
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
60508
60809
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
60509
- logger35.debug("Fetched Timeback data", {
60810
+ logger36.debug("Fetched Timeback data", {
60510
60811
  timebackId,
60511
60812
  role,
60512
60813
  enrollmentCount: enrollments.length,
@@ -60515,9 +60816,9 @@ class UserService {
60515
60816
  return { id: timebackId, role, enrollments, organizations };
60516
60817
  }
60517
60818
  async fetchStudentProfile(timebackId) {
60518
- logger35.debug("Fetching student profile", { timebackId });
60819
+ logger36.debug("Fetching student profile", { timebackId });
60519
60820
  if (!this.deps.timeback) {
60520
- logger35.warn("Timeback client not available");
60821
+ logger36.warn("Timeback client not available");
60521
60822
  return { role: "student", organizations: [] };
60522
60823
  }
60523
60824
  try {
@@ -60545,14 +60846,14 @@ class UserService {
60545
60846
  }
60546
60847
  return { role, organizations: [...orgMap.values()] };
60547
60848
  } catch (error) {
60548
- logger35.warn("Failed to fetch student profile", { error, timebackId });
60849
+ logger36.warn("Failed to fetch student profile", { error, timebackId });
60549
60850
  return { role: "student", organizations: [] };
60550
60851
  }
60551
60852
  }
60552
60853
  async fetchEnrollments(timebackId) {
60553
- logger35.debug("Fetching enrollments", { timebackId });
60854
+ logger36.debug("Fetching enrollments", { timebackId });
60554
60855
  if (!this.deps.timeback) {
60555
- logger35.warn("Timeback client not available");
60856
+ logger36.warn("Timeback client not available");
60556
60857
  return [];
60557
60858
  }
60558
60859
  try {
@@ -60573,7 +60874,7 @@ class UserService {
60573
60874
  orgId: courseToSchool.get(i2.courseId)
60574
60875
  }));
60575
60876
  } catch (error) {
60576
- logger35.warn("Failed to fetch enrollments", { error, timebackId });
60877
+ logger36.warn("Failed to fetch enrollments", { error, timebackId });
60577
60878
  return [];
60578
60879
  }
60579
60880
  }
@@ -60588,14 +60889,14 @@ class UserService {
60588
60889
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
60589
60890
  }
60590
60891
  }
60591
- var logger35;
60892
+ var logger36;
60592
60893
  var init_user_service = __esm(() => {
60593
60894
  init_drizzle_orm();
60594
60895
  init_src();
60595
60896
  init_tables_index();
60596
60897
  init_src2();
60597
60898
  init_errors();
60598
- logger35 = log.scope("UserService");
60899
+ logger36 = log.scope("UserService");
60599
60900
  });
60600
60901
 
60601
60902
  class VerifyService {
@@ -60604,16 +60905,16 @@ class VerifyService {
60604
60905
  this.deps = deps;
60605
60906
  }
60606
60907
  async verifyGameToken(token) {
60607
- logger36.debug("Verifying game token");
60908
+ logger37.debug("Verifying game token");
60608
60909
  const payload = await this.deps.validateGameToken(token);
60609
60910
  if (!payload) {
60610
- logger36.warn("Invalid or expired game token presented");
60911
+ logger37.warn("Invalid or expired game token presented");
60611
60912
  throw new ValidationError("Invalid or expired token");
60612
60913
  }
60613
60914
  const gameId = payload.sub;
60614
60915
  const userId = payload.uid;
60615
60916
  if (typeof gameId !== "string" || typeof userId !== "string") {
60616
- logger36.warn("Game token missing required claims", {
60917
+ logger37.warn("Game token missing required claims", {
60617
60918
  hasGameId: typeof gameId === "string",
60618
60919
  hasUserId: typeof userId === "string"
60619
60920
  });
@@ -60624,7 +60925,7 @@ class VerifyService {
60624
60925
  where: eq(users.id, userId)
60625
60926
  });
60626
60927
  if (!userData) {
60627
- logger36.error("User not found for valid token", {
60928
+ logger37.error("User not found for valid token", {
60628
60929
  userId
60629
60930
  });
60630
60931
  throw new NotFoundError("User", userId);
@@ -60638,7 +60939,7 @@ class VerifyService {
60638
60939
  family_name: undefined,
60639
60940
  timeback_id: userData.timebackId || undefined
60640
60941
  };
60641
- logger36.info("Token verified", { gameId, userId });
60942
+ logger37.info("Token verified", { gameId, userId });
60642
60943
  return {
60643
60944
  claims: payload,
60644
60945
  gameId,
@@ -60646,13 +60947,13 @@ class VerifyService {
60646
60947
  };
60647
60948
  }
60648
60949
  }
60649
- var logger36;
60950
+ var logger37;
60650
60951
  var init_verify_service = __esm(() => {
60651
60952
  init_drizzle_orm();
60652
60953
  init_tables_index();
60653
60954
  init_src2();
60654
60955
  init_errors();
60655
- logger36 = log.scope("VerifyService");
60956
+ logger37 = log.scope("VerifyService");
60656
60957
  });
60657
60958
  function createStandaloneServices(deps) {
60658
60959
  const { db: db2, auth: auth2, timeback: timeback2 } = deps;
@@ -65784,7 +66085,7 @@ var colorStatus = async (status) => {
65784
66085
  }
65785
66086
  return `${status}`;
65786
66087
  };
65787
- var logger37 = (fn = console.log) => {
66088
+ var logger38 = (fn = console.log) => {
65788
66089
  return async function logger2(c, next) {
65789
66090
  const { method, url: url2 } = c.req;
65790
66091
  const path2 = url2.slice(url2.indexOf("/", 8));
@@ -65961,7 +66262,7 @@ function createApp(db2, options) {
65961
66262
  const app = new Hono2;
65962
66263
  app.use("*", cors({ origin: "*", credentials: true }));
65963
66264
  if (options.verbose && !options.quiet) {
65964
- app.use("*", logger37());
66265
+ app.use("*", logger38());
65965
66266
  }
65966
66267
  app.use("/api/*", async (c, next) => {
65967
66268
  c.set("db", db2);
@@ -72663,12 +72964,12 @@ var init_session2 = __esm(() => {
72663
72964
  init_utils();
72664
72965
  init_dist5();
72665
72966
  PglitePreparedQuery = class PglitePreparedQuery2 extends PgPreparedQuery {
72666
- constructor(client, queryString, params, logger38, fields, name3, _isResponseInArrayMode, customResultMapper) {
72967
+ constructor(client, queryString, params, logger39, fields, name3, _isResponseInArrayMode, customResultMapper) {
72667
72968
  super({ sql: queryString, params });
72668
72969
  this.client = client;
72669
72970
  this.queryString = queryString;
72670
72971
  this.params = params;
72671
- this.logger = logger38;
72972
+ this.logger = logger39;
72672
72973
  this.fields = fields;
72673
72974
  this._isResponseInArrayMode = _isResponseInArrayMode;
72674
72975
  this.customResultMapper = customResultMapper;
@@ -72770,11 +73071,11 @@ var init_session2 = __esm(() => {
72770
73071
  });
72771
73072
  function construct(client, config2 = {}) {
72772
73073
  const dialect2 = new PgDialect({ casing: config2.casing });
72773
- let logger38;
73074
+ let logger39;
72774
73075
  if (config2.logger === true) {
72775
- logger38 = new DefaultLogger;
73076
+ logger39 = new DefaultLogger;
72776
73077
  } else if (config2.logger !== false) {
72777
- logger38 = config2.logger;
73078
+ logger39 = config2.logger;
72778
73079
  }
72779
73080
  let schema2;
72780
73081
  if (config2.schema) {
@@ -72785,7 +73086,7 @@ function construct(client, config2 = {}) {
72785
73086
  tableNamesMap: tablesConfig.tableNamesMap
72786
73087
  };
72787
73088
  }
72788
- const driver = new PgliteDriver(client, dialect2, { logger: logger38 });
73089
+ const driver = new PgliteDriver(client, dialect2, { logger: logger39 });
72789
73090
  const session2 = driver.createSession(schema2);
72790
73091
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
72791
73092
  db2.$client = client;
@@ -121390,8 +121691,8 @@ var init_currencies = __esm(() => {
121390
121691
  init_tables_index();
121391
121692
  init_constants();
121392
121693
  });
121393
- function setLogger(logger38) {
121394
- customLogger = logger38;
121694
+ function setLogger(logger39) {
121695
+ customLogger = logger39;
121395
121696
  }
121396
121697
  function getLogger() {
121397
121698
  if (customLogger) {
@@ -121404,10 +121705,10 @@ function getLogger() {
121404
121705
  };
121405
121706
  }
121406
121707
  var customLogger;
121407
- var logger38;
121708
+ var logger39;
121408
121709
  var init_adapter = __esm(() => {
121409
121710
  init_config();
121410
- logger38 = {
121711
+ logger39 = {
121411
121712
  info: (msg) => {
121412
121713
  if (customLogger || !config.embedded) {
121413
121714
  getLogger().info(msg);
@@ -121491,7 +121792,7 @@ async function seedCoreGames(db2) {
121491
121792
  role: "owner"
121492
121793
  }).onConflictDoNothing();
121493
121794
  } catch (error2) {
121494
- logger38.error(`Error seeding core game '${gameData.slug}': ${error2}`);
121795
+ logger39.error(`Error seeding core game '${gameData.slug}': ${error2}`);
121495
121796
  }
121496
121797
  }
121497
121798
  }
@@ -121541,7 +121842,7 @@ async function seedCurrentProjectGame(db2, project) {
121541
121842
  }
121542
121843
  return newGame;
121543
121844
  } catch (error2) {
121544
- logger38.error(`❌ Error seeding project game: ${error2}`);
121845
+ logger39.error(`❌ Error seeding project game: ${error2}`);
121545
121846
  throw error2;
121546
121847
  }
121547
121848
  }
@@ -122304,7 +122605,7 @@ async function provisionLtiUser(db2, claims) {
122304
122605
  where: eq(users.id, existingAccount.userId)
122305
122606
  });
122306
122607
  if (user) {
122307
- logger39.info("Found user by LTI account", {
122608
+ logger40.info("Found user by LTI account", {
122308
122609
  userId: user.id,
122309
122610
  ltiTimebackId
122310
122611
  });
@@ -122333,13 +122634,13 @@ async function provisionLtiUser(db2, claims) {
122333
122634
  updatedAt: new Date
122334
122635
  }).returning({ id: accounts.id });
122335
122636
  if (!account) {
122336
- logger39.error("LTI account link insert returned no rows", {
122637
+ logger40.error("LTI account link insert returned no rows", {
122337
122638
  userId: existingUser.id,
122338
122639
  ltiTimebackId
122339
122640
  });
122340
122641
  throw new InternalError("Failed to link LTI account");
122341
122642
  }
122342
- logger39.info("Linked LTI account to existing user", {
122643
+ logger40.info("Linked LTI account to existing user", {
122343
122644
  userId: existingUser.id,
122344
122645
  ltiTimebackId
122345
122646
  });
@@ -122359,7 +122660,7 @@ async function provisionLtiUser(db2, claims) {
122359
122660
  updatedAt: new Date
122360
122661
  }).returning();
122361
122662
  if (!insertedUser) {
122362
- logger39.error("LTI user insert returned no rows", { email, ltiTimebackId });
122663
+ logger40.error("LTI user insert returned no rows", { email, ltiTimebackId });
122363
122664
  throw new InternalError("Failed to create user");
122364
122665
  }
122365
122666
  await tx.insert(accounts).values({
@@ -122374,7 +122675,7 @@ async function provisionLtiUser(db2, claims) {
122374
122675
  createdAt: new Date,
122375
122676
  updatedAt: new Date
122376
122677
  });
122377
- logger39.info("Provisioned new user from LTI", {
122678
+ logger40.info("Provisioned new user from LTI", {
122378
122679
  userId: insertedUser.id,
122379
122680
  ltiTimebackId
122380
122681
  });
@@ -122382,7 +122683,7 @@ async function provisionLtiUser(db2, claims) {
122382
122683
  });
122383
122684
  return createdUser;
122384
122685
  }
122385
- var logger39;
122686
+ var logger40;
122386
122687
  var init_lti_provisioning = __esm(() => {
122387
122688
  init_drizzle_orm();
122388
122689
  init_src();
@@ -122390,7 +122691,7 @@ var init_lti_provisioning = __esm(() => {
122390
122691
  init_src2();
122391
122692
  init_errors();
122392
122693
  init_lti_util();
122393
- logger39 = log.scope("LtiProvisioning");
122694
+ logger40 = log.scope("LtiProvisioning");
122394
122695
  });
122395
122696
  function formatZodError(error2) {
122396
122697
  const flat = error2.flatten();
@@ -122433,7 +122734,7 @@ var init_utils11 = __esm(() => {
122433
122734
  init_timeback_util();
122434
122735
  init_validation_util();
122435
122736
  });
122436
- var logger40;
122737
+ var logger41;
122437
122738
  var listCurrent;
122438
122739
  var listHistory;
122439
122740
  var postProgress;
@@ -122444,14 +122745,14 @@ var init_achievement_controller = __esm(() => {
122444
122745
  init_src2();
122445
122746
  init_errors();
122446
122747
  init_utils11();
122447
- logger40 = log.scope("AchievementController");
122748
+ logger41 = log.scope("AchievementController");
122448
122749
  listCurrent = requireNonAnonymous(async (ctx) => {
122449
- logger40.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
122750
+ logger41.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
122450
122751
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
122451
122752
  });
122452
122753
  listHistory = requireNonAnonymous(async (ctx) => {
122453
122754
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
122454
- logger40.debug("Listing achievement history", { userId: ctx.user.id, limit });
122755
+ logger41.debug("Listing achievement history", { userId: ctx.user.id, limit });
122455
122756
  return ctx.services.achievement.listHistory(ctx.user, limit);
122456
122757
  });
122457
122758
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -122462,12 +122763,12 @@ var init_achievement_controller = __esm(() => {
122462
122763
  } catch (error2) {
122463
122764
  if (error2 instanceof exports_external.ZodError) {
122464
122765
  const details = formatZodError(error2);
122465
- logger40.warn("Submit achievement progress validation failed", { details });
122766
+ logger41.warn("Submit achievement progress validation failed", { details });
122466
122767
  throw ApiError.unprocessableEntity("Invalid request body", details);
122467
122768
  }
122468
122769
  throw ApiError.badRequest("Invalid JSON body");
122469
122770
  }
122470
- logger40.debug("Submitting progress", {
122771
+ logger41.debug("Submitting progress", {
122471
122772
  userId: ctx.user.id,
122472
122773
  achievementId: body2.achievementId
122473
122774
  });
@@ -122479,15 +122780,15 @@ var init_achievement_controller = __esm(() => {
122479
122780
  postProgress
122480
122781
  };
122481
122782
  });
122482
- var logger41;
122783
+ var logger42;
122483
122784
  var getAllowedOrigins;
122484
122785
  var init_admin_controller = __esm(() => {
122485
122786
  init_src2();
122486
122787
  init_utils11();
122487
- logger41 = log.scope("AdminController");
122788
+ logger42 = log.scope("AdminController");
122488
122789
  getAllowedOrigins = requireAdmin(async (ctx) => {
122489
122790
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
122490
- logger41.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
122791
+ logger42.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
122491
122792
  if (shouldRefresh) {
122492
122793
  await ctx.providers.cache.refreshGameOrigins();
122493
122794
  }
@@ -122500,7 +122801,7 @@ var init_admin_controller = __esm(() => {
122500
122801
  };
122501
122802
  });
122502
122803
  });
122503
- var logger42;
122804
+ var logger43;
122504
122805
  var listFiles;
122505
122806
  var getFile;
122506
122807
  var putFile;
@@ -122512,7 +122813,7 @@ var init_bucket_controller = __esm(() => {
122512
122813
  init_src2();
122513
122814
  init_errors();
122514
122815
  init_utils11();
122515
- logger42 = log.scope("BucketController");
122816
+ logger43 = log.scope("BucketController");
122516
122817
  listFiles = requireDeveloper(async (ctx) => {
122517
122818
  const slug2 = ctx.params.slug;
122518
122819
  if (!slug2) {
@@ -122520,7 +122821,7 @@ var init_bucket_controller = __esm(() => {
122520
122821
  }
122521
122822
  const url2 = ctx.url;
122522
122823
  const prefix2 = url2.searchParams.get("prefix") || undefined;
122523
- logger42.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122824
+ logger43.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122524
122825
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
122525
122826
  return { files };
122526
122827
  });
@@ -122530,7 +122831,7 @@ var init_bucket_controller = __esm(() => {
122530
122831
  if (!slug2 || !key) {
122531
122832
  throw ApiError.badRequest("Missing game slug or file key");
122532
122833
  }
122533
- logger42.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
122834
+ logger43.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
122534
122835
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
122535
122836
  return new Response(Buffer.from(object.body), {
122536
122837
  status: 200,
@@ -122549,7 +122850,7 @@ var init_bucket_controller = __esm(() => {
122549
122850
  const arrayBuffer = await ctx.request.arrayBuffer();
122550
122851
  const body2 = new Uint8Array(arrayBuffer);
122551
122852
  const contentType = ctx.request.headers.get("content-type") || undefined;
122552
- logger42.debug("Uploading file", {
122853
+ logger43.debug("Uploading file", {
122553
122854
  userId: ctx.user.id,
122554
122855
  slug: slug2,
122555
122856
  key,
@@ -122565,7 +122866,7 @@ var init_bucket_controller = __esm(() => {
122565
122866
  if (!slug2 || !key) {
122566
122867
  throw ApiError.badRequest("Missing game slug or file key");
122567
122868
  }
122568
- logger42.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
122869
+ logger43.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
122569
122870
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
122570
122871
  return { success: true, key };
122571
122872
  });
@@ -122577,12 +122878,12 @@ var init_bucket_controller = __esm(() => {
122577
122878
  } catch (error2) {
122578
122879
  if (error2 instanceof exports_external.ZodError) {
122579
122880
  const details = formatZodError(error2);
122580
- logger42.warn("Initiate upload validation failed", { details });
122881
+ logger43.warn("Initiate upload validation failed", { details });
122581
122882
  throw ApiError.unprocessableEntity("Validation failed", details);
122582
122883
  }
122583
122884
  throw ApiError.badRequest("Invalid JSON body");
122584
122885
  }
122585
- logger42.debug("Initiating multipart upload", {
122886
+ logger43.debug("Initiating multipart upload", {
122586
122887
  userId: ctx.user.id,
122587
122888
  gameId: body2.gameId,
122588
122889
  fileName: body2.fileName
@@ -122597,10 +122898,10 @@ async function listComponents(ctx) {
122597
122898
  if (!isNaN(parsed) && isFinite(parsed)) {
122598
122899
  level = Math.floor(Math.max(0, parsed));
122599
122900
  }
122600
- logger43.debug("Listing components", { level });
122901
+ logger44.debug("Listing components", { level });
122601
122902
  return ctx.services.character.listAvailableComponents(level);
122602
122903
  }
122603
- var logger43;
122904
+ var logger44;
122604
122905
  var get;
122605
122906
  var getByUserId;
122606
122907
  var create;
@@ -122614,9 +122915,9 @@ var init_character_controller = __esm(() => {
122614
122915
  init_src2();
122615
122916
  init_errors();
122616
122917
  init_utils11();
122617
- logger43 = log.scope("CharacterController");
122918
+ logger44 = log.scope("CharacterController");
122618
122919
  get = requireNonAnonymous(async (ctx) => {
122619
- logger43.debug("Getting character", { userId: ctx.user.id });
122920
+ logger44.debug("Getting character", { userId: ctx.user.id });
122620
122921
  return ctx.services.character.getByUser(ctx.user);
122621
122922
  });
122622
122923
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -122624,7 +122925,7 @@ var init_character_controller = __esm(() => {
122624
122925
  if (!userId) {
122625
122926
  throw ApiError.badRequest("User ID is required in the URL path");
122626
122927
  }
122627
- logger43.debug("Getting character by user ID", { requestedUserId: userId });
122928
+ logger44.debug("Getting character by user ID", { requestedUserId: userId });
122628
122929
  return ctx.services.character.getByUserId(userId);
122629
122930
  });
122630
122931
  create = requireNonAnonymous(async (ctx) => {
@@ -122635,12 +122936,12 @@ var init_character_controller = __esm(() => {
122635
122936
  } catch (error2) {
122636
122937
  if (error2 instanceof exports_external.ZodError) {
122637
122938
  const details = formatZodError(error2);
122638
- logger43.warn("Create character validation failed", { details });
122939
+ logger44.warn("Create character validation failed", { details });
122639
122940
  throw ApiError.unprocessableEntity("Invalid request body", details);
122640
122941
  }
122641
122942
  throw ApiError.badRequest("Invalid JSON body");
122642
122943
  }
122643
- logger43.debug("Creating character", {
122944
+ logger44.debug("Creating character", {
122644
122945
  userId: ctx.user.id,
122645
122946
  bodyComponentId: body2.bodyComponentId,
122646
122947
  hairstyleComponentId: body2.hairstyleComponentId
@@ -122655,12 +122956,12 @@ var init_character_controller = __esm(() => {
122655
122956
  } catch (error2) {
122656
122957
  if (error2 instanceof exports_external.ZodError) {
122657
122958
  const details = formatZodError(error2);
122658
- logger43.warn("Update character validation failed", { details });
122959
+ logger44.warn("Update character validation failed", { details });
122659
122960
  throw ApiError.unprocessableEntity("Invalid request body", details);
122660
122961
  }
122661
122962
  throw ApiError.badRequest("Invalid JSON body");
122662
122963
  }
122663
- logger43.debug("Updating character", {
122964
+ logger44.debug("Updating character", {
122664
122965
  userId: ctx.user.id,
122665
122966
  bodyComponentId: body2.bodyComponentId,
122666
122967
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -122676,12 +122977,12 @@ var init_character_controller = __esm(() => {
122676
122977
  } catch (error2) {
122677
122978
  if (error2 instanceof exports_external.ZodError) {
122678
122979
  const details = formatZodError(error2);
122679
- logger43.warn("Equip accessory validation failed", { details });
122980
+ logger44.warn("Equip accessory validation failed", { details });
122680
122981
  throw ApiError.unprocessableEntity("Invalid request body", details);
122681
122982
  }
122682
122983
  throw ApiError.badRequest("Invalid JSON body");
122683
122984
  }
122684
- logger43.debug("Equipping accessory", {
122985
+ logger44.debug("Equipping accessory", {
122685
122986
  userId: ctx.user.id,
122686
122987
  slot: body2.slot,
122687
122988
  accessoryComponentId: body2.accessoryComponentId
@@ -122693,7 +122994,7 @@ var init_character_controller = __esm(() => {
122693
122994
  if (!slot) {
122694
122995
  throw ApiError.badRequest("Slot is required in the URL path");
122695
122996
  }
122696
- logger43.debug("Removing accessory", { userId: ctx.user.id, slot });
122997
+ logger44.debug("Removing accessory", { userId: ctx.user.id, slot });
122697
122998
  await ctx.services.character.removeAccessory(slot, ctx.user);
122698
122999
  return { success: true };
122699
123000
  });
@@ -122707,7 +123008,7 @@ var init_character_controller = __esm(() => {
122707
123008
  removeAccessory
122708
123009
  };
122709
123010
  });
122710
- var logger44;
123011
+ var logger45;
122711
123012
  var list;
122712
123013
  var getById;
122713
123014
  var create2;
@@ -122721,9 +123022,9 @@ var init_currency_controller = __esm(() => {
122721
123022
  init_src4();
122722
123023
  init_errors();
122723
123024
  init_utils11();
122724
- logger44 = log.scope("CurrencyController");
123025
+ logger45 = log.scope("CurrencyController");
122725
123026
  list = requireNonAnonymous(async (ctx) => {
122726
- logger44.debug("Listing currencies", { userId: ctx.user.id });
123027
+ logger45.debug("Listing currencies", { userId: ctx.user.id });
122727
123028
  return ctx.services.currency.list();
122728
123029
  });
122729
123030
  getById = requireNonAnonymous(async (ctx) => {
@@ -122734,7 +123035,7 @@ var init_currency_controller = __esm(() => {
122734
123035
  if (!isValidUUID(currencyId)) {
122735
123036
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
122736
123037
  }
122737
- logger44.debug("Getting currency", { userId: ctx.user.id, currencyId });
123038
+ logger45.debug("Getting currency", { userId: ctx.user.id, currencyId });
122738
123039
  return ctx.services.currency.getById(currencyId);
122739
123040
  });
122740
123041
  create2 = requireAdmin(async (ctx) => {
@@ -122745,12 +123046,12 @@ var init_currency_controller = __esm(() => {
122745
123046
  } catch (error2) {
122746
123047
  if (error2 instanceof exports_external.ZodError) {
122747
123048
  const details = formatZodError(error2);
122748
- logger44.warn("Create currency validation failed", { details });
123049
+ logger45.warn("Create currency validation failed", { details });
122749
123050
  throw ApiError.unprocessableEntity("Validation failed", details);
122750
123051
  }
122751
123052
  throw ApiError.badRequest("Invalid JSON body");
122752
123053
  }
122753
- logger44.debug("Creating currency", {
123054
+ logger45.debug("Creating currency", {
122754
123055
  userId: ctx.user.id,
122755
123056
  symbol: body2.symbol,
122756
123057
  itemId: body2.itemId,
@@ -122773,12 +123074,12 @@ var init_currency_controller = __esm(() => {
122773
123074
  } catch (error2) {
122774
123075
  if (error2 instanceof exports_external.ZodError) {
122775
123076
  const details = formatZodError(error2);
122776
- logger44.warn("Update currency validation failed", { details });
123077
+ logger45.warn("Update currency validation failed", { details });
122777
123078
  throw ApiError.unprocessableEntity("Validation failed", details);
122778
123079
  }
122779
123080
  throw ApiError.badRequest("Invalid JSON body");
122780
123081
  }
122781
- logger44.debug("Updating currency", {
123082
+ logger45.debug("Updating currency", {
122782
123083
  userId: ctx.user.id,
122783
123084
  currencyId,
122784
123085
  symbol: body2.symbol,
@@ -122795,7 +123096,7 @@ var init_currency_controller = __esm(() => {
122795
123096
  if (!isValidUUID(currencyId)) {
122796
123097
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
122797
123098
  }
122798
- logger44.debug("Deleting currency", { userId: ctx.user.id, currencyId });
123099
+ logger45.debug("Deleting currency", { userId: ctx.user.id, currencyId });
122799
123100
  await ctx.services.currency.delete(currencyId);
122800
123101
  });
122801
123102
  currencyController = {
@@ -122806,7 +123107,7 @@ var init_currency_controller = __esm(() => {
122806
123107
  remove
122807
123108
  };
122808
123109
  });
122809
- var logger45;
123110
+ var logger46;
122810
123111
  var reset;
122811
123112
  var init_database_controller = __esm(() => {
122812
123113
  init_esm();
@@ -122814,7 +123115,7 @@ var init_database_controller = __esm(() => {
122814
123115
  init_src2();
122815
123116
  init_errors();
122816
123117
  init_utils11();
122817
- logger45 = log.scope("DatabaseController");
123118
+ logger46 = log.scope("DatabaseController");
122818
123119
  reset = requireDeveloper(async (ctx) => {
122819
123120
  const slug2 = ctx.params.slug;
122820
123121
  if (!slug2) {
@@ -122827,11 +123128,11 @@ var init_database_controller = __esm(() => {
122827
123128
  } catch (error2) {
122828
123129
  if (error2 instanceof exports_external.ZodError) {
122829
123130
  const details = formatZodError(error2);
122830
- logger45.warn("Database reset validation failed", { details });
123131
+ logger46.warn("Database reset validation failed", { details });
122831
123132
  throw ApiError.unprocessableEntity("Validation failed", details);
122832
123133
  }
122833
123134
  }
122834
- logger45.debug("Resetting database", {
123135
+ logger46.debug("Resetting database", {
122835
123136
  userId: ctx.user.id,
122836
123137
  slug: slug2,
122837
123138
  hasSchema: Boolean(body2.schema)
@@ -122847,7 +123148,7 @@ async function createJob(ctx) {
122847
123148
  let body2;
122848
123149
  try {
122849
123150
  const json4 = await ctx.request.json();
122850
- logger46.debug("Deploy request body", {
123151
+ logger47.debug("Deploy request body", {
122851
123152
  keys: Object.keys(json4 || {}),
122852
123153
  hasUploadToken: Boolean(json4?.uploadToken),
122853
123154
  hasCode: Boolean(json4?.code),
@@ -122857,7 +123158,7 @@ async function createJob(ctx) {
122857
123158
  } catch (error2) {
122858
123159
  if (error2 instanceof exports_external.ZodError) {
122859
123160
  const details = formatZodError(error2);
122860
- logger46.warn("Deploy validation failed", { details });
123161
+ logger47.warn("Deploy validation failed", { details });
122861
123162
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
122862
123163
  }
122863
123164
  throw ApiError.badRequest("Invalid JSON body");
@@ -122878,7 +123179,7 @@ async function getJob(ctx) {
122878
123179
  }
122879
123180
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
122880
123181
  }
122881
- var logger46;
123182
+ var logger47;
122882
123183
  var deploy;
122883
123184
  var init_deploy_controller = __esm(() => {
122884
123185
  init_esm();
@@ -122886,26 +123187,26 @@ var init_deploy_controller = __esm(() => {
122886
123187
  init_src2();
122887
123188
  init_errors();
122888
123189
  init_utils11();
122889
- logger46 = log.scope("DeployController");
123190
+ logger47 = log.scope("DeployController");
122890
123191
  deploy = {
122891
123192
  createJob: requireDeveloper(createJob),
122892
123193
  getJob: requireDeveloper(getJob)
122893
123194
  };
122894
123195
  });
122895
- var logger47;
123196
+ var logger48;
122896
123197
  var apply;
122897
123198
  var getStatus;
122898
123199
  var developer;
122899
123200
  var init_developer_controller = __esm(() => {
122900
123201
  init_src2();
122901
123202
  init_utils11();
122902
- logger47 = log.scope("DeveloperController");
123203
+ logger48 = log.scope("DeveloperController");
122903
123204
  apply = requireNonAnonymous(async (ctx) => {
122904
- logger47.debug("Applying for developer status", { userId: ctx.user.id });
123205
+ logger48.debug("Applying for developer status", { userId: ctx.user.id });
122905
123206
  await ctx.services.developer.apply(ctx.user);
122906
123207
  });
122907
123208
  getStatus = requireNonAnonymous(async (ctx) => {
122908
- logger47.debug("Getting developer status", { userId: ctx.user.id });
123209
+ logger48.debug("Getting developer status", { userId: ctx.user.id });
122909
123210
  const status = await ctx.services.developer.getStatus(ctx.user.id);
122910
123211
  return { status };
122911
123212
  });
@@ -122914,7 +123215,7 @@ var init_developer_controller = __esm(() => {
122914
123215
  getStatus
122915
123216
  };
122916
123217
  });
122917
- var logger48;
123218
+ var logger49;
122918
123219
  var add;
122919
123220
  var list2;
122920
123221
  var getStatus2;
@@ -122927,7 +123228,7 @@ var init_domain_controller = __esm(() => {
122927
123228
  init_config2();
122928
123229
  init_errors();
122929
123230
  init_utils11();
122930
- logger48 = log.scope("DomainController");
123231
+ logger49 = log.scope("DomainController");
122931
123232
  add = requireDeveloper(async (ctx) => {
122932
123233
  const slug2 = ctx.params.slug;
122933
123234
  if (!slug2) {
@@ -122940,13 +123241,13 @@ var init_domain_controller = __esm(() => {
122940
123241
  } catch (error2) {
122941
123242
  if (error2 instanceof exports_external.ZodError) {
122942
123243
  const details = formatZodError(error2);
122943
- logger48.warn("Add domain validation failed", { details });
123244
+ logger49.warn("Add domain validation failed", { details });
122944
123245
  throw ApiError.unprocessableEntity("Validation failed", details);
122945
123246
  }
122946
123247
  throw ApiError.badRequest("Invalid JSON body");
122947
123248
  }
122948
123249
  const environment = getPlatformEnvironment(ctx.config);
122949
- logger48.debug("Adding domain", {
123250
+ logger49.debug("Adding domain", {
122950
123251
  userId: ctx.user.id,
122951
123252
  slug: slug2,
122952
123253
  hostname: body2.hostname,
@@ -122960,7 +123261,7 @@ var init_domain_controller = __esm(() => {
122960
123261
  throw ApiError.badRequest("Missing game slug");
122961
123262
  }
122962
123263
  const environment = getPlatformEnvironment(ctx.config);
122963
- logger48.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
123264
+ logger49.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
122964
123265
  const domains22 = await ctx.services.domain.list(slug2, environment, ctx.user);
122965
123266
  return { domains: domains22 };
122966
123267
  });
@@ -122975,7 +123276,7 @@ var init_domain_controller = __esm(() => {
122975
123276
  }
122976
123277
  const refresh = ctx.url.searchParams.get("refresh") === "true";
122977
123278
  const environment = getPlatformEnvironment(ctx.config);
122978
- logger48.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
123279
+ logger49.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
122979
123280
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
122980
123281
  });
122981
123282
  remove2 = requireDeveloper(async (ctx) => {
@@ -122988,7 +123289,7 @@ var init_domain_controller = __esm(() => {
122988
123289
  throw ApiError.badRequest("Missing hostname");
122989
123290
  }
122990
123291
  const environment = getPlatformEnvironment(ctx.config);
122991
- logger48.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
123292
+ logger49.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
122992
123293
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
122993
123294
  });
122994
123295
  domains2 = {
@@ -122998,7 +123299,7 @@ var init_domain_controller = __esm(() => {
122998
123299
  remove: remove2
122999
123300
  };
123000
123301
  });
123001
- var logger49;
123302
+ var logger50;
123002
123303
  var list3;
123003
123304
  var listAccessible;
123004
123305
  var getSubjects;
@@ -123015,17 +123316,17 @@ var init_game_controller = __esm(() => {
123015
123316
  init_src4();
123016
123317
  init_errors();
123017
123318
  init_utils11();
123018
- logger49 = log.scope("GameController");
123319
+ logger50 = log.scope("GameController");
123019
123320
  list3 = requireNonAnonymous(async (ctx) => {
123020
- logger49.debug("Listing games", { userId: ctx.user.id });
123321
+ logger50.debug("Listing games", { userId: ctx.user.id });
123021
123322
  return ctx.services.game.list(ctx.user);
123022
123323
  });
123023
123324
  listAccessible = requireNonAnonymous(async (ctx) => {
123024
- logger49.debug("Listing accessible games", { userId: ctx.user.id });
123325
+ logger50.debug("Listing accessible games", { userId: ctx.user.id });
123025
123326
  return ctx.services.game.listAccessible(ctx.user);
123026
123327
  });
123027
123328
  getSubjects = requireNonAnonymous(async (ctx) => {
123028
- logger49.debug("Getting game subjects", { userId: ctx.user.id });
123329
+ logger50.debug("Getting game subjects", { userId: ctx.user.id });
123029
123330
  return ctx.services.game.getSubjects();
123030
123331
  });
123031
123332
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -123036,7 +123337,7 @@ var init_game_controller = __esm(() => {
123036
123337
  if (!isValidUUID(gameId)) {
123037
123338
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123038
123339
  }
123039
- logger49.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
123340
+ logger50.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
123040
123341
  return ctx.services.game.getById(gameId, ctx.user);
123041
123342
  });
123042
123343
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -123044,7 +123345,7 @@ var init_game_controller = __esm(() => {
123044
123345
  if (!slug2) {
123045
123346
  throw ApiError.badRequest("Missing game slug");
123046
123347
  }
123047
- logger49.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
123348
+ logger50.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
123048
123349
  return ctx.services.game.getBySlug(slug2, ctx.user);
123049
123350
  });
123050
123351
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -123055,7 +123356,7 @@ var init_game_controller = __esm(() => {
123055
123356
  if (!isValidUUID(gameId)) {
123056
123357
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123057
123358
  }
123058
- logger49.debug("Getting game manifest by ID", {
123359
+ logger50.debug("Getting game manifest by ID", {
123059
123360
  userId: ctx.user.id,
123060
123361
  gameId,
123061
123362
  launchId: ctx.launchId
@@ -123074,12 +123375,12 @@ var init_game_controller = __esm(() => {
123074
123375
  } catch (error2) {
123075
123376
  if (error2 instanceof exports_external.ZodError) {
123076
123377
  const details = formatZodError(error2);
123077
- logger49.warn("Upsert game validation failed", { details });
123378
+ logger50.warn("Upsert game validation failed", { details });
123078
123379
  throw ApiError.unprocessableEntity("Validation failed", details);
123079
123380
  }
123080
123381
  throw ApiError.badRequest("Invalid JSON body");
123081
123382
  }
123082
- logger49.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
123383
+ logger50.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
123083
123384
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
123084
123385
  });
123085
123386
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -123090,7 +123391,7 @@ var init_game_controller = __esm(() => {
123090
123391
  if (!isValidUUID(gameId)) {
123091
123392
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123092
123393
  }
123093
- logger49.debug("Deleting game", { userId: ctx.user.id, gameId });
123394
+ logger50.debug("Deleting game", { userId: ctx.user.id, gameId });
123094
123395
  await ctx.services.game.delete(gameId, ctx.user);
123095
123396
  });
123096
123397
  games2 = {
@@ -123104,7 +123405,7 @@ var init_game_controller = __esm(() => {
123104
123405
  remove: remove3
123105
123406
  };
123106
123407
  });
123107
- var logger50;
123408
+ var logger51;
123108
123409
  var list4;
123109
123410
  var addItem;
123110
123411
  var removeItem;
@@ -123115,9 +123416,9 @@ var init_inventory_controller = __esm(() => {
123115
123416
  init_src2();
123116
123417
  init_errors();
123117
123418
  init_utils11();
123118
- logger50 = log.scope("InventoryController");
123419
+ logger51 = log.scope("InventoryController");
123119
123420
  list4 = requireNonAnonymous(async (ctx) => {
123120
- logger50.debug("Listing inventory", { userId: ctx.user.id });
123421
+ logger51.debug("Listing inventory", { userId: ctx.user.id });
123121
123422
  return ctx.services.inventory.list(ctx.user);
123122
123423
  });
123123
123424
  addItem = requireNonAnonymous(async (ctx) => {
@@ -123128,12 +123429,12 @@ var init_inventory_controller = __esm(() => {
123128
123429
  } catch (error2) {
123129
123430
  if (error2 instanceof exports_external.ZodError) {
123130
123431
  const details = formatZodError(error2);
123131
- logger50.warn("Add inventory item validation failed", { details });
123432
+ logger51.warn("Add inventory item validation failed", { details });
123132
123433
  throw ApiError.unprocessableEntity("Invalid request body", details);
123133
123434
  }
123134
123435
  throw ApiError.badRequest("Invalid JSON body");
123135
123436
  }
123136
- logger50.debug("Adding item", {
123437
+ logger51.debug("Adding item", {
123137
123438
  userId: ctx.user.id,
123138
123439
  itemId: body2.itemId,
123139
123440
  qty: body2.qty
@@ -123148,12 +123449,12 @@ var init_inventory_controller = __esm(() => {
123148
123449
  } catch (error2) {
123149
123450
  if (error2 instanceof exports_external.ZodError) {
123150
123451
  const details = formatZodError(error2);
123151
- logger50.warn("Remove inventory item validation failed", { details });
123452
+ logger51.warn("Remove inventory item validation failed", { details });
123152
123453
  throw ApiError.unprocessableEntity("Invalid request body", details);
123153
123454
  }
123154
123455
  throw ApiError.badRequest("Invalid JSON body");
123155
123456
  }
123156
- logger50.debug("Removing item", {
123457
+ logger51.debug("Removing item", {
123157
123458
  userId: ctx.user.id,
123158
123459
  itemId: body2.itemId,
123159
123460
  qty: body2.qty
@@ -123166,7 +123467,7 @@ var init_inventory_controller = __esm(() => {
123166
123467
  removeItem
123167
123468
  };
123168
123469
  });
123169
- var logger51;
123470
+ var logger52;
123170
123471
  var list5;
123171
123472
  var getById3;
123172
123473
  var resolve2;
@@ -123185,10 +123486,10 @@ var init_item_controller = __esm(() => {
123185
123486
  init_src4();
123186
123487
  init_errors();
123187
123488
  init_utils11();
123188
- logger51 = log.scope("ItemController");
123489
+ logger52 = log.scope("ItemController");
123189
123490
  list5 = requireNonAnonymous(async (ctx) => {
123190
123491
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
123191
- logger51.debug("Listing items", { userId: ctx.user.id, gameId });
123492
+ logger52.debug("Listing items", { userId: ctx.user.id, gameId });
123192
123493
  return ctx.services.item.list(gameId);
123193
123494
  });
123194
123495
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -123199,7 +123500,7 @@ var init_item_controller = __esm(() => {
123199
123500
  if (!isValidUUID(itemId)) {
123200
123501
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123201
123502
  }
123202
- logger51.debug("Getting item", { userId: ctx.user.id, itemId });
123503
+ logger52.debug("Getting item", { userId: ctx.user.id, itemId });
123203
123504
  return ctx.services.item.getById(itemId);
123204
123505
  });
123205
123506
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -123211,7 +123512,7 @@ var init_item_controller = __esm(() => {
123211
123512
  if (gameId && !isValidUUID(gameId)) {
123212
123513
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123213
123514
  }
123214
- logger51.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
123515
+ logger52.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
123215
123516
  return ctx.services.item.resolveBySlug(slug2, gameId);
123216
123517
  });
123217
123518
  create3 = requireRole(["admin"], async (ctx) => {
@@ -123222,12 +123523,12 @@ var init_item_controller = __esm(() => {
123222
123523
  } catch (error2) {
123223
123524
  if (error2 instanceof exports_external.ZodError) {
123224
123525
  const details = formatZodError(error2);
123225
- logger51.warn("Create item validation failed", { details });
123526
+ logger52.warn("Create item validation failed", { details });
123226
123527
  throw ApiError.unprocessableEntity("Validation failed", details);
123227
123528
  }
123228
123529
  throw ApiError.badRequest("Invalid JSON body");
123229
123530
  }
123230
- logger51.debug("Creating item", {
123531
+ logger52.debug("Creating item", {
123231
123532
  userId: ctx.user.id,
123232
123533
  slug: body2.slug,
123233
123534
  displayName: body2.displayName
@@ -123249,7 +123550,7 @@ var init_item_controller = __esm(() => {
123249
123550
  } catch (error2) {
123250
123551
  if (error2 instanceof exports_external.ZodError) {
123251
123552
  const details = formatZodError(error2);
123252
- logger51.warn("Update item validation failed", { details });
123553
+ logger52.warn("Update item validation failed", { details });
123253
123554
  throw ApiError.unprocessableEntity("Validation failed", details);
123254
123555
  }
123255
123556
  throw ApiError.badRequest("Invalid JSON body");
@@ -123257,7 +123558,7 @@ var init_item_controller = __esm(() => {
123257
123558
  if (Object.keys(body2).length === 0) {
123258
123559
  throw ApiError.badRequest("No update data provided");
123259
123560
  }
123260
- logger51.debug("Updating item", {
123561
+ logger52.debug("Updating item", {
123261
123562
  userId: ctx.user.id,
123262
123563
  itemId,
123263
123564
  slug: body2.slug,
@@ -123274,7 +123575,7 @@ var init_item_controller = __esm(() => {
123274
123575
  if (!isValidUUID(itemId)) {
123275
123576
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123276
123577
  }
123277
- logger51.debug("Deleting item", { userId: ctx.user.id, itemId });
123578
+ logger52.debug("Deleting item", { userId: ctx.user.id, itemId });
123278
123579
  await ctx.services.item.delete(itemId);
123279
123580
  });
123280
123581
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -123285,7 +123586,7 @@ var init_item_controller = __esm(() => {
123285
123586
  if (!isValidUUID(gameId)) {
123286
123587
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123287
123588
  }
123288
- logger51.debug("Listing game items", { userId: ctx.user.id, gameId });
123589
+ logger52.debug("Listing game items", { userId: ctx.user.id, gameId });
123289
123590
  return ctx.services.item.listByGame(gameId);
123290
123591
  });
123291
123592
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -123303,12 +123604,12 @@ var init_item_controller = __esm(() => {
123303
123604
  } catch (error2) {
123304
123605
  if (error2 instanceof exports_external.ZodError) {
123305
123606
  const details = formatZodError(error2);
123306
- logger51.warn("Create game item validation failed", { details });
123607
+ logger52.warn("Create game item validation failed", { details });
123307
123608
  throw ApiError.unprocessableEntity("Validation failed", details);
123308
123609
  }
123309
123610
  throw ApiError.badRequest("Invalid JSON body");
123310
123611
  }
123311
- logger51.debug("Creating game item", {
123612
+ logger52.debug("Creating game item", {
123312
123613
  userId: ctx.user.id,
123313
123614
  gameId,
123314
123615
  slug: body2.slug,
@@ -123335,7 +123636,7 @@ var init_item_controller = __esm(() => {
123335
123636
  } catch (error2) {
123336
123637
  if (error2 instanceof exports_external.ZodError) {
123337
123638
  const details = formatZodError(error2);
123338
- logger51.warn("Update game item validation failed", { details });
123639
+ logger52.warn("Update game item validation failed", { details });
123339
123640
  throw ApiError.unprocessableEntity("Validation failed", details);
123340
123641
  }
123341
123642
  throw ApiError.badRequest("Invalid JSON body");
@@ -123343,7 +123644,7 @@ var init_item_controller = __esm(() => {
123343
123644
  if (Object.keys(body2).length === 0) {
123344
123645
  throw ApiError.badRequest("No update data provided");
123345
123646
  }
123346
- logger51.debug("Updating game item", {
123647
+ logger52.debug("Updating game item", {
123347
123648
  userId: ctx.user.id,
123348
123649
  gameId,
123349
123650
  itemId,
@@ -123365,7 +123666,7 @@ var init_item_controller = __esm(() => {
123365
123666
  if (!isValidUUID(itemId)) {
123366
123667
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123367
123668
  }
123368
- logger51.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
123669
+ logger52.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
123369
123670
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
123370
123671
  });
123371
123672
  items2 = {
@@ -123381,7 +123682,7 @@ var init_item_controller = __esm(() => {
123381
123682
  deleteForGame
123382
123683
  };
123383
123684
  });
123384
- var logger52;
123685
+ var logger53;
123385
123686
  var listKeys;
123386
123687
  var getStats;
123387
123688
  var seed;
@@ -123396,7 +123697,7 @@ var init_kv_controller = __esm(() => {
123396
123697
  init_src2();
123397
123698
  init_errors();
123398
123699
  init_utils11();
123399
- logger52 = log.scope("KVController");
123700
+ logger53 = log.scope("KVController");
123400
123701
  listKeys = requireDeveloper(async (ctx) => {
123401
123702
  const slug2 = ctx.params.slug;
123402
123703
  if (!slug2) {
@@ -123404,7 +123705,7 @@ var init_kv_controller = __esm(() => {
123404
123705
  }
123405
123706
  const url2 = ctx.url;
123406
123707
  const prefix2 = url2.searchParams.get("prefix") || undefined;
123407
- logger52.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
123708
+ logger53.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
123408
123709
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
123409
123710
  return { keys };
123410
123711
  });
@@ -123413,7 +123714,7 @@ var init_kv_controller = __esm(() => {
123413
123714
  if (!slug2) {
123414
123715
  throw ApiError.badRequest("Missing game slug");
123415
123716
  }
123416
- logger52.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
123717
+ logger53.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
123417
123718
  return ctx.services.kv.getStats(slug2, ctx.user);
123418
123719
  });
123419
123720
  seed = requireDeveloper(async (ctx) => {
@@ -123428,12 +123729,12 @@ var init_kv_controller = __esm(() => {
123428
123729
  } catch (error2) {
123429
123730
  if (error2 instanceof exports_external.ZodError) {
123430
123731
  const details = formatZodError(error2);
123431
- logger52.warn("Seed validation failed", { details });
123732
+ logger53.warn("Seed validation failed", { details });
123432
123733
  throw ApiError.unprocessableEntity("Validation failed", details);
123433
123734
  }
123434
123735
  throw ApiError.badRequest("Invalid JSON body");
123435
123736
  }
123436
- logger52.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
123737
+ logger53.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
123437
123738
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
123438
123739
  return { success: true, count: body2.entries.length };
123439
123740
  });
@@ -123443,7 +123744,7 @@ var init_kv_controller = __esm(() => {
123443
123744
  if (!slug2 || !key) {
123444
123745
  throw ApiError.badRequest("Missing game slug or key");
123445
123746
  }
123446
- logger52.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
123747
+ logger53.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
123447
123748
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
123448
123749
  return { key, value };
123449
123750
  });
@@ -123457,7 +123758,7 @@ var init_kv_controller = __esm(() => {
123457
123758
  if (!value) {
123458
123759
  throw ApiError.badRequest("Missing value in request body");
123459
123760
  }
123460
- logger52.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
123761
+ logger53.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
123461
123762
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
123462
123763
  return { success: true, key };
123463
123764
  });
@@ -123467,7 +123768,7 @@ var init_kv_controller = __esm(() => {
123467
123768
  if (!slug2 || !key) {
123468
123769
  throw ApiError.badRequest("Missing game slug or key");
123469
123770
  }
123470
- logger52.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
123771
+ logger53.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
123471
123772
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
123472
123773
  return { success: true, key };
123473
123774
  });
@@ -123477,7 +123778,7 @@ var init_kv_controller = __esm(() => {
123477
123778
  if (!slug2 || !key) {
123478
123779
  throw ApiError.badRequest("Missing game slug or key");
123479
123780
  }
123480
- logger52.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
123781
+ logger53.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
123481
123782
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
123482
123783
  return { key, metadata: metadata2 };
123483
123784
  });
@@ -123486,12 +123787,12 @@ var init_kv_controller = __esm(() => {
123486
123787
  if (!slug2) {
123487
123788
  throw ApiError.badRequest("Missing game slug");
123488
123789
  }
123489
- logger52.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
123790
+ logger53.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
123490
123791
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
123491
123792
  return { success: true, deleted };
123492
123793
  });
123493
123794
  });
123494
- var logger53;
123795
+ var logger54;
123495
123796
  var submitScore;
123496
123797
  var getGlobalLeaderboard;
123497
123798
  var getLeaderboard;
@@ -123505,7 +123806,7 @@ var init_leaderboard_controller = __esm(() => {
123505
123806
  init_src2();
123506
123807
  init_errors();
123507
123808
  init_utils11();
123508
- logger53 = log.scope("LeaderboardController");
123809
+ logger54 = log.scope("LeaderboardController");
123509
123810
  submitScore = requireAuth(async (ctx) => {
123510
123811
  const gameId = ctx.params.gameId;
123511
123812
  if (!gameId) {
@@ -123518,12 +123819,12 @@ var init_leaderboard_controller = __esm(() => {
123518
123819
  } catch (error2) {
123519
123820
  if (error2 instanceof exports_external.ZodError) {
123520
123821
  const details = formatZodError(error2);
123521
- logger53.warn("Submit score validation failed", { details });
123822
+ logger54.warn("Submit score validation failed", { details });
123522
123823
  throw ApiError.unprocessableEntity("Validation failed", details);
123523
123824
  }
123524
123825
  throw ApiError.badRequest("Invalid JSON body");
123525
123826
  }
123526
- logger53.debug("Submitting score", {
123827
+ logger54.debug("Submitting score", {
123527
123828
  userId: ctx.user.id,
123528
123829
  gameId,
123529
123830
  score: body2.score
@@ -123546,12 +123847,12 @@ var init_leaderboard_controller = __esm(() => {
123546
123847
  } catch (error2) {
123547
123848
  if (error2 instanceof exports_external.ZodError) {
123548
123849
  const details = formatZodError(error2);
123549
- logger53.warn("Get global leaderboard query validation failed", { details });
123850
+ logger54.warn("Get global leaderboard query validation failed", { details });
123550
123851
  throw ApiError.badRequest("Invalid query parameters", details);
123551
123852
  }
123552
123853
  throw ApiError.badRequest("Invalid query parameters");
123553
123854
  }
123554
- logger53.debug("Getting global leaderboard", {
123855
+ logger54.debug("Getting global leaderboard", {
123555
123856
  userId: ctx.user.id,
123556
123857
  gameId,
123557
123858
  ...query
@@ -123574,12 +123875,12 @@ var init_leaderboard_controller = __esm(() => {
123574
123875
  } catch (error2) {
123575
123876
  if (error2 instanceof exports_external.ZodError) {
123576
123877
  const details = formatZodError(error2);
123577
- logger53.warn("Get leaderboard query validation failed", { details });
123878
+ logger54.warn("Get leaderboard query validation failed", { details });
123578
123879
  throw ApiError.badRequest("Invalid query parameters", details);
123579
123880
  }
123580
123881
  throw ApiError.badRequest("Invalid query parameters");
123581
123882
  }
123582
- logger53.debug("Getting leaderboard", {
123883
+ logger54.debug("Getting leaderboard", {
123583
123884
  userId: ctx.user.id,
123584
123885
  gameId,
123585
123886
  ...query
@@ -123591,7 +123892,7 @@ var init_leaderboard_controller = __esm(() => {
123591
123892
  if (!gameId || !userId) {
123592
123893
  throw ApiError.badRequest("Game ID and User ID are required");
123593
123894
  }
123594
- logger53.debug("Getting user rank", {
123895
+ logger54.debug("Getting user rank", {
123595
123896
  requesterId: ctx.user.id,
123596
123897
  gameId,
123597
123898
  targetUserId: userId
@@ -123606,7 +123907,7 @@ var init_leaderboard_controller = __esm(() => {
123606
123907
  const url2 = ctx.url;
123607
123908
  const limit = Math.min(Number(url2.searchParams.get("limit") || "50"), 100);
123608
123909
  const gameId = url2.searchParams.get("gameId") || undefined;
123609
- logger53.debug("Getting user all scores", {
123910
+ logger54.debug("Getting user all scores", {
123610
123911
  requesterId: ctx.user.id,
123611
123912
  targetUserId: userId,
123612
123913
  gameId,
@@ -123621,7 +123922,7 @@ var init_leaderboard_controller = __esm(() => {
123621
123922
  }
123622
123923
  const url2 = ctx.url;
123623
123924
  const limit = Math.min(Number(url2.searchParams.get("limit") || "10"), 100);
123624
- logger53.debug("Getting user scores", {
123925
+ logger54.debug("Getting user scores", {
123625
123926
  requesterId: ctx.user.id,
123626
123927
  gameId,
123627
123928
  targetUserId: userId,
@@ -123639,7 +123940,7 @@ var init_leaderboard_controller = __esm(() => {
123639
123940
  };
123640
123941
  });
123641
123942
  async function listConfigs(ctx) {
123642
- logger54.debug("Listing level configs");
123943
+ logger55.debug("Listing level configs");
123643
123944
  return ctx.services.level.listConfigs();
123644
123945
  }
123645
123946
  async function getConfig(ctx) {
@@ -123651,10 +123952,10 @@ async function getConfig(ctx) {
123651
123952
  if (isNaN(level) || level < 1) {
123652
123953
  throw ApiError.badRequest("Level must be a positive integer");
123653
123954
  }
123654
- logger54.debug("Getting level config", { level });
123955
+ logger55.debug("Getting level config", { level });
123655
123956
  return ctx.services.level.getConfig(level);
123656
123957
  }
123657
- var logger54;
123958
+ var logger55;
123658
123959
  var getByUser;
123659
123960
  var getProgress;
123660
123961
  var levels;
@@ -123662,13 +123963,13 @@ var init_level_controller = __esm(() => {
123662
123963
  init_src2();
123663
123964
  init_errors();
123664
123965
  init_utils11();
123665
- logger54 = log.scope("LevelController");
123966
+ logger55 = log.scope("LevelController");
123666
123967
  getByUser = requireNonAnonymous(async (ctx) => {
123667
- logger54.debug("Getting user level", { userId: ctx.user.id });
123968
+ logger55.debug("Getting user level", { userId: ctx.user.id });
123668
123969
  return ctx.services.level.getByUser(ctx.user);
123669
123970
  });
123670
123971
  getProgress = requireNonAnonymous(async (ctx) => {
123671
- logger54.debug("Getting level progress", { userId: ctx.user.id });
123972
+ logger55.debug("Getting level progress", { userId: ctx.user.id });
123672
123973
  return ctx.services.level.getProgress(ctx.user);
123673
123974
  });
123674
123975
  levels = {
@@ -123678,14 +123979,14 @@ var init_level_controller = __esm(() => {
123678
123979
  getProgress
123679
123980
  };
123680
123981
  });
123681
- var logger55;
123982
+ var logger56;
123682
123983
  var generateToken;
123683
123984
  var logs;
123684
123985
  var init_logs_controller = __esm(() => {
123685
123986
  init_src2();
123686
123987
  init_errors();
123687
123988
  init_utils11();
123688
- logger55 = log.scope("LogsController");
123989
+ logger56 = log.scope("LogsController");
123689
123990
  generateToken = requireDeveloper(async (ctx) => {
123690
123991
  const slug2 = ctx.params.slug;
123691
123992
  if (!slug2) {
@@ -123704,7 +124005,7 @@ var init_logs_controller = __esm(() => {
123704
124005
  }
123705
124006
  throw ApiError.badRequest("Invalid JSON body");
123706
124007
  }
123707
- logger55.debug("Generating log stream token", {
124008
+ logger56.debug("Generating log stream token", {
123708
124009
  userId: ctx.user.id,
123709
124010
  slug: slug2,
123710
124011
  environment: body2.environment
@@ -123721,22 +124022,22 @@ var init_logs_controller = __esm(() => {
123721
124022
  generateToken
123722
124023
  };
123723
124024
  });
123724
- var logger56;
124025
+ var logger57;
123725
124026
  var getStatus3;
123726
124027
  var lti;
123727
124028
  var init_lti_controller = __esm(() => {
123728
124029
  init_src2();
123729
124030
  init_utils11();
123730
- logger56 = log.scope("LtiController");
124031
+ logger57 = log.scope("LtiController");
123731
124032
  getStatus3 = requireNonAnonymous(async (ctx) => {
123732
- logger56.debug("Getting status", { userId: ctx.user.id });
124033
+ logger57.debug("Getting status", { userId: ctx.user.id });
123733
124034
  return ctx.services.lti.getStatus(ctx.user);
123734
124035
  });
123735
124036
  lti = {
123736
124037
  getStatus: getStatus3
123737
124038
  };
123738
124039
  });
123739
- var logger57;
124040
+ var logger58;
123740
124041
  var getByIdentifier;
123741
124042
  var getElements;
123742
124043
  var getObjects;
@@ -123750,13 +124051,13 @@ var init_map_controller = __esm(() => {
123750
124051
  init_src4();
123751
124052
  init_errors();
123752
124053
  init_utils11();
123753
- logger57 = log.scope("MapController");
124054
+ logger58 = log.scope("MapController");
123754
124055
  getByIdentifier = requireNonAnonymous(async (ctx) => {
123755
124056
  const identifier = ctx.params.identifier;
123756
124057
  if (!identifier) {
123757
124058
  throw ApiError.badRequest("Missing map identifier");
123758
124059
  }
123759
- logger57.debug("Getting map", { userId: ctx.user.id, identifier });
124060
+ logger58.debug("Getting map", { userId: ctx.user.id, identifier });
123760
124061
  return ctx.services.map.getByIdentifier(identifier);
123761
124062
  });
123762
124063
  getElements = requireNonAnonymous(async (ctx) => {
@@ -123767,7 +124068,7 @@ var init_map_controller = __esm(() => {
123767
124068
  if (!isValidUUID(mapId)) {
123768
124069
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
123769
124070
  }
123770
- logger57.debug("Getting map elements", { userId: ctx.user.id, mapId });
124071
+ logger58.debug("Getting map elements", { userId: ctx.user.id, mapId });
123771
124072
  return ctx.services.map.getElements(mapId);
123772
124073
  });
123773
124074
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -123778,7 +124079,7 @@ var init_map_controller = __esm(() => {
123778
124079
  if (!isValidUUID(mapId)) {
123779
124080
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
123780
124081
  }
123781
- logger57.debug("Getting map objects", { userId: ctx.user.id, mapId });
124082
+ logger58.debug("Getting map objects", { userId: ctx.user.id, mapId });
123782
124083
  return ctx.services.map.getObjects(mapId, ctx.user.id);
123783
124084
  });
123784
124085
  createObject = requireNonAnonymous(async (ctx) => {
@@ -123800,12 +124101,12 @@ var init_map_controller = __esm(() => {
123800
124101
  } catch (error2) {
123801
124102
  if (error2 instanceof exports_external.ZodError) {
123802
124103
  const details = formatZodError(error2);
123803
- logger57.warn("Create map object validation failed", { details });
124104
+ logger58.warn("Create map object validation failed", { details });
123804
124105
  throw ApiError.unprocessableEntity("Validation failed", details);
123805
124106
  }
123806
124107
  throw ApiError.badRequest("Invalid JSON body");
123807
124108
  }
123808
- logger57.debug("Creating map object", {
124109
+ logger58.debug("Creating map object", {
123809
124110
  userId: ctx.user.id,
123810
124111
  mapId,
123811
124112
  itemId: body2.itemId,
@@ -123829,7 +124130,7 @@ var init_map_controller = __esm(() => {
123829
124130
  if (!isValidUUID(objectId)) {
123830
124131
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
123831
124132
  }
123832
- logger57.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
124133
+ logger58.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
123833
124134
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
123834
124135
  });
123835
124136
  maps2 = {
@@ -123840,7 +124141,7 @@ var init_map_controller = __esm(() => {
123840
124141
  deleteObject
123841
124142
  };
123842
124143
  });
123843
- var logger58;
124144
+ var logger59;
123844
124145
  var list6;
123845
124146
  var updateStatus;
123846
124147
  var getStats2;
@@ -123853,7 +124154,7 @@ var init_notification_controller = __esm(() => {
123853
124154
  init_src2();
123854
124155
  init_errors();
123855
124156
  init_utils11();
123856
- logger58 = log.scope("NotificationController");
124157
+ logger59 = log.scope("NotificationController");
123857
124158
  list6 = requireNonAnonymous(async (ctx) => {
123858
124159
  const query = {
123859
124160
  status: ctx.url.searchParams.get("status") || undefined,
@@ -123864,10 +124165,10 @@ var init_notification_controller = __esm(() => {
123864
124165
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
123865
124166
  if (!result.success) {
123866
124167
  const details = formatZodError(result.error);
123867
- logger58.warn("List notifications query validation failed", { details });
124168
+ logger59.warn("List notifications query validation failed", { details });
123868
124169
  throw ApiError.badRequest("Invalid query parameters", details);
123869
124170
  }
123870
- logger58.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
124171
+ logger59.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
123871
124172
  return ctx.services.notification.list(ctx.user, result.data);
123872
124173
  });
123873
124174
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -123882,12 +124183,12 @@ var init_notification_controller = __esm(() => {
123882
124183
  } catch (error2) {
123883
124184
  if (error2 instanceof exports_external.ZodError) {
123884
124185
  const details = formatZodError(error2);
123885
- logger58.warn("Update notification status validation failed", { details });
124186
+ logger59.warn("Update notification status validation failed", { details });
123886
124187
  throw ApiError.unprocessableEntity("Invalid request body", details);
123887
124188
  }
123888
124189
  throw ApiError.badRequest("Invalid JSON body");
123889
124190
  }
123890
- logger58.debug("Updating status", {
124191
+ logger59.debug("Updating status", {
123891
124192
  userId: ctx.user.id,
123892
124193
  notificationId,
123893
124194
  status: body2.status
@@ -123897,7 +124198,7 @@ var init_notification_controller = __esm(() => {
123897
124198
  getStats2 = requireNonAnonymous(async (ctx) => {
123898
124199
  const startDate = ctx.url.searchParams.get("startDate");
123899
124200
  const endDate = ctx.url.searchParams.get("endDate");
123900
- logger58.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
124201
+ logger59.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
123901
124202
  return ctx.services.notification.getStats(ctx.user, {
123902
124203
  startDate: startDate ? new Date(startDate) : undefined,
123903
124204
  endDate: endDate ? new Date(endDate) : undefined
@@ -123911,12 +124212,12 @@ var init_notification_controller = __esm(() => {
123911
124212
  } catch (error2) {
123912
124213
  if (error2 instanceof exports_external.ZodError) {
123913
124214
  const details = formatZodError(error2);
123914
- logger58.warn("Create notification validation failed", { details });
124215
+ logger59.warn("Create notification validation failed", { details });
123915
124216
  throw ApiError.unprocessableEntity("Invalid request body", details);
123916
124217
  }
123917
124218
  throw ApiError.badRequest("Invalid JSON body");
123918
124219
  }
123919
- logger58.debug("Creating notification", {
124220
+ logger59.debug("Creating notification", {
123920
124221
  userId: ctx.user.id,
123921
124222
  targetUserId: body2.userId,
123922
124223
  type: body2.type
@@ -123934,12 +124235,12 @@ var init_notification_controller = __esm(() => {
123934
124235
  });
123935
124236
  });
123936
124237
  deliver = requireNonAnonymous(async (ctx) => {
123937
- logger58.debug("Delivering notifications", { userId: ctx.user.id });
124238
+ logger59.debug("Delivering notifications", { userId: ctx.user.id });
123938
124239
  try {
123939
124240
  await ctx.services.notification.deliverPending(ctx.user.id);
123940
124241
  return { success: true };
123941
124242
  } catch (error2) {
123942
- logger58.error("Failed to deliver notifications", { error: error2 });
124243
+ logger59.error("Failed to deliver notifications", { error: error2 });
123943
124244
  throw ApiError.internal("Failed to deliver notifications");
123944
124245
  }
123945
124246
  });
@@ -123951,16 +124252,16 @@ var init_notification_controller = __esm(() => {
123951
124252
  deliver
123952
124253
  };
123953
124254
  });
123954
- var logger59;
124255
+ var logger60;
123955
124256
  var generateToken2;
123956
124257
  var realtime;
123957
124258
  var init_realtime_controller = __esm(() => {
123958
124259
  init_src2();
123959
124260
  init_utils11();
123960
- logger59 = log.scope("RealtimeController");
124261
+ logger60 = log.scope("RealtimeController");
123961
124262
  generateToken2 = requireNonAnonymous(async (ctx) => {
123962
124263
  const gameIdOrSlug = ctx.params.gameId;
123963
- logger59.debug("Generating token", {
124264
+ logger60.debug("Generating token", {
123964
124265
  userId: ctx.user.id,
123965
124266
  gameId: gameIdOrSlug || "global",
123966
124267
  launchId: ctx.launchId
@@ -123971,7 +124272,7 @@ var init_realtime_controller = __esm(() => {
123971
124272
  generateToken: generateToken2
123972
124273
  };
123973
124274
  });
123974
- var logger60;
124275
+ var logger61;
123975
124276
  var listKeys2;
123976
124277
  var setSecrets;
123977
124278
  var deleteSecret;
@@ -123982,13 +124283,13 @@ var init_secrets_controller = __esm(() => {
123982
124283
  init_src2();
123983
124284
  init_errors();
123984
124285
  init_utils11();
123985
- logger60 = log.scope("SecretsController");
124286
+ logger61 = log.scope("SecretsController");
123986
124287
  listKeys2 = requireDeveloper(async (ctx) => {
123987
124288
  const slug2 = ctx.params.slug;
123988
124289
  if (!slug2) {
123989
124290
  throw ApiError.badRequest("Missing game slug");
123990
124291
  }
123991
- logger60.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
124292
+ logger61.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
123992
124293
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
123993
124294
  return { keys };
123994
124295
  });
@@ -124004,12 +124305,12 @@ var init_secrets_controller = __esm(() => {
124004
124305
  } catch (error2) {
124005
124306
  if (error2 instanceof exports_external.ZodError) {
124006
124307
  const details = formatZodError(error2);
124007
- logger60.warn("Set secrets validation failed", { details });
124308
+ logger61.warn("Set secrets validation failed", { details });
124008
124309
  throw ApiError.unprocessableEntity("Validation failed", details);
124009
124310
  }
124010
124311
  throw ApiError.badRequest("Invalid JSON body");
124011
124312
  }
124012
- logger60.debug("Setting secrets", {
124313
+ logger61.debug("Setting secrets", {
124013
124314
  userId: ctx.user.id,
124014
124315
  slug: slug2,
124015
124316
  keyCount: Object.keys(body2).length
@@ -124026,7 +124327,7 @@ var init_secrets_controller = __esm(() => {
124026
124327
  if (!key) {
124027
124328
  throw ApiError.badRequest("Missing secret key");
124028
124329
  }
124029
- logger60.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
124330
+ logger61.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
124030
124331
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
124031
124332
  return { success: true };
124032
124333
  });
@@ -124036,7 +124337,7 @@ var init_secrets_controller = __esm(() => {
124036
124337
  deleteSecret
124037
124338
  };
124038
124339
  });
124039
- var logger61;
124340
+ var logger62;
124040
124341
  var seed2;
124041
124342
  var init_seed_controller = __esm(() => {
124042
124343
  init_esm();
@@ -124044,7 +124345,7 @@ var init_seed_controller = __esm(() => {
124044
124345
  init_src2();
124045
124346
  init_errors();
124046
124347
  init_utils11();
124047
- logger61 = log.scope("SeedController");
124348
+ logger62 = log.scope("SeedController");
124048
124349
  seed2 = requireDeveloper(async (ctx) => {
124049
124350
  const slug2 = ctx.params.slug;
124050
124351
  if (!slug2) {
@@ -124057,12 +124358,12 @@ var init_seed_controller = __esm(() => {
124057
124358
  } catch (error2) {
124058
124359
  if (error2 instanceof exports_external.ZodError) {
124059
124360
  const details = formatZodError(error2);
124060
- logger61.warn("Seed database validation failed", { details });
124361
+ logger62.warn("Seed database validation failed", { details });
124061
124362
  throw ApiError.unprocessableEntity("Validation failed", details);
124062
124363
  }
124063
124364
  throw ApiError.badRequest("Invalid JSON body");
124064
124365
  }
124065
- logger61.debug("Seeding database", {
124366
+ logger62.debug("Seeding database", {
124066
124367
  userId: ctx.user.id,
124067
124368
  slug: slug2,
124068
124369
  codeLength: body2.code.length,
@@ -124071,7 +124372,7 @@ var init_seed_controller = __esm(() => {
124071
124372
  return ctx.services.seed.seed(slug2, body2.code, ctx.user, body2.secrets);
124072
124373
  });
124073
124374
  });
124074
- var logger62;
124375
+ var logger63;
124075
124376
  var start2;
124076
124377
  var end;
124077
124378
  var mintToken;
@@ -124081,13 +124382,13 @@ var init_session_controller = __esm(() => {
124081
124382
  init_tunnel();
124082
124383
  init_errors();
124083
124384
  init_utils11();
124084
- logger62 = log.scope("SessionController");
124385
+ logger63 = log.scope("SessionController");
124085
124386
  start2 = requireAuth(async (ctx) => {
124086
124387
  const gameIdOrSlug = ctx.params.gameId;
124087
124388
  if (!gameIdOrSlug) {
124088
124389
  throw ApiError.badRequest("Missing game ID or slug");
124089
124390
  }
124090
- logger62.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124391
+ logger63.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124091
124392
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
124092
124393
  });
124093
124394
  end = requireAuth(async (ctx) => {
@@ -124099,7 +124400,7 @@ var init_session_controller = __esm(() => {
124099
124400
  if (!sessionId) {
124100
124401
  throw ApiError.badRequest("Missing session ID");
124101
124402
  }
124102
- logger62.debug("Ending session", {
124403
+ logger63.debug("Ending session", {
124103
124404
  userId: ctx.user.id,
124104
124405
  gameIdOrSlug,
124105
124406
  sessionId,
@@ -124112,7 +124413,7 @@ var init_session_controller = __esm(() => {
124112
124413
  if (!gameIdOrSlug) {
124113
124414
  throw ApiError.badRequest("Missing game ID or slug");
124114
124415
  }
124115
- logger62.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124416
+ logger63.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124116
124417
  const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
124117
124418
  let baseUrl;
124118
124419
  if (ctx.config.isLocal) {
@@ -124128,22 +124429,22 @@ var init_session_controller = __esm(() => {
124128
124429
  mintToken
124129
124430
  };
124130
124431
  });
124131
- var logger63;
124432
+ var logger64;
124132
124433
  var getShopView;
124133
124434
  var shop;
124134
124435
  var init_shop_controller = __esm(() => {
124135
124436
  init_src2();
124136
124437
  init_utils11();
124137
- logger63 = log.scope("ShopController");
124438
+ logger64 = log.scope("ShopController");
124138
124439
  getShopView = requireNonAnonymous(async (ctx) => {
124139
- logger63.debug("Getting shop view", { userId: ctx.user.id });
124440
+ logger64.debug("Getting shop view", { userId: ctx.user.id });
124140
124441
  return ctx.services.shop.getShopView(ctx.user);
124141
124442
  });
124142
124443
  shop = {
124143
124444
  getShopView
124144
124445
  };
124145
124446
  });
124146
- var logger64;
124447
+ var logger65;
124147
124448
  var list7;
124148
124449
  var getById4;
124149
124450
  var create5;
@@ -124162,9 +124463,9 @@ var init_shop_listing_controller = __esm(() => {
124162
124463
  init_src4();
124163
124464
  init_errors();
124164
124465
  init_utils11();
124165
- logger64 = log.scope("ShopListingController");
124466
+ logger65 = log.scope("ShopListingController");
124166
124467
  list7 = requireAdmin(async (ctx) => {
124167
- logger64.debug("Listing shop listings", { userId: ctx.user.id });
124468
+ logger65.debug("Listing shop listings", { userId: ctx.user.id });
124168
124469
  return ctx.services.shopListing.list();
124169
124470
  });
124170
124471
  getById4 = requireAdmin(async (ctx) => {
@@ -124175,7 +124476,7 @@ var init_shop_listing_controller = __esm(() => {
124175
124476
  if (!isValidUUID(listingId)) {
124176
124477
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
124177
124478
  }
124178
- logger64.debug("Getting listing", { userId: ctx.user.id, listingId });
124479
+ logger65.debug("Getting listing", { userId: ctx.user.id, listingId });
124179
124480
  return ctx.services.shopListing.getById(listingId);
124180
124481
  });
124181
124482
  create5 = requireAdmin(async (ctx) => {
@@ -124186,12 +124487,12 @@ var init_shop_listing_controller = __esm(() => {
124186
124487
  } catch (error2) {
124187
124488
  if (error2 instanceof exports_external.ZodError) {
124188
124489
  const details = formatZodError(error2);
124189
- logger64.warn("Create shop listing validation failed", { details });
124490
+ logger65.warn("Create shop listing validation failed", { details });
124190
124491
  throw ApiError.unprocessableEntity("Validation failed", details);
124191
124492
  }
124192
124493
  throw ApiError.badRequest("Invalid JSON body");
124193
124494
  }
124194
- logger64.debug("Creating listing", {
124495
+ logger65.debug("Creating listing", {
124195
124496
  userId: ctx.user.id,
124196
124497
  itemId: body2.itemId,
124197
124498
  currencyId: body2.currencyId,
@@ -124214,12 +124515,12 @@ var init_shop_listing_controller = __esm(() => {
124214
124515
  } catch (error2) {
124215
124516
  if (error2 instanceof exports_external.ZodError) {
124216
124517
  const details = formatZodError(error2);
124217
- logger64.warn("Update shop listing validation failed", { details });
124518
+ logger65.warn("Update shop listing validation failed", { details });
124218
124519
  throw ApiError.unprocessableEntity("Validation failed", details);
124219
124520
  }
124220
124521
  throw ApiError.badRequest("Invalid JSON body");
124221
124522
  }
124222
- logger64.debug("Updating listing", {
124523
+ logger65.debug("Updating listing", {
124223
124524
  userId: ctx.user.id,
124224
124525
  listingId,
124225
124526
  price: body2.price,
@@ -124236,7 +124537,7 @@ var init_shop_listing_controller = __esm(() => {
124236
124537
  if (!isValidUUID(listingId)) {
124237
124538
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
124238
124539
  }
124239
- logger64.debug("Deleting listing", { userId: ctx.user.id, listingId });
124540
+ logger65.debug("Deleting listing", { userId: ctx.user.id, listingId });
124240
124541
  await ctx.services.shopListing.delete(listingId);
124241
124542
  });
124242
124543
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -124247,7 +124548,7 @@ var init_shop_listing_controller = __esm(() => {
124247
124548
  if (!isValidUUID(gameId)) {
124248
124549
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
124249
124550
  }
124250
- logger64.debug("Listing game listings", { userId: ctx.user.id, gameId });
124551
+ logger65.debug("Listing game listings", { userId: ctx.user.id, gameId });
124251
124552
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
124252
124553
  });
124253
124554
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -124262,7 +124563,7 @@ var init_shop_listing_controller = __esm(() => {
124262
124563
  if (!isValidUUID(itemId)) {
124263
124564
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
124264
124565
  }
124265
- logger64.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
124566
+ logger65.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
124266
124567
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
124267
124568
  });
124268
124569
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -124284,12 +124585,12 @@ var init_shop_listing_controller = __esm(() => {
124284
124585
  } catch (error2) {
124285
124586
  if (error2 instanceof exports_external.ZodError) {
124286
124587
  const details = formatZodError(error2);
124287
- logger64.warn("Create game item listing validation failed", { details });
124588
+ logger65.warn("Create game item listing validation failed", { details });
124288
124589
  throw ApiError.unprocessableEntity("Validation failed", details);
124289
124590
  }
124290
124591
  throw ApiError.badRequest("Invalid JSON body");
124291
124592
  }
124292
- logger64.debug("Creating game item listing", {
124593
+ logger65.debug("Creating game item listing", {
124293
124594
  userId: ctx.user.id,
124294
124595
  gameId,
124295
124596
  itemId,
@@ -124317,12 +124618,12 @@ var init_shop_listing_controller = __esm(() => {
124317
124618
  } catch (error2) {
124318
124619
  if (error2 instanceof exports_external.ZodError) {
124319
124620
  const details = formatZodError(error2);
124320
- logger64.warn("Update game item listing validation failed", { details });
124621
+ logger65.warn("Update game item listing validation failed", { details });
124321
124622
  throw ApiError.unprocessableEntity("Validation failed", details);
124322
124623
  }
124323
124624
  throw ApiError.badRequest("Invalid JSON body");
124324
124625
  }
124325
- logger64.debug("Updating game item listing", {
124626
+ logger65.debug("Updating game item listing", {
124326
124627
  userId: ctx.user.id,
124327
124628
  gameId,
124328
124629
  itemId,
@@ -124344,7 +124645,7 @@ var init_shop_listing_controller = __esm(() => {
124344
124645
  if (!isValidUUID(itemId)) {
124345
124646
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
124346
124647
  }
124347
- logger64.debug("Deleting game item listing", {
124648
+ logger65.debug("Deleting game item listing", {
124348
124649
  userId: ctx.user.id,
124349
124650
  gameId,
124350
124651
  itemId
@@ -124369,20 +124670,20 @@ async function getBySlug2(ctx) {
124369
124670
  if (!slug2) {
124370
124671
  throw ApiError.badRequest("Template slug is required");
124371
124672
  }
124372
- logger65.debug("Getting sprite by slug", { slug: slug2 });
124673
+ logger66.debug("Getting sprite by slug", { slug: slug2 });
124373
124674
  return ctx.services.sprite.getBySlug(slug2);
124374
124675
  }
124375
- var logger65;
124676
+ var logger66;
124376
124677
  var sprites;
124377
124678
  var init_sprite_controller = __esm(() => {
124378
124679
  init_src2();
124379
124680
  init_errors();
124380
- logger65 = log.scope("SpriteController");
124681
+ logger66 = log.scope("SpriteController");
124381
124682
  sprites = {
124382
124683
  getBySlug: getBySlug2
124383
124684
  };
124384
124685
  });
124385
- var logger66;
124686
+ var logger67;
124386
124687
  var getTodayXp;
124387
124688
  var getTotalXp;
124388
124689
  var updateTodayXp;
@@ -124392,6 +124693,8 @@ var getUser;
124392
124693
  var getUserById;
124393
124694
  var setupIntegration;
124394
124695
  var getIntegrations;
124696
+ var updateIntegration;
124697
+ var getIntegrationConfig;
124395
124698
  var verifyIntegration;
124396
124699
  var getConfig2;
124397
124700
  var deleteIntegrations;
@@ -124407,6 +124710,7 @@ var getActivityDetail;
124407
124710
  var grantXp;
124408
124711
  var adjustTime;
124409
124712
  var adjustMastery;
124713
+ var reconcileMasteryForConfigChange;
124410
124714
  var searchStudents;
124411
124715
  var enrollStudent;
124412
124716
  var unenrollStudent;
@@ -124432,15 +124736,15 @@ var init_timeback_controller = __esm(() => {
124432
124736
  init_src4();
124433
124737
  init_errors();
124434
124738
  init_utils11();
124435
- logger66 = log.scope("TimebackController");
124739
+ logger67 = log.scope("TimebackController");
124436
124740
  getTodayXp = requireNonAnonymous(async (ctx) => {
124437
124741
  const date4 = ctx.url.searchParams.get("date") || undefined;
124438
124742
  const tz = ctx.url.searchParams.get("tz") || undefined;
124439
- logger66.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
124743
+ logger67.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
124440
124744
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
124441
124745
  });
124442
124746
  getTotalXp = requireNonAnonymous(async (ctx) => {
124443
- logger66.debug("Getting total XP", { userId: ctx.user.id });
124747
+ logger67.debug("Getting total XP", { userId: ctx.user.id });
124444
124748
  return ctx.services.timeback.getTotalXp(ctx.user.id);
124445
124749
  });
124446
124750
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -124451,18 +124755,18 @@ var init_timeback_controller = __esm(() => {
124451
124755
  } catch (error2) {
124452
124756
  if (error2 instanceof exports_external.ZodError) {
124453
124757
  const details = formatZodError(error2);
124454
- logger66.warn("Update today XP validation failed", { details });
124758
+ logger67.warn("Update today XP validation failed", { details });
124455
124759
  throw ApiError.unprocessableEntity("Validation failed", details);
124456
124760
  }
124457
124761
  throw ApiError.badRequest("Invalid JSON body");
124458
124762
  }
124459
- logger66.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
124763
+ logger67.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
124460
124764
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
124461
124765
  });
124462
124766
  getXpHistory = requireNonAnonymous(async (ctx) => {
124463
124767
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
124464
124768
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
124465
- logger66.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
124769
+ logger67.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
124466
124770
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
124467
124771
  });
124468
124772
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -124473,18 +124777,18 @@ var init_timeback_controller = __esm(() => {
124473
124777
  } catch (error2) {
124474
124778
  if (error2 instanceof exports_external.ZodError) {
124475
124779
  const details = formatZodError(error2);
124476
- logger66.warn("Populate student validation failed", { details });
124780
+ logger67.warn("Populate student validation failed", { details });
124477
124781
  throw ApiError.unprocessableEntity("Validation failed", details);
124478
124782
  }
124479
124783
  }
124480
- logger66.debug("Populating student", {
124784
+ logger67.debug("Populating student", {
124481
124785
  userId: ctx.user.id,
124482
124786
  hasProvidedNames: Boolean(providedNames)
124483
124787
  });
124484
124788
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
124485
124789
  });
124486
124790
  getUser = requireNonAnonymous(async (ctx) => {
124487
- logger66.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
124791
+ logger67.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
124488
124792
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
124489
124793
  });
124490
124794
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -124492,7 +124796,7 @@ var init_timeback_controller = __esm(() => {
124492
124796
  if (!timebackId) {
124493
124797
  throw ApiError.badRequest("Missing timebackId parameter");
124494
124798
  }
124495
- logger66.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
124799
+ logger67.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
124496
124800
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
124497
124801
  });
124498
124802
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -124503,12 +124807,12 @@ var init_timeback_controller = __esm(() => {
124503
124807
  } catch (error2) {
124504
124808
  if (error2 instanceof exports_external.ZodError) {
124505
124809
  const details = formatZodError(error2);
124506
- logger66.warn("Setup integration validation failed", { details });
124810
+ logger67.warn("Setup integration validation failed", { details });
124507
124811
  throw ApiError.unprocessableEntity("Validation failed", details);
124508
124812
  }
124509
124813
  throw ApiError.badRequest("Invalid JSON body");
124510
124814
  }
124511
- logger66.debug("Setting up integration", {
124815
+ logger67.debug("Setting up integration", {
124512
124816
  userId: ctx.user.id,
124513
124817
  gameId: body2.gameId
124514
124818
  });
@@ -124522,9 +124826,37 @@ var init_timeback_controller = __esm(() => {
124522
124826
  if (!isValidUUID(gameId)) {
124523
124827
  throw ApiError.unprocessableEntity("Invalid gameId format");
124524
124828
  }
124525
- logger66.debug("Getting integrations", { userId: ctx.user.id, gameId });
124829
+ logger67.debug("Getting integrations", { userId: ctx.user.id, gameId });
124526
124830
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
124527
124831
  });
124832
+ updateIntegration = requireDeveloper(async (ctx) => {
124833
+ const { gameId, courseId } = ctx.params;
124834
+ if (!gameId || !courseId) {
124835
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
124836
+ }
124837
+ if (!isValidUUID(gameId)) {
124838
+ throw ApiError.unprocessableEntity("Invalid gameId format");
124839
+ }
124840
+ const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
124841
+ logger67.debug("Updating integration", {
124842
+ userId: ctx.user.id,
124843
+ gameId,
124844
+ courseId,
124845
+ fields: Object.keys(body2)
124846
+ });
124847
+ return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
124848
+ });
124849
+ getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
124850
+ const { gameId, courseId } = ctx.params;
124851
+ if (!gameId || !courseId) {
124852
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
124853
+ }
124854
+ if (!isValidUUID(gameId)) {
124855
+ throw ApiError.unprocessableEntity("Invalid gameId format");
124856
+ }
124857
+ logger67.debug("Getting integration config", { userId: ctx.user.id, gameId, courseId });
124858
+ return ctx.services.timeback.getIntegrationConfig(gameId, courseId, ctx.user);
124859
+ });
124528
124860
  verifyIntegration = requireDeveloper(async (ctx) => {
124529
124861
  const gameId = ctx.params.gameId;
124530
124862
  if (!gameId) {
@@ -124533,7 +124865,7 @@ var init_timeback_controller = __esm(() => {
124533
124865
  if (!isValidUUID(gameId)) {
124534
124866
  throw ApiError.unprocessableEntity("Invalid gameId format");
124535
124867
  }
124536
- logger66.debug("Verifying integration", { userId: ctx.user.id, gameId });
124868
+ logger67.debug("Verifying integration", { userId: ctx.user.id, gameId });
124537
124869
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
124538
124870
  });
124539
124871
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -124544,7 +124876,7 @@ var init_timeback_controller = __esm(() => {
124544
124876
  if (!isValidUUID(gameId)) {
124545
124877
  throw ApiError.unprocessableEntity("Invalid gameId format");
124546
124878
  }
124547
- logger66.debug("Getting config", { userId: ctx.user.id, gameId });
124879
+ logger67.debug("Getting config", { userId: ctx.user.id, gameId });
124548
124880
  return ctx.services.timeback.getConfig(gameId, ctx.user);
124549
124881
  });
124550
124882
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -124555,7 +124887,7 @@ var init_timeback_controller = __esm(() => {
124555
124887
  if (!isValidUUID(gameId)) {
124556
124888
  throw ApiError.unprocessableEntity("Invalid gameId format");
124557
124889
  }
124558
- logger66.debug("Deleting integrations", { userId: ctx.user.id, gameId });
124890
+ logger67.debug("Deleting integrations", { userId: ctx.user.id, gameId });
124559
124891
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
124560
124892
  });
124561
124893
  endActivity = requireDeveloper(async (ctx) => {
@@ -124566,7 +124898,7 @@ var init_timeback_controller = __esm(() => {
124566
124898
  } catch (error2) {
124567
124899
  if (error2 instanceof exports_external.ZodError) {
124568
124900
  const details = formatZodError(error2);
124569
- logger66.warn("End activity validation failed", { details });
124901
+ logger67.warn("End activity validation failed", { details });
124570
124902
  throw ApiError.unprocessableEntity("Validation failed", details);
124571
124903
  }
124572
124904
  throw ApiError.badRequest("Invalid JSON body");
@@ -124584,7 +124916,7 @@ var init_timeback_controller = __esm(() => {
124584
124916
  masteredUnits,
124585
124917
  extensions
124586
124918
  } = body2;
124587
- logger66.debug("Ending activity", { userId: ctx.user.id, gameId });
124919
+ logger67.debug("Ending activity", { userId: ctx.user.id, gameId });
124588
124920
  return ctx.services.timeback.endActivity({
124589
124921
  gameId,
124590
124922
  studentId,
@@ -124608,7 +124940,7 @@ var init_timeback_controller = __esm(() => {
124608
124940
  } catch (error2) {
124609
124941
  if (error2 instanceof exports_external.ZodError) {
124610
124942
  const details = formatZodError(error2);
124611
- logger66.warn("Heartbeat validation failed", { details });
124943
+ logger67.warn("Heartbeat validation failed", { details });
124612
124944
  throw ApiError.unprocessableEntity("Validation failed", details);
124613
124945
  }
124614
124946
  throw ApiError.badRequest("Invalid JSON body");
@@ -124624,7 +124956,7 @@ var init_timeback_controller = __esm(() => {
124624
124956
  windowSequence,
124625
124957
  isFinal
124626
124958
  } = body2;
124627
- logger66.debug("Recording heartbeat", {
124959
+ logger67.debug("Recording heartbeat", {
124628
124960
  userId: ctx.user.id,
124629
124961
  gameId,
124630
124962
  runId,
@@ -124649,7 +124981,7 @@ var init_timeback_controller = __esm(() => {
124649
124981
  });
124650
124982
  advanceCourse = requireDeveloper(async (ctx) => {
124651
124983
  const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
124652
- logger66.debug("Advancing student manually", {
124984
+ logger67.debug("Advancing student manually", {
124653
124985
  userId: ctx.user.id,
124654
124986
  gameId: body2.gameId,
124655
124987
  studentId: body2.studentId,
@@ -124690,7 +125022,7 @@ var init_timeback_controller = __esm(() => {
124690
125022
  perCourse: includeOptions.includes("percourse"),
124691
125023
  today: includeOptions.includes("today")
124692
125024
  };
124693
- logger66.debug("Getting student XP", {
125025
+ logger67.debug("Getting student XP", {
124694
125026
  requesterId: ctx.user.id,
124695
125027
  timebackId,
124696
125028
  gameId,
@@ -124712,7 +125044,7 @@ var init_timeback_controller = __esm(() => {
124712
125044
  if (!gameId || !courseId) {
124713
125045
  throw ApiError.badRequest("Missing gameId or courseId parameter");
124714
125046
  }
124715
- logger66.debug("Getting course roster", {
125047
+ logger67.debug("Getting course roster", {
124716
125048
  requesterId: ctx.user.id,
124717
125049
  gameId,
124718
125050
  courseId,
@@ -124729,7 +125061,7 @@ var init_timeback_controller = __esm(() => {
124729
125061
  if (!timebackId || !gameId) {
124730
125062
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
124731
125063
  }
124732
- logger66.debug("Getting student overview", {
125064
+ logger67.debug("Getting student overview", {
124733
125065
  requesterId: ctx.user.id,
124734
125066
  timebackId,
124735
125067
  gameId,
@@ -124743,7 +125075,7 @@ var init_timeback_controller = __esm(() => {
124743
125075
  if (!gameId || !timebackId) {
124744
125076
  throw ApiError.badRequest("Missing gameId or timebackId path parameter");
124745
125077
  }
124746
- logger66.debug("Getting game metrics", {
125078
+ logger67.debug("Getting game metrics", {
124747
125079
  requesterId: ctx.user.id,
124748
125080
  gameId,
124749
125081
  timebackId
@@ -124761,7 +125093,7 @@ var init_timeback_controller = __esm(() => {
124761
125093
  if (!timebackId || !courseId || !gameId) {
124762
125094
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
124763
125095
  }
124764
- logger66.debug("Getting student activity", {
125096
+ logger67.debug("Getting student activity", {
124765
125097
  requesterId: ctx.user.id,
124766
125098
  timebackId,
124767
125099
  courseId,
@@ -124786,7 +125118,7 @@ var init_timeback_controller = __esm(() => {
124786
125118
  if (!timebackId || !courseId || !activityId || !gameId) {
124787
125119
  throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
124788
125120
  }
124789
- logger66.debug("Getting activity detail", {
125121
+ logger67.debug("Getting activity detail", {
124790
125122
  requesterId: ctx.user.id,
124791
125123
  timebackId,
124792
125124
  courseId,
@@ -124804,7 +125136,7 @@ var init_timeback_controller = __esm(() => {
124804
125136
  });
124805
125137
  grantXp = requireDeveloper(async (ctx) => {
124806
125138
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
124807
- logger66.debug("Granting manual XP", {
125139
+ logger67.debug("Granting manual XP", {
124808
125140
  requesterId: ctx.user.id,
124809
125141
  gameId: body2.gameId,
124810
125142
  courseId: body2.courseId,
@@ -124816,7 +125148,7 @@ var init_timeback_controller = __esm(() => {
124816
125148
  });
124817
125149
  adjustTime = requireDeveloper(async (ctx) => {
124818
125150
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
124819
- logger66.debug("Adjusting time spent", {
125151
+ logger67.debug("Adjusting time spent", {
124820
125152
  requesterId: ctx.user.id,
124821
125153
  gameId: body2.gameId,
124822
125154
  courseId: body2.courseId,
@@ -124828,7 +125160,7 @@ var init_timeback_controller = __esm(() => {
124828
125160
  });
124829
125161
  adjustMastery = requireDeveloper(async (ctx) => {
124830
125162
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
124831
- logger66.debug("Adjusting mastered units", {
125163
+ logger67.debug("Adjusting mastered units", {
124832
125164
  requesterId: ctx.user.id,
124833
125165
  gameId: body2.gameId,
124834
125166
  courseId: body2.courseId,
@@ -124838,6 +125170,22 @@ var init_timeback_controller = __esm(() => {
124838
125170
  });
124839
125171
  return ctx.services.timebackAdmin.adjustMasteredUnits(body2, ctx.user);
124840
125172
  });
125173
+ reconcileMasteryForConfigChange = requireDeveloper(async (ctx) => {
125174
+ const body2 = await parseRequestBody(ctx.request, ReconcileMasteryForConfigChangeSchema);
125175
+ logger67.debug("Reconciling mastery completion for config change", {
125176
+ requesterId: ctx.user.id,
125177
+ gameId: body2.gameId,
125178
+ courseId: body2.courseId,
125179
+ oldMasterableUnits: body2.oldMasterableUnits,
125180
+ newMasterableUnits: body2.newMasterableUnits,
125181
+ affectedCount: body2.affectedStudentIds.length
125182
+ });
125183
+ return ctx.services.timebackAdmin.reconcileMasteryForConfigChange(body2.gameId, body2.courseId, ctx.user, {
125184
+ oldMasterableUnits: body2.oldMasterableUnits,
125185
+ newMasterableUnits: body2.newMasterableUnits,
125186
+ affectedStudentIds: body2.affectedStudentIds
125187
+ });
125188
+ });
124841
125189
  searchStudents = requireGameManagementAccess(async (ctx) => {
124842
125190
  const gameId = ctx.params.gameId;
124843
125191
  const courseId = ctx.params.courseId;
@@ -124845,7 +125193,7 @@ var init_timeback_controller = __esm(() => {
124845
125193
  if (!gameId || !courseId) {
124846
125194
  throw ApiError.badRequest("Missing gameId or courseId parameter");
124847
125195
  }
124848
- logger66.debug("Searching students for enrollment", {
125196
+ logger67.debug("Searching students for enrollment", {
124849
125197
  requesterId: ctx.user.id,
124850
125198
  gameId,
124851
125199
  courseId,
@@ -124855,7 +125203,7 @@ var init_timeback_controller = __esm(() => {
124855
125203
  });
124856
125204
  enrollStudent = requireGameManagementAccess(async (ctx) => {
124857
125205
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
124858
- logger66.debug("Enrolling student", {
125206
+ logger67.debug("Enrolling student", {
124859
125207
  requesterId: ctx.user.id,
124860
125208
  gameId: body2.gameId,
124861
125209
  courseId: body2.courseId,
@@ -124865,7 +125213,7 @@ var init_timeback_controller = __esm(() => {
124865
125213
  });
124866
125214
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
124867
125215
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
124868
- logger66.debug("Unenrolling student", {
125216
+ logger67.debug("Unenrolling student", {
124869
125217
  requesterId: ctx.user.id,
124870
125218
  gameId: body2.gameId,
124871
125219
  courseId: body2.courseId,
@@ -124875,7 +125223,7 @@ var init_timeback_controller = __esm(() => {
124875
125223
  });
124876
125224
  reactivateEnrollment = requireGameManagementAccess(async (ctx) => {
124877
125225
  const body2 = await parseRequestBody(ctx.request, ReactivateEnrollmentRequestSchema);
124878
- logger66.debug("Reactivating enrollment", {
125226
+ logger67.debug("Reactivating enrollment", {
124879
125227
  requesterId: ctx.user.id,
124880
125228
  gameId: body2.gameId,
124881
125229
  courseId: body2.courseId,
@@ -125015,6 +125363,8 @@ var init_timeback_controller = __esm(() => {
125015
125363
  getUserById,
125016
125364
  setupIntegration,
125017
125365
  getIntegrations,
125366
+ updateIntegration,
125367
+ getIntegrationConfig,
125018
125368
  verifyIntegration,
125019
125369
  getConfig: getConfig2,
125020
125370
  deleteIntegrations,
@@ -125030,6 +125380,7 @@ var init_timeback_controller = __esm(() => {
125030
125380
  grantXp,
125031
125381
  adjustTime,
125032
125382
  adjustMastery,
125383
+ reconcileMasteryForConfigChange,
125033
125384
  searchStudents,
125034
125385
  enrollStudent,
125035
125386
  unenrollStudent,
@@ -125049,7 +125400,7 @@ var init_timeback_controller = __esm(() => {
125049
125400
  destroyAssessmentBank
125050
125401
  };
125051
125402
  });
125052
- var logger67;
125403
+ var logger68;
125053
125404
  var initiate;
125054
125405
  var init_upload_controller = __esm(() => {
125055
125406
  init_esm();
@@ -125057,7 +125408,7 @@ var init_upload_controller = __esm(() => {
125057
125408
  init_src2();
125058
125409
  init_errors();
125059
125410
  init_utils11();
125060
- logger67 = log.scope("UploadController");
125411
+ logger68 = log.scope("UploadController");
125061
125412
  initiate = requireDeveloper(async (ctx) => {
125062
125413
  let body2;
125063
125414
  try {
@@ -125066,16 +125417,16 @@ var init_upload_controller = __esm(() => {
125066
125417
  } catch (error2) {
125067
125418
  if (error2 instanceof exports_external.ZodError) {
125068
125419
  const details = formatZodError(error2);
125069
- logger67.warn("Initiate upload validation failed", { details });
125420
+ logger68.warn("Initiate upload validation failed", { details });
125070
125421
  throw ApiError.unprocessableEntity("Validation failed", details);
125071
125422
  }
125072
125423
  throw ApiError.badRequest("Invalid JSON body");
125073
125424
  }
125074
- logger67.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
125425
+ logger68.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
125075
125426
  return ctx.services.upload.initiate(body2, ctx.user);
125076
125427
  });
125077
125428
  });
125078
- var logger68;
125429
+ var logger69;
125079
125430
  var getMe;
125080
125431
  var getDemoProfile;
125081
125432
  var updateDemoProfile;
@@ -125084,18 +125435,18 @@ var init_user_controller = __esm(() => {
125084
125435
  init_schemas_index();
125085
125436
  init_src2();
125086
125437
  init_utils11();
125087
- logger68 = log.scope("UserController");
125438
+ logger69 = log.scope("UserController");
125088
125439
  getMe = requireNonAnonymous(async (ctx) => {
125089
- logger68.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
125440
+ logger69.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
125090
125441
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
125091
125442
  });
125092
125443
  getDemoProfile = requireAnonymous(async (ctx) => {
125093
- logger68.debug("Getting demo profile", { userId: ctx.user.id });
125444
+ logger69.debug("Getting demo profile", { userId: ctx.user.id });
125094
125445
  return ctx.services.user.getDemoProfile(ctx.user.id);
125095
125446
  });
125096
125447
  updateDemoProfile = requireAnonymous(async (ctx) => {
125097
125448
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
125098
- logger68.debug("Updating demo profile", {
125449
+ logger69.debug("Updating demo profile", {
125099
125450
  userId: ctx.user.id,
125100
125451
  displayName: body2.displayName
125101
125452
  });
@@ -125107,13 +125458,13 @@ var init_user_controller = __esm(() => {
125107
125458
  updateDemoProfile
125108
125459
  };
125109
125460
  });
125110
- var logger69;
125461
+ var logger70;
125111
125462
  var init_verify_controller = __esm(() => {
125112
125463
  init_schemas_index();
125113
125464
  init_src2();
125114
125465
  init_errors();
125115
125466
  init_utils11();
125116
- logger69 = log.scope("VerifyController");
125467
+ logger70 = log.scope("VerifyController");
125117
125468
  });
125118
125469
  var init_controllers = __esm(() => {
125119
125470
  init_achievement_controller();
@@ -125411,7 +125762,7 @@ var init_uploads = __esm(() => {
125411
125762
  gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
125412
125763
  gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
125413
125764
  });
125414
- var logger70;
125765
+ var logger71;
125415
125766
  var gameDeployRouter;
125416
125767
  var init_deploy = __esm(() => {
125417
125768
  init_drizzle_orm();
@@ -125422,7 +125773,7 @@ var init_deploy = __esm(() => {
125422
125773
  init_src2();
125423
125774
  init_api();
125424
125775
  init_uploads();
125425
- logger70 = log.scope("SandboxDeploy");
125776
+ logger71 = log.scope("SandboxDeploy");
125426
125777
  gameDeployRouter = new Hono2;
125427
125778
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
125428
125779
  const user = c2.get("user");
@@ -125548,7 +125899,7 @@ var init_deploy = __esm(() => {
125548
125899
  completedAt: now2
125549
125900
  };
125550
125901
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
125551
- logger70.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
125902
+ logger71.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
125552
125903
  return c2.json({
125553
125904
  id: insertedJob.id,
125554
125905
  status: "succeeded",
@@ -126119,7 +126470,7 @@ var init_timeback6 = __esm(() => {
126119
126470
  return {
126120
126471
  grade: e.grade,
126121
126472
  subject: e.subject,
126122
- title: `${e.subject} Grade ${e.grade}`,
126473
+ title: `${e.subject} ${formatGradeLabel(e.grade)}`,
126123
126474
  totalXp: totalXp2,
126124
126475
  ...includeToday && { todayXp: todayXp2 }
126125
126476
  };
@@ -126146,7 +126497,7 @@ function verifyMockToken(idToken) {
126146
126497
  throw new Error("Invalid LTI token format");
126147
126498
  }
126148
126499
  }
126149
- var logger71;
126500
+ var logger72;
126150
126501
  var ltiRouter;
126151
126502
  var init_lti = __esm(() => {
126152
126503
  init_drizzle_orm();
@@ -126157,7 +126508,7 @@ var init_lti = __esm(() => {
126157
126508
  init_src2();
126158
126509
  init_constants();
126159
126510
  init_api();
126160
- logger71 = log.scope("SandboxLti");
126511
+ logger72 = log.scope("SandboxLti");
126161
126512
  ltiRouter = new Hono2;
126162
126513
  ltiRouter.post("/launch", async (c2) => {
126163
126514
  const db2 = c2.get("db");
@@ -126175,7 +126526,7 @@ var init_lti = __esm(() => {
126175
126526
  claims = verifyMockToken(idToken);
126176
126527
  } catch (error2) {
126177
126528
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
126178
- logger71.error("LTI token verification failed", { error: errorMessage });
126529
+ logger72.error("LTI token verification failed", { error: errorMessage });
126179
126530
  return c2.json({
126180
126531
  error: "invalid_token",
126181
126532
  message: errorMessage
@@ -126183,7 +126534,7 @@ var init_lti = __esm(() => {
126183
126534
  }
126184
126535
  const validationError = validateLtiClaims(claims);
126185
126536
  if (validationError) {
126186
- logger71.warn("LTI claims validation failed", {
126537
+ logger72.warn("LTI claims validation failed", {
126187
126538
  error: validationError,
126188
126539
  sub: claims.sub
126189
126540
  });
@@ -126203,7 +126554,7 @@ var init_lti = __esm(() => {
126203
126554
  createdAt: new Date,
126204
126555
  updatedAt: new Date
126205
126556
  });
126206
- logger71.info("LTI launch successful", { userId: user.id });
126557
+ logger72.info("LTI launch successful", { userId: user.id });
126207
126558
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
126208
126559
  const currentHost = new URL(c2.req.url).hostname;
126209
126560
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -126211,7 +126562,7 @@ var init_lti = __esm(() => {
126211
126562
  return c2.redirect(redirectPath);
126212
126563
  } catch (error2) {
126213
126564
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
126214
- logger71.error("Unexpected error during LTI launch", { error: errorMessage });
126565
+ logger72.error("Unexpected error during LTI launch", { error: errorMessage });
126215
126566
  return c2.json({
126216
126567
  error: "unexpected_error",
126217
126568
  message: "An unexpected error occurred during LTI launch"
@@ -127885,7 +128236,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
127885
128236
  // package.json
127886
128237
  var package_default2 = {
127887
128238
  name: "@playcademy/vite-plugin",
127888
- version: "0.2.34-beta.2",
128239
+ version: "0.2.35-beta.1",
127889
128240
  type: "module",
127890
128241
  exports: {
127891
128242
  ".": {