@playcademy/vite-plugin 0.2.34 → 0.2.35-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +844 -501
  2. package/package.json +1 -1
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",
25356
+ version: "0.3.19-beta.2",
25357
25357
  description: "Local development server for Playcademy game development",
25358
25358
  type: "module",
25359
25359
  exports: {
@@ -30030,7 +30030,8 @@ var init_schema = __esm(() => {
30030
30030
  ltiTestMode: exports_external.boolean().default(false),
30031
30031
  realtime: realtimeConfigSchema.optional(),
30032
30032
  platformServiceJwt: platformServiceJwtConfigSchema.optional(),
30033
- uploadBucket: exports_external.string().optional()
30033
+ uploadBucket: exports_external.string().optional(),
30034
+ queueIngressSecret: exports_external.string().optional()
30034
30035
  });
30035
30036
  });
30036
30037
  var init_config2 = __esm(() => {
@@ -50287,6 +50288,14 @@ class DeployService {
50287
50288
  await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
50288
50289
  logger3.info("Set API key on worker", { slug, deploymentId });
50289
50290
  }
50291
+ async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
50292
+ const secret = this.deps.config.queueIngressSecret;
50293
+ if (!secret) {
50294
+ return;
50295
+ }
50296
+ const cf = this.getCloudflare();
50297
+ await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
50298
+ }
50290
50299
  async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
50291
50300
  const frontendZip = await this.timeStep("Fetch temporary files", () => uploadDeps.getObjectAsByteArray(uploadToken), { slug });
50292
50301
  if (!frontendZip || frontendZip.length === 0) {
@@ -50369,7 +50378,10 @@ class DeployService {
50369
50378
  const codeHash = hasBackend ? await generateDeploymentHash(request.code) : null;
50370
50379
  await this.saveDeployment(game.id, result.deploymentId, result.url, codeHash, result.resources);
50371
50380
  if (hasBackend) {
50372
- await this.timeStep("Configure worker secrets", () => this.ensureApiKeyOnWorker(user, slug, result.deploymentId), { slug, deploymentId: result.deploymentId });
50381
+ await this.timeStep("Configure worker secrets", async () => {
50382
+ await this.ensureApiKeyOnWorker(user, slug, result.deploymentId);
50383
+ await this.ensureQueueIngressSecretOnWorker(slug, result.deploymentId);
50384
+ }, { slug, deploymentId: result.deploymentId });
50373
50385
  }
50374
50386
  if (hasMetadata || hasFrontend) {
50375
50387
  await this.applyGameMetadata(game.id, request, hasFrontend, hasMetadata, result.url);
@@ -55125,6 +55137,8 @@ var TIMEBACK_SUBJECTS4;
55125
55137
  var TimebackGradeSchema;
55126
55138
  var TimebackSubjectSchema;
55127
55139
  var UpdateTimebackXpRequestSchema;
55140
+ var CourseGoalsSchema;
55141
+ var UpdateGameTimebackIntegrationRequestSchema;
55128
55142
  var TimebackActivityDataSchema;
55129
55143
  var EndActivityRequestSchema;
55130
55144
  var GameActivityMetricsSchema;
@@ -55144,6 +55158,7 @@ var ADMIN_GRANT_XP_AMOUNT_RANGE_MESSAGE;
55144
55158
  var GrantTimebackXpRequestSchema;
55145
55159
  var AdjustTimebackTimeRequestSchema;
55146
55160
  var AdjustTimebackMasteryRequestSchema;
55161
+ var ReconcileMasteryForConfigChangeSchema;
55147
55162
  var EnrollStudentRequestSchema;
55148
55163
  var UnenrollStudentRequestSchema;
55149
55164
  var ReactivateEnrollmentRequestSchema;
@@ -55175,6 +55190,24 @@ var init_schemas11 = __esm(() => {
55175
55190
  xp: exports_external.number().min(0, "XP must be a non-negative number"),
55176
55191
  userTimestamp: exports_external.string().datetime().optional()
55177
55192
  });
55193
+ CourseGoalsSchema = exports_external.object({
55194
+ dailyXp: exports_external.number().int().nonnegative().nullable().optional(),
55195
+ dailyLessons: exports_external.number().int().nonnegative().nullable().optional(),
55196
+ dailyActiveMinutes: exports_external.number().int().nonnegative().nullable().optional(),
55197
+ dailyAccuracy: exports_external.number().int().min(0).max(100).nullable().optional(),
55198
+ dailyMasteredUnits: exports_external.number().int().nonnegative().nullable().optional()
55199
+ });
55200
+ UpdateGameTimebackIntegrationRequestSchema = exports_external.object({
55201
+ title: exports_external.string().trim().min(1).optional(),
55202
+ courseCode: exports_external.string().trim().min(1).optional(),
55203
+ subject: TimebackSubjectSchema.optional(),
55204
+ totalXp: exports_external.number().int().nonnegative().nullable().optional(),
55205
+ masterableUnits: exports_external.number().int().nonnegative().nullable().optional(),
55206
+ goals: CourseGoalsSchema.optional(),
55207
+ publishStatus: exports_external.enum(["draft", "testing", "published", "deactivated"]).nullable().optional(),
55208
+ isSupplemental: exports_external.boolean().optional(),
55209
+ timebackVisible: exports_external.boolean().nullable().optional()
55210
+ });
55178
55211
  TimebackActivityDataSchema = exports_external.object({
55179
55212
  activityId: exports_external.string().min(1),
55180
55213
  activityName: exports_external.string().optional(),
@@ -55344,6 +55377,13 @@ var init_schemas11 = __esm(() => {
55344
55377
  date: AdminAttributionDateSchema.optional(),
55345
55378
  useCurrentTime: exports_external.boolean().optional()
55346
55379
  });
55380
+ ReconcileMasteryForConfigChangeSchema = exports_external.object({
55381
+ gameId: exports_external.string().uuid(),
55382
+ courseId: exports_external.string().min(1),
55383
+ oldMasterableUnits: exports_external.number().int().positive(),
55384
+ newMasterableUnits: exports_external.number().int().positive(),
55385
+ affectedStudentIds: exports_external.array(exports_external.string().min(1)).min(1).max(500)
55386
+ });
55347
55387
  EnrollStudentRequestSchema = exports_external.object({
55348
55388
  gameId: exports_external.string().uuid(),
55349
55389
  courseId: exports_external.string().min(1),
@@ -55492,6 +55532,63 @@ function compareEnrollmentsByRecency(a, b) {
55492
55532
  var init_timeback_admin_util = __esm(() => {
55493
55533
  init_errors();
55494
55534
  });
55535
+ async function upsertMasteryCompletionEntry(params) {
55536
+ const { client, courseId, studentId, appName, action } = params;
55537
+ const ids = deriveSourcedIds(courseId);
55538
+ const lineItemId = `${ids.course}-mastery-completion-assessment`;
55539
+ const resultId = `${lineItemId}:${studentId}:completion`;
55540
+ if (action === "complete") {
55541
+ await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
55542
+ sourcedId: lineItemId,
55543
+ title: "Mastery Completion",
55544
+ status: ONEROSTER_STATUS.active,
55545
+ course: { sourcedId: ids.course },
55546
+ ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
55547
+ });
55548
+ await client.oneroster.assessmentResults.upsert(resultId, {
55549
+ sourcedId: resultId,
55550
+ status: ONEROSTER_STATUS.active,
55551
+ assessmentLineItem: { sourcedId: lineItemId },
55552
+ student: { sourcedId: studentId },
55553
+ score: 100,
55554
+ scoreDate: new Date().toISOString(),
55555
+ scoreStatus: SCORE_STATUS.fullyGraded,
55556
+ inProgress: "false",
55557
+ metadata: {
55558
+ isMasteryCompletion: true,
55559
+ adminAction: true,
55560
+ appName
55561
+ }
55562
+ });
55563
+ } else {
55564
+ try {
55565
+ await client.oneroster.assessmentResults.upsert(resultId, {
55566
+ sourcedId: resultId,
55567
+ status: ONEROSTER_STATUS.active,
55568
+ assessmentLineItem: { sourcedId: lineItemId },
55569
+ student: { sourcedId: studentId },
55570
+ score: 0,
55571
+ scoreDate: new Date().toISOString(),
55572
+ scoreStatus: SCORE_STATUS.notSubmitted,
55573
+ inProgress: "true",
55574
+ metadata: {
55575
+ isMasteryCompletion: true,
55576
+ adminAction: true,
55577
+ appName
55578
+ }
55579
+ });
55580
+ } catch {
55581
+ logger17.debug("No completion entry to revoke", { studentId, courseId });
55582
+ }
55583
+ }
55584
+ }
55585
+ var logger17;
55586
+ var init_timeback_mastery_completion_util = __esm(() => {
55587
+ init_src2();
55588
+ init_constants4();
55589
+ init_utils6();
55590
+ logger17 = log.scope("timeback-mastery-completion");
55591
+ });
55495
55592
  function isRecord2(value) {
55496
55593
  return typeof value === "object" && value !== null;
55497
55594
  }
@@ -55847,7 +55944,7 @@ class TimebackAdminService {
55847
55944
  }
55848
55945
  requireClient() {
55849
55946
  if (!this.deps.timeback) {
55850
- logger17.error("Timeback client not available in context");
55947
+ logger18.error("Timeback client not available in context");
55851
55948
  throw new ValidationError("Timeback integration not available in this environment");
55852
55949
  }
55853
55950
  return this.deps.timeback;
@@ -56067,7 +56164,7 @@ class TimebackAdminService {
56067
56164
  });
56068
56165
  return [enrollmentId, this.summarizeAnalyticsFacts(analytics.facts)];
56069
56166
  } catch (error) {
56070
- logger17.warn("Failed to load enrollment analytics summary", {
56167
+ logger18.warn("Failed to load enrollment analytics summary", {
56071
56168
  enrollmentId,
56072
56169
  error: error instanceof Error ? error.message : String(error)
56073
56170
  });
@@ -56097,7 +56194,7 @@ class TimebackAdminService {
56097
56194
  const events = await this.fetchCaliperEventsForStudent(client, studentId, source, eventLimit);
56098
56195
  return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
56099
56196
  } catch (error) {
56100
- logger17.warn("Failed to load recent Caliper activity", {
56197
+ logger18.warn("Failed to load recent Caliper activity", {
56101
56198
  studentId,
56102
56199
  gameId: source.gameId,
56103
56200
  sourceMode: source.sourceMode,
@@ -56423,60 +56520,44 @@ class TimebackAdminService {
56423
56520
  const wasMastered = currentMastered >= masterableUnits;
56424
56521
  const willBeMastered = currentMastered + data.units >= masterableUnits;
56425
56522
  if (wasMastered !== willBeMastered) {
56426
- const ids = deriveSourcedIds(data.courseId);
56427
- const lineItemId = `${ids.course}-mastery-completion-assessment`;
56428
- const resultId = `${lineItemId}:${data.studentId}:completion`;
56429
- if (willBeMastered) {
56430
- await client.oneroster.assessmentLineItems.findOrCreate(lineItemId, {
56431
- sourcedId: lineItemId,
56432
- title: "Mastery Completion",
56433
- status: ONEROSTER_STATUS.active,
56434
- course: { sourcedId: ids.course },
56435
- ...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
56436
- });
56437
- await client.oneroster.assessmentResults.upsert(resultId, {
56438
- sourcedId: resultId,
56439
- status: ONEROSTER_STATUS.active,
56440
- assessmentLineItem: { sourcedId: lineItemId },
56441
- student: { sourcedId: data.studentId },
56442
- score: 100,
56443
- scoreDate: new Date().toISOString(),
56444
- scoreStatus: SCORE_STATUS.fullyGraded,
56445
- inProgress: "false",
56446
- metadata: {
56447
- isMasteryCompletion: true,
56448
- adminAction: true,
56449
- appName
56450
- }
56451
- });
56452
- } else {
56453
- try {
56454
- await client.oneroster.assessmentResults.upsert(resultId, {
56455
- sourcedId: resultId,
56456
- status: ONEROSTER_STATUS.active,
56457
- assessmentLineItem: { sourcedId: lineItemId },
56458
- student: { sourcedId: data.studentId },
56459
- score: 0,
56460
- scoreDate: new Date().toISOString(),
56461
- scoreStatus: SCORE_STATUS.notSubmitted,
56462
- inProgress: "true",
56463
- metadata: {
56464
- isMasteryCompletion: true,
56465
- adminAction: true,
56466
- appName
56467
- }
56468
- });
56469
- } catch {
56470
- logger17.debug("No completion entry to revoke", {
56471
- studentId: data.studentId,
56472
- courseId: data.courseId
56473
- });
56474
- }
56475
- }
56523
+ await upsertMasteryCompletionEntry({
56524
+ client,
56525
+ courseId: data.courseId,
56526
+ studentId: data.studentId,
56527
+ appName,
56528
+ action: willBeMastered ? "complete" : "revoke"
56529
+ });
56476
56530
  }
56477
56531
  }
56478
56532
  return { status: "ok" };
56479
56533
  }
56534
+ async reconcileMasteryForConfigChange(gameId, courseId, user, context) {
56535
+ const { client, appName } = await this.resolveAdminMutationContext(gameId, courseId, user);
56536
+ const action = context.newMasterableUnits < context.oldMasterableUnits ? "complete" : "revoke";
56537
+ const failed = [];
56538
+ let processed = 0;
56539
+ await TimebackAdminService.runWithConcurrency(context.affectedStudentIds, 8, async (studentId) => {
56540
+ try {
56541
+ await upsertMasteryCompletionEntry({
56542
+ client,
56543
+ courseId,
56544
+ studentId,
56545
+ appName,
56546
+ action
56547
+ });
56548
+ processed++;
56549
+ } catch (error) {
56550
+ logger18.warn("Failed to reconcile mastery completion for student", {
56551
+ studentId,
56552
+ courseId,
56553
+ action,
56554
+ error: error instanceof Error ? error.message : String(error)
56555
+ });
56556
+ failed.push(studentId);
56557
+ }
56558
+ });
56559
+ return { processed, failed };
56560
+ }
56480
56561
  async searchStudentsForEnrollment(gameId, courseId, query, user) {
56481
56562
  const client = this.requireClient();
56482
56563
  await this.deps.validateGameManagementAccess(user, gameId);
@@ -56503,7 +56584,7 @@ class TimebackAdminService {
56503
56584
  const response = await client["request"](endpoint, "GET");
56504
56585
  allUsers = response.users || [];
56505
56586
  } catch (error) {
56506
- logger17.warn("Failed to search OneRoster users", {
56587
+ logger18.warn("Failed to search OneRoster users", {
56507
56588
  query: trimmedQuery,
56508
56589
  error: error instanceof Error ? error.message : String(error)
56509
56590
  });
@@ -56648,7 +56729,7 @@ class TimebackAdminService {
56648
56729
  return results;
56649
56730
  }
56650
56731
  }
56651
- var logger17;
56732
+ var logger18;
56652
56733
  var init_timeback_admin_service = __esm(() => {
56653
56734
  init_drizzle_orm();
56654
56735
  init_src();
@@ -56661,8 +56742,9 @@ var init_timeback_admin_service = __esm(() => {
56661
56742
  init_errors();
56662
56743
  init_timeback_admin_metrics_util();
56663
56744
  init_timeback_admin_util();
56745
+ init_timeback_mastery_completion_util();
56664
56746
  init_timeback_util();
56665
- logger17 = log.scope("TimebackAdminService");
56747
+ logger18 = log.scope("TimebackAdminService");
56666
56748
  });
56667
56749
  var __esm5 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
56668
56750
  var TIMEBACK_API_URLS4;
@@ -56935,7 +57017,7 @@ class TimebackAssessmentsService {
56935
57017
  isActive: row.bankActive
56936
57018
  };
56937
57019
  } catch {
56938
- logger18.warn("Failed to fetch QTI test metadata", {
57020
+ logger19.warn("Failed to fetch QTI test metadata", {
56939
57021
  identifier: row.qtiTestIdentifier
56940
57022
  });
56941
57023
  return {
@@ -56989,7 +57071,7 @@ class TimebackAssessmentsService {
56989
57071
  });
56990
57072
  } catch (error) {
56991
57073
  if (error instanceof TimebackApiError && error.status === 409) {
56992
- logger18.info("QTI test already exists (idempotent retry)", {
57074
+ logger19.info("QTI test already exists (idempotent retry)", {
56993
57075
  qtiTestIdentifier: input.qtiTestIdentifier
56994
57076
  });
56995
57077
  } else {
@@ -57002,7 +57084,7 @@ class TimebackAssessmentsService {
57002
57084
  qtiTestIdentifier: input.qtiTestIdentifier,
57003
57085
  sortOrder: maxSortOrder + 1
57004
57086
  }).returning();
57005
- logger18.info("Assessment created", {
57087
+ logger19.info("Assessment created", {
57006
57088
  integrationId,
57007
57089
  qtiTestIdentifier: input.qtiTestIdentifier
57008
57090
  });
@@ -57024,13 +57106,13 @@ class TimebackAssessmentsService {
57024
57106
  }
57025
57107
  await client.qti.tests.delete(qtiTestIdentifier);
57026
57108
  } catch (error) {
57027
- logger18.warn("Partial QTI cleanup during assessment deletion", {
57109
+ logger19.warn("Partial QTI cleanup during assessment deletion", {
57028
57110
  qtiTestIdentifier,
57029
57111
  error: error instanceof Error ? error.message : String(error)
57030
57112
  });
57031
57113
  }
57032
57114
  await this.deps.db.delete(gameTimebackAssessmentTests).where(eq(gameTimebackAssessmentTests.id, row.id));
57033
- logger18.info("Assessment deleted", { integrationId, qtiTestIdentifier });
57115
+ logger19.info("Assessment deleted", { integrationId, qtiTestIdentifier });
57034
57116
  }
57035
57117
  async reorderAssessments(integrationId, identifiers) {
57036
57118
  await this.requireIntegration(integrationId);
@@ -57075,7 +57157,7 @@ class TimebackAssessmentsService {
57075
57157
  return client.qti.tests.reorderItems(qtiTestIdentifier, partId, sectionId, items2);
57076
57158
  }
57077
57159
  async activateAssessment(integrationId, qtiTestIdentifier) {
57078
- logger18.debug("Activating assessment", { integrationId, qtiTestIdentifier });
57160
+ logger19.debug("Activating assessment", { integrationId, qtiTestIdentifier });
57079
57161
  const client = this.requireClient();
57080
57162
  const integration = await this.requireIntegration(integrationId);
57081
57163
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -57113,7 +57195,7 @@ class TimebackAssessmentsService {
57113
57195
  });
57114
57196
  }
57115
57197
  } catch {
57116
- logger18.warn("Failed to reactivate existing child resource, will create new", {
57198
+ logger19.warn("Failed to reactivate existing child resource, will create new", {
57117
57199
  childResourceId
57118
57200
  });
57119
57201
  childResourceId = null;
@@ -57126,7 +57208,7 @@ class TimebackAssessmentsService {
57126
57208
  const resourceUrl = resource.metadata?.url;
57127
57209
  if (resourceUrl === qtiTestUrl) {
57128
57210
  childResourceId = resourceId;
57129
- logger18.info("Found existing child Resource for QTI test (idempotent retry)", {
57211
+ logger19.info("Found existing child Resource for QTI test (idempotent retry)", {
57130
57212
  qtiTestIdentifier,
57131
57213
  childResourceId
57132
57214
  });
@@ -57135,7 +57217,7 @@ class TimebackAssessmentsService {
57135
57217
  } catch {}
57136
57218
  }
57137
57219
  if (!childResourceId) {
57138
- logger18.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
57220
+ logger19.debug("Creating child resource", { qtiTestIdentifier, qtiTestUrl });
57139
57221
  const childResult = await client.oneroster.resources.create({
57140
57222
  resource: {
57141
57223
  status: "active",
@@ -57163,10 +57245,10 @@ class TimebackAssessmentsService {
57163
57245
  }
57164
57246
  }
57165
57247
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: childResourceId, bankActive: true }).where(eq(gameTimebackAssessmentTests.id, row.id));
57166
- logger18.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
57248
+ logger19.info("Assessment activated", { integrationId, qtiTestIdentifier, childResourceId });
57167
57249
  }
57168
57250
  async deactivateAssessment(integrationId, qtiTestIdentifier) {
57169
- logger18.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
57251
+ logger19.debug("Deactivating assessment", { integrationId, qtiTestIdentifier });
57170
57252
  const client = this.requireClient();
57171
57253
  const integration = await this.requireIntegration(integrationId);
57172
57254
  const row = await this.requireAssessmentRow(integrationId, qtiTestIdentifier);
@@ -57179,7 +57261,7 @@ class TimebackAssessmentsService {
57179
57261
  const childResourceId = row.bankResourceId;
57180
57262
  const bankIds = deriveAssessmentBankIds(integration.courseId);
57181
57263
  try {
57182
- logger18.debug("Reading parent resource for deactivation", {
57264
+ logger19.debug("Reading parent resource for deactivation", {
57183
57265
  resourceId: bankIds.resource
57184
57266
  });
57185
57267
  const parentResource = await client.oneroster.resources.get(bankIds.resource);
@@ -57198,22 +57280,22 @@ class TimebackAssessmentsService {
57198
57280
  });
57199
57281
  }
57200
57282
  } catch (error) {
57201
- logger18.warn("Failed to update parent resource during deactivation", {
57283
+ logger19.warn("Failed to update parent resource during deactivation", {
57202
57284
  bankResourceId: bankIds.resource,
57203
57285
  error: error instanceof Error ? error.message : String(error)
57204
57286
  });
57205
57287
  }
57206
57288
  try {
57207
- logger18.debug("Deleting child resource", { childResourceId });
57289
+ logger19.debug("Deleting child resource", { childResourceId });
57208
57290
  await client.oneroster.resources.delete(childResourceId);
57209
57291
  } catch (error) {
57210
- logger18.warn("Failed to delete child resource (may already be deleted)", {
57292
+ logger19.warn("Failed to delete child resource (may already be deleted)", {
57211
57293
  childResourceId,
57212
57294
  error: error instanceof Error ? error.message : String(error)
57213
57295
  });
57214
57296
  }
57215
57297
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankActive: false }).where(eq(gameTimebackAssessmentTests.id, row.id));
57216
- logger18.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
57298
+ logger19.info("Assessment deactivated", { integrationId, qtiTestIdentifier });
57217
57299
  }
57218
57300
  isAssessmentActive(row) {
57219
57301
  return row.bankActive;
@@ -57244,14 +57326,14 @@ class TimebackAssessmentsService {
57244
57326
  status: "tobedeleted"
57245
57327
  });
57246
57328
  } catch (error) {
57247
- logger18.warn("Failed to delete component resource", { error });
57329
+ logger19.warn("Failed to delete component resource", { error });
57248
57330
  }
57249
57331
  for (const row of activeRows) {
57250
57332
  if (row.bankResourceId) {
57251
57333
  try {
57252
57334
  await client.oneroster.resources.delete(row.bankResourceId);
57253
57335
  } catch (error) {
57254
- logger18.warn("Failed to delete child resource", {
57336
+ logger19.warn("Failed to delete child resource", {
57255
57337
  childResourceId: row.bankResourceId,
57256
57338
  error
57257
57339
  });
@@ -57261,17 +57343,17 @@ class TimebackAssessmentsService {
57261
57343
  try {
57262
57344
  await client.oneroster.resources.delete(bankIds.resource);
57263
57345
  } catch (error) {
57264
- logger18.warn("Failed to delete parent resource", { error });
57346
+ logger19.warn("Failed to delete parent resource", { error });
57265
57347
  }
57266
57348
  try {
57267
57349
  await client.oneroster.courseComponents.update(bankIds.component, {
57268
57350
  status: "tobedeleted"
57269
57351
  });
57270
57352
  } catch (error) {
57271
- logger18.warn("Failed to delete course component", { error });
57353
+ logger19.warn("Failed to delete course component", { error });
57272
57354
  }
57273
57355
  await this.deps.db.update(gameTimebackAssessmentTests).set({ bankResourceId: null, bankActive: false }).where(eq(gameTimebackAssessmentTests.integrationId, integrationId));
57274
- logger18.info("Bank destroyed", { integrationId });
57356
+ logger19.info("Bank destroyed", { integrationId });
57275
57357
  }
57276
57358
  requireClient() {
57277
57359
  if (!this.deps.timeback) {
@@ -57300,7 +57382,7 @@ class TimebackAssessmentsService {
57300
57382
  async ensureBank(integration) {
57301
57383
  const client = this.requireClient();
57302
57384
  const bankIds = deriveAssessmentBankIds(integration.courseId);
57303
- logger18.debug("Ensuring assessment bank hierarchy", {
57385
+ logger19.debug("Ensuring assessment bank hierarchy", {
57304
57386
  courseId: integration.courseId,
57305
57387
  bankIds
57306
57388
  });
@@ -57317,7 +57399,7 @@ class TimebackAssessmentsService {
57317
57399
  });
57318
57400
  } catch (error) {
57319
57401
  if (error instanceof TimebackApiError && error.status === 409) {
57320
- logger18.debug("Course component already exists", { sourcedId: bankIds.component });
57402
+ logger19.debug("Course component already exists", { sourcedId: bankIds.component });
57321
57403
  } else {
57322
57404
  throw error;
57323
57405
  }
@@ -57341,7 +57423,7 @@ class TimebackAssessmentsService {
57341
57423
  });
57342
57424
  } catch (error) {
57343
57425
  if (error instanceof TimebackApiError && error.status === 409) {
57344
- logger18.debug("Parent resource already exists", { sourcedId: bankIds.resource });
57426
+ logger19.debug("Parent resource already exists", { sourcedId: bankIds.resource });
57345
57427
  } else {
57346
57428
  throw error;
57347
57429
  }
@@ -57361,14 +57443,14 @@ class TimebackAssessmentsService {
57361
57443
  });
57362
57444
  } catch (error) {
57363
57445
  if (error instanceof TimebackApiError && error.status === 409) {
57364
- logger18.debug("Component resource already exists", {
57446
+ logger19.debug("Component resource already exists", {
57365
57447
  sourcedId: bankIds.componentResource
57366
57448
  });
57367
57449
  } else {
57368
57450
  throw error;
57369
57451
  }
57370
57452
  }
57371
- logger18.info("Assessment bank hierarchy created", {
57453
+ logger19.info("Assessment bank hierarchy created", {
57372
57454
  courseId: integration.courseId
57373
57455
  });
57374
57456
  }
@@ -57383,7 +57465,7 @@ class TimebackAssessmentsService {
57383
57465
  return Math.max(...rows.map((r) => r.sortOrder));
57384
57466
  }
57385
57467
  }
57386
- var logger18;
57468
+ var logger19;
57387
57469
  var init_timeback_assessments_service = __esm(() => {
57388
57470
  init_drizzle_orm();
57389
57471
  init_tables_index();
@@ -57392,7 +57474,7 @@ var init_timeback_assessments_service = __esm(() => {
57392
57474
  init_errors4();
57393
57475
  init_utils6();
57394
57476
  init_errors();
57395
- logger18 = log.scope("TimebackAssessmentsService");
57477
+ logger19 = log.scope("TimebackAssessmentsService");
57396
57478
  });
57397
57479
  async function promoteCompletedCourse({
57398
57480
  db: db2,
@@ -57406,7 +57488,7 @@ async function promoteCompletedCourse({
57406
57488
  });
57407
57489
  const nextIntegration = subjectIntegrations.filter((integration) => integration.grade > currentIntegration.grade).toSorted((left, right) => left.grade - right.grade)[0];
57408
57490
  if (!nextIntegration) {
57409
- logger19.debug("Skipping promotion because no next course is configured", {
57491
+ logger20.debug("Skipping promotion because no next course is configured", {
57410
57492
  gameId: currentIntegration.gameId,
57411
57493
  studentId,
57412
57494
  grade: currentIntegration.grade,
@@ -57423,7 +57505,7 @@ async function promoteCompletedCourse({
57423
57505
  const nextEnrollment = enrollments.find((enrollment) => enrollment.course.id === nextIntegration.courseId);
57424
57506
  if (!currentEnrollment) {
57425
57507
  if (nextEnrollment) {
57426
- logger19.debug("Skipping promotion because student is already on the next course", {
57508
+ logger20.debug("Skipping promotion because student is already on the next course", {
57427
57509
  gameId: currentIntegration.gameId,
57428
57510
  studentId,
57429
57511
  grade: currentIntegration.grade,
@@ -57437,7 +57519,7 @@ async function promoteCompletedCourse({
57437
57519
  nextCourseId: nextIntegration.courseId
57438
57520
  };
57439
57521
  }
57440
- logger19.debug("Skipping promotion because student is not enrolled in the current course", {
57522
+ logger20.debug("Skipping promotion because student is not enrolled in the current course", {
57441
57523
  gameId: currentIntegration.gameId,
57442
57524
  studentId,
57443
57525
  grade: currentIntegration.grade,
@@ -57468,7 +57550,7 @@ async function promoteCompletedCourse({
57468
57550
  client.invalidateEnrollments(studentId);
57469
57551
  }
57470
57552
  }
57471
- logger19.info("Promoted student to next course", {
57553
+ logger20.info("Promoted student to next course", {
57472
57554
  gameId: currentIntegration.gameId,
57473
57555
  studentId,
57474
57556
  subject: currentIntegration.subject,
@@ -57483,14 +57565,14 @@ async function promoteCompletedCourse({
57483
57565
  nextCourseId: nextIntegration.courseId
57484
57566
  };
57485
57567
  }
57486
- var logger19;
57568
+ var logger20;
57487
57569
  var init_timeback_promotion_util = __esm(() => {
57488
57570
  init_drizzle_orm();
57489
57571
  init_tables_index();
57490
57572
  init_src2();
57491
- logger19 = log.scope("TimebackPromotion");
57573
+ logger20 = log.scope("TimebackPromotion");
57492
57574
  });
57493
- var logger20;
57575
+ var logger21;
57494
57576
  var TimebackService;
57495
57577
  var init_timeback_service = __esm(() => {
57496
57578
  init_drizzle_orm();
@@ -57502,7 +57584,7 @@ var init_timeback_service = __esm(() => {
57502
57584
  init_errors();
57503
57585
  init_timeback_promotion_util();
57504
57586
  init_timeback_util();
57505
- logger20 = log.scope("TimebackService");
57587
+ logger21 = log.scope("TimebackService");
57506
57588
  TimebackService = class TimebackService2 {
57507
57589
  static HEARTBEAT_DEDUPE_TTL_MS = 300000;
57508
57590
  static processedHeartbeatWindows = new Map;
@@ -57548,7 +57630,7 @@ var init_timeback_service = __esm(() => {
57548
57630
  }
57549
57631
  requireClient() {
57550
57632
  if (!this.deps.timeback) {
57551
- logger20.error("Timeback client not available in context");
57633
+ logger21.error("Timeback client not available in context");
57552
57634
  throw new ValidationError("Timeback integration not available in this environment");
57553
57635
  }
57554
57636
  return this.deps.timeback;
@@ -57601,7 +57683,7 @@ var init_timeback_service = __esm(() => {
57601
57683
  set: { xp: sql`excluded.xp`, updatedAt: new Date }
57602
57684
  }).returning({ xp: timebackDailyXp.xp, date: timebackDailyXp.date });
57603
57685
  if (!result) {
57604
- logger20.error("Daily XP upsert returned no rows", { userId, date: targetDate });
57686
+ logger21.error("Daily XP upsert returned no rows", { userId, date: targetDate });
57605
57687
  throw new InternalError("Failed to update daily XP record");
57606
57688
  }
57607
57689
  return { xp: result.xp, date: result.date.toISOString() };
@@ -57632,7 +57714,7 @@ var init_timeback_service = __esm(() => {
57632
57714
  columns: { id: true, timebackId: true }
57633
57715
  });
57634
57716
  if (dbUser?.timebackId) {
57635
- logger20.info("Student already onboarded", { userId: user.id });
57717
+ logger21.info("Student already onboarded", { userId: user.id });
57636
57718
  return { status: "already_populated" };
57637
57719
  }
57638
57720
  let timebackId;
@@ -57641,7 +57723,7 @@ var init_timeback_service = __esm(() => {
57641
57723
  const existingUser = await client.oneroster.users.findByEmail(user.email);
57642
57724
  timebackId = existingUser.sourcedId;
57643
57725
  name3 = `${existingUser.givenName} ${existingUser.familyName}`;
57644
- logger20.info("Found existing student in OneRoster", {
57726
+ logger21.info("Found existing student in OneRoster", {
57645
57727
  userId: user.id,
57646
57728
  timebackId
57647
57729
  });
@@ -57670,7 +57752,7 @@ var init_timeback_service = __esm(() => {
57670
57752
  }
57671
57753
  timebackId = response.sourcedIdPairs.allocatedSourcedId;
57672
57754
  name3 = `${providedNames.firstName} ${providedNames.lastName}`;
57673
- logger20.info("Created student in OneRoster", { userId: user.id, timebackId });
57755
+ logger21.info("Created student in OneRoster", { userId: user.id, timebackId });
57674
57756
  }
57675
57757
  const assessments = await this.fetchAssessments(timebackId);
57676
57758
  await db2.transaction(async (tx) => {
@@ -57704,7 +57786,7 @@ var init_timeback_service = __esm(() => {
57704
57786
  }
57705
57787
  const [updated] = await tx.update(users).set({ timebackId, name: name3 }).where(eq(users.id, user.id)).returning({ id: users.id });
57706
57788
  if (!updated) {
57707
- logger20.error("User Timeback ID update returned no rows", {
57789
+ logger21.error("User Timeback ID update returned no rows", {
57708
57790
  userId: user.id,
57709
57791
  timebackId
57710
57792
  });
@@ -57728,13 +57810,13 @@ var init_timeback_service = __esm(() => {
57728
57810
  }
57729
57811
  offset += limit;
57730
57812
  }
57731
- logger20.debug("Fetched assessments", {
57813
+ logger21.debug("Fetched assessments", {
57732
57814
  studentSourcedId,
57733
57815
  totalCount: allAssessments.length
57734
57816
  });
57735
57817
  return allAssessments;
57736
57818
  } catch (error) {
57737
- logger20.warn("Failed to fetch assessments", { studentSourcedId, error });
57819
+ logger21.warn("Failed to fetch assessments", { studentSourcedId, error });
57738
57820
  return [];
57739
57821
  }
57740
57822
  }
@@ -57847,7 +57929,7 @@ var init_timeback_service = __esm(() => {
57847
57929
  masterableUnits: derivedMasterableUnits
57848
57930
  } = courseConfig;
57849
57931
  if (!isTimebackSubject(subjectInput)) {
57850
- logger20.warn("Invalid Timeback subject in course config", {
57932
+ logger21.warn("Invalid Timeback subject in course config", {
57851
57933
  subject: subjectInput,
57852
57934
  courseCode,
57853
57935
  title
@@ -57855,7 +57937,7 @@ var init_timeback_service = __esm(() => {
57855
57937
  throw new ValidationError(`Invalid subject "${subjectInput}"`);
57856
57938
  }
57857
57939
  if (!isTimebackGrade(grade)) {
57858
- logger20.warn("Invalid Timeback grade in course config", {
57940
+ logger21.warn("Invalid Timeback grade in course config", {
57859
57941
  grade,
57860
57942
  courseCode,
57861
57943
  title
@@ -57867,7 +57949,7 @@ var init_timeback_service = __esm(() => {
57867
57949
  const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
57868
57950
  const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
57869
57951
  if (typeof totalXp !== "number") {
57870
- logger20.warn("Course missing totalXp in Timeback config", {
57952
+ logger21.warn("Course missing totalXp in Timeback config", {
57871
57953
  courseCode,
57872
57954
  title
57873
57955
  });
@@ -57883,7 +57965,9 @@ var init_timeback_service = __esm(() => {
57883
57965
  courseCode,
57884
57966
  level,
57885
57967
  gradingScheme: "STANDARD",
57886
- metadata: metadata2
57968
+ metadata: TimebackService2.patchCourseMetadata(metadata2, totalXp, {
57969
+ masterableUnits: masterableUnits ?? null
57970
+ })
57887
57971
  },
57888
57972
  component: {
57889
57973
  ...baseConfig.component,
@@ -57908,7 +57992,7 @@ var init_timeback_service = __esm(() => {
57908
57992
  const existingIntegration = existing.find((i2) => i2.grade === grade && i2.subject === subject);
57909
57993
  if (existingIntegration) {
57910
57994
  await client.update(existingIntegration.courseId, fullConfig);
57911
- const [updated] = await db2.update(gameTimebackIntegrations).set({ totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
57995
+ const [updated] = await db2.update(gameTimebackIntegrations).set({ subject, totalXp, updatedAt: new Date }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
57912
57996
  if (updated) {
57913
57997
  integrations.push(this.toGameTimebackIntegration(updated));
57914
57998
  }
@@ -57933,6 +58017,60 @@ var init_timeback_service = __esm(() => {
57933
58017
  });
57934
58018
  return rows.map((row) => this.toGameTimebackIntegration(row));
57935
58019
  }
58020
+ async getIntegrationConfig(gameId, courseId, user) {
58021
+ const client = this.requireClient();
58022
+ await this.deps.validateGameManagementAccess(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 config2 = await client.getConfig(courseId);
58030
+ return this.toGameTimebackIntegrationConfig(integration, config2);
58031
+ }
58032
+ async updateIntegration(gameId, courseId, user, patch) {
58033
+ const client = this.requireClient();
58034
+ await this.deps.validateDeveloperAccess(user, gameId);
58035
+ const integration = await this.deps.db.query.gameTimebackIntegrations.findFirst({
58036
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
58037
+ });
58038
+ if (!integration) {
58039
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
58040
+ }
58041
+ const timebackConfig = await client.getConfig(courseId);
58042
+ const liveSubject = timebackConfig.course.subjects[0];
58043
+ const subject = patch.subject ?? (isTimebackSubject(liveSubject) ? liveSubject : integration.subject);
58044
+ if (!isTimebackSubject(subject)) {
58045
+ throw new ValidationError(`Invalid subject "${subject}"`);
58046
+ }
58047
+ if (subject !== integration.subject) {
58048
+ const subjectConflict = await this.deps.db.query.gameTimebackIntegrations.findFirst({
58049
+ where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, integration.grade), eq(gameTimebackIntegrations.subject, subject))
58050
+ });
58051
+ if (subjectConflict && subjectConflict.id !== integration.id) {
58052
+ throw new ValidationError(`A TimeBack integration already exists for ${subject} Grade ${integration.grade}`);
58053
+ }
58054
+ }
58055
+ const totalXp = "totalXp" in patch ? patch.totalXp ?? null : TimebackService2.getTotalXpFromConfig(timebackConfig) ?? integration.totalXp ?? null;
58056
+ const masterableUnits = "masterableUnits" in patch ? patch.masterableUnits ?? null : TimebackService2.getMasterableUnitsFromConfig(timebackConfig);
58057
+ await client.update(courseId, TimebackService2.patchTimebackConfig(timebackConfig, {
58058
+ ...patch,
58059
+ subject,
58060
+ totalXp,
58061
+ masterableUnits,
58062
+ grade: integration.grade
58063
+ }));
58064
+ const [updated] = await this.deps.db.update(gameTimebackIntegrations).set({
58065
+ subject,
58066
+ totalXp,
58067
+ updatedAt: new Date
58068
+ }).where(eq(gameTimebackIntegrations.id, integration.id)).returning();
58069
+ if (!updated) {
58070
+ throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
58071
+ }
58072
+ return this.toGameTimebackIntegration(updated);
58073
+ }
57936
58074
  async verifyIntegration(gameId, user) {
57937
58075
  const client = this.requireClient();
57938
58076
  const db2 = this.deps.db;
@@ -57990,6 +58128,146 @@ var init_timeback_service = __esm(() => {
57990
58128
  }
57991
58129
  await db2.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
57992
58130
  }
58131
+ static getMasterableUnitsFromConfig(config2) {
58132
+ const playcademyMetadata = config2.resource.metadata?.playcademy;
58133
+ if (!isPlaycademyResourceMetadata(playcademyMetadata)) {
58134
+ return null;
58135
+ }
58136
+ return playcademyMetadata?.mastery?.masterableUnits ?? null;
58137
+ }
58138
+ static getTotalXpFromConfig(config2) {
58139
+ const courseMetadata = isCourseMetadata(config2.course.metadata) ? config2.course.metadata : undefined;
58140
+ if (typeof courseMetadata?.metrics?.totalXp === "number") {
58141
+ return courseMetadata.metrics.totalXp;
58142
+ }
58143
+ const resourceMetadata = config2.resource.metadata;
58144
+ if (isRecord2(resourceMetadata) && typeof resourceMetadata.xp === "number") {
58145
+ return resourceMetadata.xp;
58146
+ }
58147
+ return null;
58148
+ }
58149
+ static patchCourseMetadata(metadata2, totalXp, options) {
58150
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
58151
+ const currentMetrics = isRecord2(nextMetadata.metrics) ? nextMetadata.metrics : {};
58152
+ const metrics = { ...currentMetrics };
58153
+ if (totalXp === null) {
58154
+ delete metrics.totalXp;
58155
+ } else {
58156
+ metrics.totalXp = totalXp;
58157
+ }
58158
+ if (options?.masterableUnits !== undefined) {
58159
+ if (options.masterableUnits === null) {
58160
+ delete metrics.totalLessons;
58161
+ } else {
58162
+ metrics.totalLessons = options.masterableUnits;
58163
+ }
58164
+ }
58165
+ metrics.totalGrades = 1;
58166
+ if (Object.keys(metrics).length > 0) {
58167
+ nextMetadata.metrics = metrics;
58168
+ } else {
58169
+ delete nextMetadata.metrics;
58170
+ }
58171
+ const goals = options?.goals;
58172
+ if (goals !== undefined) {
58173
+ if (goals === null) {
58174
+ delete nextMetadata.goals;
58175
+ } else {
58176
+ const currentGoals = isRecord2(nextMetadata.goals) ? nextMetadata.goals : {};
58177
+ const nextGoals = { ...currentGoals };
58178
+ for (const [key, value] of Object.entries(goals)) {
58179
+ if (value === null) {
58180
+ delete nextGoals[key];
58181
+ } else if (value !== undefined) {
58182
+ nextGoals[key] = value;
58183
+ }
58184
+ }
58185
+ if (Object.keys(nextGoals).length > 0) {
58186
+ nextMetadata.goals = nextGoals;
58187
+ } else {
58188
+ delete nextMetadata.goals;
58189
+ }
58190
+ }
58191
+ }
58192
+ if (options?.publishStatus !== undefined) {
58193
+ if (options.publishStatus === null) {
58194
+ delete nextMetadata.publishStatus;
58195
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
58196
+ delete alphaLearn.publishStatus;
58197
+ if (Object.keys(alphaLearn).length > 0) {
58198
+ nextMetadata.AlphaLearn = alphaLearn;
58199
+ } else {
58200
+ delete nextMetadata.AlphaLearn;
58201
+ }
58202
+ } else {
58203
+ nextMetadata.publishStatus = options.publishStatus;
58204
+ const alphaLearn = isRecord2(nextMetadata.AlphaLearn) ? { ...nextMetadata.AlphaLearn } : {};
58205
+ alphaLearn.publishStatus = options.publishStatus === "published" ? "active" : options.publishStatus;
58206
+ nextMetadata.AlphaLearn = alphaLearn;
58207
+ }
58208
+ }
58209
+ if (options?.isSupplemental !== undefined) {
58210
+ nextMetadata.isSupplemental = options.isSupplemental;
58211
+ }
58212
+ if (options?.timebackVisible !== undefined) {
58213
+ if (options.timebackVisible === null) {
58214
+ delete nextMetadata.timebackVisible;
58215
+ } else {
58216
+ nextMetadata.timebackVisible = options.timebackVisible;
58217
+ }
58218
+ }
58219
+ return Object.keys(nextMetadata).length > 0 ? nextMetadata : undefined;
58220
+ }
58221
+ static patchResourceMetadata(metadata2, options) {
58222
+ const nextMetadata = isRecord2(metadata2) ? { ...metadata2 } : {};
58223
+ const playcademyMetadata = isRecord2(nextMetadata.playcademy) ? { ...nextMetadata.playcademy } : {};
58224
+ const masteryMetadata = isRecord2(playcademyMetadata.mastery) ? { ...playcademyMetadata.mastery } : {};
58225
+ nextMetadata.subject = options.subject;
58226
+ nextMetadata.grades = [options.grade];
58227
+ if (options.totalXp === null) {
58228
+ delete nextMetadata.xp;
58229
+ } else {
58230
+ nextMetadata.xp = options.totalXp;
58231
+ }
58232
+ if (options.masterableUnits === null) {
58233
+ delete masteryMetadata.masterableUnits;
58234
+ } else {
58235
+ masteryMetadata.masterableUnits = options.masterableUnits;
58236
+ }
58237
+ if (Object.keys(masteryMetadata).length > 0) {
58238
+ playcademyMetadata.mastery = masteryMetadata;
58239
+ } else {
58240
+ delete playcademyMetadata.mastery;
58241
+ }
58242
+ if (Object.keys(playcademyMetadata).length > 0) {
58243
+ nextMetadata.playcademy = playcademyMetadata;
58244
+ } else {
58245
+ delete nextMetadata.playcademy;
58246
+ }
58247
+ return nextMetadata;
58248
+ }
58249
+ static patchTimebackConfig(config2, patch) {
58250
+ return {
58251
+ ...config2,
58252
+ course: {
58253
+ ...config2.course,
58254
+ title: patch.title ?? config2.course.title,
58255
+ courseCode: patch.courseCode ?? config2.course.courseCode,
58256
+ subjects: [patch.subject],
58257
+ metadata: TimebackService2.patchCourseMetadata(config2.course.metadata, patch.totalXp, {
58258
+ masterableUnits: patch.masterableUnits,
58259
+ goals: patch.goals,
58260
+ publishStatus: patch.publishStatus,
58261
+ isSupplemental: patch.isSupplemental,
58262
+ timebackVisible: patch.timebackVisible
58263
+ })
58264
+ },
58265
+ resource: {
58266
+ ...config2.resource,
58267
+ metadata: TimebackService2.patchResourceMetadata(config2.resource.metadata, patch)
58268
+ }
58269
+ };
58270
+ }
57993
58271
  toGameTimebackIntegration(integration) {
57994
58272
  return {
57995
58273
  id: integration.id,
@@ -58003,6 +58281,21 @@ var init_timeback_service = __esm(() => {
58003
58281
  lastVerifiedAt: integration.lastVerifiedAt ?? null
58004
58282
  };
58005
58283
  }
58284
+ toGameTimebackIntegrationConfig(integration, config2) {
58285
+ const subject = config2.course.subjects[0] ?? integration.subject;
58286
+ if (!isTimebackSubject(subject)) {
58287
+ throw new ValidationError(`Invalid subject "${subject}"`);
58288
+ }
58289
+ return {
58290
+ integration: this.toGameTimebackIntegration(integration),
58291
+ title: config2.course.title,
58292
+ courseCode: config2.course.courseCode,
58293
+ subject,
58294
+ totalXp: TimebackService2.getTotalXpFromConfig(config2) ?? integration.totalXp ?? null,
58295
+ masterableUnits: TimebackService2.getMasterableUnitsFromConfig(config2),
58296
+ metadata: isCourseMetadata(config2.course.metadata) ? config2.course.metadata : null
58297
+ };
58298
+ }
58006
58299
  async endActivity({
58007
58300
  gameId,
58008
58301
  studentId,
@@ -58068,7 +58361,7 @@ var init_timeback_service = __esm(() => {
58068
58361
  ...runId ? { runId } : {}
58069
58362
  });
58070
58363
  }
58071
- logger20.info("Recorded activity completion", {
58364
+ logger21.info("Recorded activity completion", {
58072
58365
  gameId,
58073
58366
  courseId: integration.courseId,
58074
58367
  studentId,
@@ -58122,7 +58415,7 @@ var init_timeback_service = __esm(() => {
58122
58415
  masteredUnits: masteryStatus.masteredUnits,
58123
58416
  masterableUnits: masteryStatus.masterableUnits
58124
58417
  };
58125
- logger20.debug("Skipping course advancement because mastery is incomplete", {
58418
+ logger21.debug("Skipping course advancement because mastery is incomplete", {
58126
58419
  gameId,
58127
58420
  studentId,
58128
58421
  subject: currentIntegration.subject,
@@ -58140,7 +58433,7 @@ var init_timeback_service = __esm(() => {
58140
58433
  studentId,
58141
58434
  enrollments
58142
58435
  });
58143
- logger20.info("Manually advanced student", {
58436
+ logger21.info("Manually advanced student", {
58144
58437
  gameId,
58145
58438
  studentId,
58146
58439
  subject: currentIntegration.subject,
@@ -58173,7 +58466,7 @@ var init_timeback_service = __esm(() => {
58173
58466
  const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
58174
58467
  const effectiveResumeId = resumeId ?? runId;
58175
58468
  if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
58176
- logger20.debug("Skipping duplicate heartbeat window", {
58469
+ logger21.debug("Skipping duplicate heartbeat window", {
58177
58470
  gameId,
58178
58471
  studentId,
58179
58472
  runId,
@@ -58186,7 +58479,7 @@ var init_timeback_service = __esm(() => {
58186
58479
  await this.deps.validateDeveloperAccess(user, gameId);
58187
58480
  const inFlightHeartbeat = TimebackService2.getInFlightHeartbeatWindow(heartbeatWindowKey);
58188
58481
  if (inFlightHeartbeat) {
58189
- logger20.debug("Joining in-flight heartbeat window", {
58482
+ logger21.debug("Joining in-flight heartbeat window", {
58190
58483
  gameId,
58191
58484
  studentId,
58192
58485
  runId,
@@ -58223,7 +58516,7 @@ var init_timeback_service = __esm(() => {
58223
58516
  });
58224
58517
  }
58225
58518
  TimebackService2.markHeartbeatWindowProcessed(heartbeatWindowKey);
58226
- logger20.debug("Recorded heartbeat", {
58519
+ logger21.debug("Recorded heartbeat", {
58227
58520
  gameId,
58228
58521
  courseId: integration.courseId,
58229
58522
  studentId,
@@ -58258,7 +58551,7 @@ var init_timeback_service = __esm(() => {
58258
58551
  });
58259
58552
  courseIds = integrations.map((i2) => i2.courseId);
58260
58553
  if (courseIds.length === 0) {
58261
- logger20.debug("No integrations found for game, returning 0 XP", {
58554
+ logger21.debug("No integrations found for game, returning 0 XP", {
58262
58555
  timebackId,
58263
58556
  gameId: options.gameId,
58264
58557
  grade: options.grade,
@@ -58275,7 +58568,7 @@ var init_timeback_service = __esm(() => {
58275
58568
  courseIds: courseIds.length > 0 ? courseIds : undefined,
58276
58569
  include: options?.include
58277
58570
  });
58278
- logger20.debug("Retrieved student XP", {
58571
+ logger21.debug("Retrieved student XP", {
58279
58572
  timebackId,
58280
58573
  gameId: options?.gameId,
58281
58574
  grade: options?.grade,
@@ -58303,15 +58596,15 @@ class UploadService {
58303
58596
  const { fileName, gameId } = request;
58304
58597
  const bucketName = this.deps.uploadBucket;
58305
58598
  if (!bucketName) {
58306
- logger21.error("Upload bucket not configured in environment");
58599
+ logger22.error("Upload bucket not configured in environment");
58307
58600
  throw new ValidationError("Upload bucket not configured");
58308
58601
  }
58309
58602
  await this.deps.validateDeveloperAccess(user, gameId);
58310
58603
  const version2 = ulid();
58311
58604
  const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
58312
- logger21.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
58605
+ logger22.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
58313
58606
  const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
58314
- logger21.info("Presigned URL generated", {
58607
+ logger22.info("Presigned URL generated", {
58315
58608
  userId: user.id,
58316
58609
  gameId,
58317
58610
  version: version2
@@ -58324,12 +58617,12 @@ class UploadService {
58324
58617
  };
58325
58618
  }
58326
58619
  }
58327
- var logger21;
58620
+ var logger22;
58328
58621
  var init_upload_service = __esm(() => {
58329
58622
  init_node();
58330
58623
  init_src2();
58331
58624
  init_errors();
58332
- logger21 = log.scope("UploadService");
58625
+ logger22 = log.scope("UploadService");
58333
58626
  });
58334
58627
  function createPlatformServices(deps) {
58335
58628
  const {
@@ -58630,7 +58923,7 @@ class AchievementService {
58630
58923
  results.push(result);
58631
58924
  }
58632
58925
  }
58633
- logger22.debug("Listed current achievements", { userId: user.id, count: results.length });
58926
+ logger23.debug("Listed current achievements", { userId: user.id, count: results.length });
58634
58927
  return results;
58635
58928
  }
58636
58929
  async listHistory(user, limit) {
@@ -58648,14 +58941,14 @@ class AchievementService {
58648
58941
  createdAt: c.createdAt,
58649
58942
  scopeKey: c.scopeKey
58650
58943
  }));
58651
- logger22.debug("Listed achievement history", { userId: user.id, count: results.length });
58944
+ logger23.debug("Listed achievement history", { userId: user.id, count: results.length });
58652
58945
  return results;
58653
58946
  }
58654
58947
  async submitProgress(achievementId, user) {
58655
58948
  const { claim, wasNewClaim } = await this.award(user.id, achievementId, {
58656
58949
  broadcast: false
58657
58950
  });
58658
- logger22.debug("Submitted progress", {
58951
+ logger23.debug("Submitted progress", {
58659
58952
  userId: user.id,
58660
58953
  achievementId,
58661
58954
  wasNewClaim
@@ -58687,7 +58980,7 @@ class AchievementService {
58687
58980
  rewardCredits
58688
58981
  }).returning();
58689
58982
  if (!newClaim) {
58690
- logger22.error("Achievement claim insert returned no rows", {
58983
+ logger23.error("Achievement claim insert returned no rows", {
58691
58984
  userId,
58692
58985
  achievementId,
58693
58986
  scopeKey
@@ -58696,7 +58989,7 @@ class AchievementService {
58696
58989
  }
58697
58990
  await this.deps.addCredits(userId, rewardCredits);
58698
58991
  await this.deps.createAchievementNotification(userId, achievement, rewardCredits, scopeKey, { broadcast, metadata: metadata2 });
58699
- logger22.info("Awarded achievement", {
58992
+ logger23.info("Awarded achievement", {
58700
58993
  userId,
58701
58994
  achievementId,
58702
58995
  scopeKey,
@@ -58733,7 +59026,7 @@ class AchievementService {
58733
59026
  return { title, body: body2 };
58734
59027
  }
58735
59028
  }
58736
- var logger22;
59029
+ var logger23;
58737
59030
  var init_achievement_service = __esm(() => {
58738
59031
  init_drizzle_orm();
58739
59032
  init_tables_index();
@@ -58742,7 +59035,7 @@ var init_achievement_service = __esm(() => {
58742
59035
  init_errors();
58743
59036
  init_leaderboard_util();
58744
59037
  init_scope_util();
58745
- logger22 = log.scope("AchievementService");
59038
+ logger23 = log.scope("AchievementService");
58746
59039
  });
58747
59040
 
58748
59041
  class InventoryService {
@@ -58769,7 +59062,7 @@ class InventoryService {
58769
59062
  },
58770
59063
  updatedAt: inventoryItems.updatedAt
58771
59064
  }).from(inventoryItems).where(eq(inventoryItems.userId, user.id)).innerJoin(items, eq(inventoryItems.itemId, items.id));
58772
- logger23.debug("Listed inventory", { userId: user.id, count: inventory.length });
59065
+ logger24.debug("Listed inventory", { userId: user.id, count: inventory.length });
58773
59066
  return inventory;
58774
59067
  }
58775
59068
  async addItem(itemId, quantity, user) {
@@ -58786,7 +59079,7 @@ class InventoryService {
58786
59079
  const [inserted] = await tx.insert(inventoryItems).values({ userId: user.id, itemId, quantity }).returning({ quantity: inventoryItems.quantity });
58787
59080
  return inserted?.quantity ?? 0;
58788
59081
  });
58789
- logger23.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
59082
+ logger24.debug("Added item", { userId: user.id, itemId, quantity, newTotal });
58790
59083
  return { newTotal };
58791
59084
  }
58792
59085
  async removeItem(itemId, quantity, user) {
@@ -58797,7 +59090,7 @@ class InventoryService {
58797
59090
  }
58798
59091
  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);
58799
59092
  if (!currentItem) {
58800
- logger23.warn("Insufficient inventory for removal", {
59093
+ logger24.warn("Insufficient inventory for removal", {
58801
59094
  userId: user.id,
58802
59095
  itemId,
58803
59096
  requestedQuantity: quantity
@@ -58807,13 +59100,13 @@ class InventoryService {
58807
59100
  const [updated] = await tx.update(inventoryItems).set({ quantity: sql`${inventoryItems.quantity} - ${quantity}` }).where(eq(inventoryItems.id, currentItem.id)).returning({ quantity: inventoryItems.quantity });
58808
59101
  return updated?.quantity ?? 0;
58809
59102
  });
58810
- logger23.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
59103
+ logger24.debug("Removed item", { userId: user.id, itemId, quantity, newTotal });
58811
59104
  return { newTotal };
58812
59105
  }
58813
59106
  async addCredits(userId, amount) {
58814
59107
  const [creditsItem] = await this.deps.db.select({ id: items.id }).from(items).where(eq(items.slug, CURRENCIES.PRIMARY)).limit(1);
58815
59108
  if (!creditsItem) {
58816
- logger23.error("Primary currency not found", {
59109
+ logger24.error("Primary currency not found", {
58817
59110
  userId,
58818
59111
  amount
58819
59112
  });
@@ -58826,17 +59119,17 @@ class InventoryService {
58826
59119
  updatedAt: new Date
58827
59120
  }
58828
59121
  });
58829
- logger23.debug("Added credits", { userId, amount });
59122
+ logger24.debug("Added credits", { userId, amount });
58830
59123
  }
58831
59124
  }
58832
- var logger23;
59125
+ var logger24;
58833
59126
  var init_inventory_service = __esm(() => {
58834
59127
  init_drizzle_orm();
58835
59128
  init_src();
58836
59129
  init_tables_index();
58837
59130
  init_src2();
58838
59131
  init_errors();
58839
- logger23 = log.scope("InventoryService");
59132
+ logger24 = log.scope("InventoryService");
58840
59133
  });
58841
59134
 
58842
59135
  class LeaderboardService {
@@ -58867,7 +59160,7 @@ class LeaderboardService {
58867
59160
  sessionId
58868
59161
  }).returning();
58869
59162
  if (!newScore) {
58870
- logger24.error("Score insert returned no rows", { userId, gameId, score: input.score });
59163
+ logger25.error("Score insert returned no rows", { userId, gameId, score: input.score });
58871
59164
  throw new InternalError("Failed to insert score");
58872
59165
  }
58873
59166
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
@@ -58895,7 +59188,7 @@ class LeaderboardService {
58895
59188
  movedUpWithinTop3
58896
59189
  });
58897
59190
  }
58898
- logger24.info("Score submitted", {
59191
+ logger25.info("Score submitted", {
58899
59192
  gameId,
58900
59193
  userId,
58901
59194
  isAnonymousUser,
@@ -58972,7 +59265,7 @@ class LeaderboardService {
58972
59265
  });
58973
59266
  }
58974
59267
  } catch (error) {
58975
- logger24.warn("Failed to publish notification", { error });
59268
+ logger25.warn("Failed to publish notification", { error });
58976
59269
  }
58977
59270
  }
58978
59271
  async getLeaderboard(gameId, query, isAnonymousUser) {
@@ -59091,7 +59384,7 @@ class LeaderboardService {
59091
59384
  return db2.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(effectiveLimit);
59092
59385
  }
59093
59386
  }
59094
- var logger24;
59387
+ var logger25;
59095
59388
  var init_leaderboard_service = __esm(() => {
59096
59389
  init_drizzle_orm();
59097
59390
  init_src();
@@ -59101,7 +59394,7 @@ var init_leaderboard_service = __esm(() => {
59101
59394
  init_notification();
59102
59395
  init_errors();
59103
59396
  init_leaderboard_util();
59104
- logger24 = log.scope("LeaderboardService");
59397
+ logger25 = log.scope("LeaderboardService");
59105
59398
  });
59106
59399
 
59107
59400
  class LevelService {
@@ -59116,9 +59409,9 @@ class LevelService {
59116
59409
  for (const config2 of configs) {
59117
59410
  levelConfigCache.set(config2.level, config2);
59118
59411
  }
59119
- logger25.info("Cache pre-warmed", { count: configs.length });
59412
+ logger26.info("Cache pre-warmed", { count: configs.length });
59120
59413
  } catch (error) {
59121
- logger25.error("Cache pre-warm failed", { error });
59414
+ logger26.error("Cache pre-warm failed", { error });
59122
59415
  }
59123
59416
  }
59124
59417
  async getConfig(level) {
@@ -59152,7 +59445,7 @@ class LevelService {
59152
59445
  totalXP
59153
59446
  }).returning();
59154
59447
  if (!newUserLevel) {
59155
- logger25.error("User level insert returned no rows", { userId: user.id });
59448
+ logger26.error("User level insert returned no rows", { userId: user.id });
59156
59449
  throw new InternalError("Failed to create user level cache record");
59157
59450
  }
59158
59451
  userLevel = newUserLevel;
@@ -59167,7 +59460,7 @@ class LevelService {
59167
59460
  userLevel = updatedUserLevel;
59168
59461
  }
59169
59462
  }
59170
- logger25.debug("Retrieved user level", {
59463
+ logger26.debug("Retrieved user level", {
59171
59464
  userId: user.id,
59172
59465
  totalXP,
59173
59466
  currentLevel,
@@ -59178,7 +59471,7 @@ class LevelService {
59178
59471
  async getProgress(user) {
59179
59472
  const userLevel = await this.getByUser(user);
59180
59473
  const xpToNextLevel = await this.calculateXPToNextLevel(userLevel.currentLevel, userLevel.currentXp);
59181
- logger25.debug("Retrieved progress", { userId: user.id });
59474
+ logger26.debug("Retrieved progress", { userId: user.id });
59182
59475
  return {
59183
59476
  level: userLevel.currentLevel,
59184
59477
  currentXp: userLevel.currentXp,
@@ -59212,7 +59505,7 @@ class LevelService {
59212
59505
  if (leveledUp && previousUserLevel) {
59213
59506
  await this.awardLevelUpCredits(userId, previousUserLevel.currentLevel, currentLevel);
59214
59507
  }
59215
- logger25.info("Synced from Timeback", {
59508
+ logger26.info("Synced from Timeback", {
59216
59509
  userId,
59217
59510
  totalXP,
59218
59511
  currentLevel,
@@ -59253,7 +59546,7 @@ class LevelService {
59253
59546
  }
59254
59547
  if (totalCredits > 0) {
59255
59548
  await this.deps.addCredits(userId, totalCredits);
59256
- logger25.info("Awarded level-up credits", {
59549
+ logger26.info("Awarded level-up credits", {
59257
59550
  userId,
59258
59551
  fromLevel,
59259
59552
  toLevel,
@@ -59291,7 +59584,7 @@ class LevelService {
59291
59584
  };
59292
59585
  }
59293
59586
  }
59294
- var logger25;
59587
+ var logger26;
59295
59588
  var levelConfigCache = null;
59296
59589
  var init_level_service = __esm(() => {
59297
59590
  init_drizzle_orm();
@@ -59299,7 +59592,7 @@ var init_level_service = __esm(() => {
59299
59592
  init_tables_index();
59300
59593
  init_src2();
59301
59594
  init_errors();
59302
- logger25 = log.scope("LevelService");
59595
+ logger26 = log.scope("LevelService");
59303
59596
  });
59304
59597
  var init_events = () => {};
59305
59598
  function convertWebSocketUrlToHttp(wsUrl) {
@@ -59316,13 +59609,13 @@ async function publishToUser(baseUrl, secret, userId, type, payload) {
59316
59609
  });
59317
59610
  if (!res.ok) {
59318
59611
  const text3 = await res.text().catch(() => "");
59319
- logger26.warn("Failed to publish to user", {
59612
+ logger27.warn("Failed to publish to user", {
59320
59613
  status: res.status,
59321
59614
  body: text3
59322
59615
  });
59323
59616
  }
59324
59617
  } catch (error) {
59325
- logger26.error("Publish to user error", { error });
59618
+ logger27.error("Publish to user error", { error });
59326
59619
  }
59327
59620
  }
59328
59621
 
@@ -59344,7 +59637,7 @@ class NotificationService {
59344
59637
  conditions2.push(eq(notifications.type, type));
59345
59638
  }
59346
59639
  const results = await this.deps.db.select().from(notifications).where(and(...conditions2)).orderBy(desc(notifications.createdAt)).limit(limit).offset(offset);
59347
- logger26.debug("Listed notifications", { userId: user.id, count: results.length });
59640
+ logger27.debug("Listed notifications", { userId: user.id, count: results.length });
59348
59641
  return results;
59349
59642
  }
59350
59643
  async updateStatus(notificationId, status, method) {
@@ -59363,7 +59656,7 @@ class NotificationService {
59363
59656
  if (!updated) {
59364
59657
  throw new NotFoundError("Notification", notificationId);
59365
59658
  }
59366
- logger26.debug("Updated status", { notificationId, status });
59659
+ logger27.debug("Updated status", { notificationId, status });
59367
59660
  return updated;
59368
59661
  }
59369
59662
  async getStats(user, options) {
@@ -59389,7 +59682,7 @@ class NotificationService {
59389
59682
  const clicked = statsMap.clicked || 0;
59390
59683
  const dismissed = statsMap.dismissed || 0;
59391
59684
  const expired = statsMap.expired || 0;
59392
- logger26.debug("Retrieved stats", { userId: user.id, total });
59685
+ logger27.debug("Retrieved stats", { userId: user.id, total });
59393
59686
  return {
59394
59687
  total,
59395
59688
  delivered,
@@ -59438,7 +59731,7 @@ class NotificationService {
59438
59731
  options: { data, clickUrl, metadata: metadata2 }
59439
59732
  });
59440
59733
  }
59441
- logger26.debug("Created notification", {
59734
+ logger27.debug("Created notification", {
59442
59735
  userId,
59443
59736
  type,
59444
59737
  id: notificationId,
@@ -59446,7 +59739,7 @@ class NotificationService {
59446
59739
  });
59447
59740
  return notificationId;
59448
59741
  } catch (error) {
59449
- logger26.error("Failed to create notification", { userId, type, error });
59742
+ logger27.error("Failed to create notification", { userId, type, error });
59450
59743
  return null;
59451
59744
  }
59452
59745
  }
@@ -59460,7 +59753,7 @@ class NotificationService {
59460
59753
  }) {
59461
59754
  const realtimeConfig = this.deps.realtime;
59462
59755
  if (!realtimeConfig) {
59463
- logger26.warn("No realtime config for publish");
59756
+ logger27.warn("No realtime config for publish");
59464
59757
  return;
59465
59758
  }
59466
59759
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -59502,13 +59795,13 @@ class NotificationService {
59502
59795
  metadata: data.metadata || {}
59503
59796
  }).returning();
59504
59797
  if (!notification) {
59505
- logger26.error("Notification insert returned no rows", {
59798
+ logger27.error("Notification insert returned no rows", {
59506
59799
  userId: data.userId,
59507
59800
  type: data.type
59508
59801
  });
59509
59802
  throw new InternalError("Failed to create notification");
59510
59803
  }
59511
- logger26.info("Inserted notification", {
59804
+ logger27.info("Inserted notification", {
59512
59805
  notificationId: notification.id,
59513
59806
  userId: notification.userId,
59514
59807
  type: notification.type
@@ -59518,7 +59811,7 @@ class NotificationService {
59518
59811
  async deliverPending(userId) {
59519
59812
  const realtimeConfig = this.deps.realtime;
59520
59813
  if (!realtimeConfig) {
59521
- logger26.warn("No realtime config for delivery");
59814
+ logger27.warn("No realtime config for delivery");
59522
59815
  return;
59523
59816
  }
59524
59817
  const { relayUrl, publishSecret } = realtimeConfig;
@@ -59545,13 +59838,13 @@ class NotificationService {
59545
59838
  metadata: notification.metadata,
59546
59839
  clickUrl: notification.clickUrl
59547
59840
  });
59548
- logger26.info("Delivered notification", {
59841
+ logger27.info("Delivered notification", {
59549
59842
  notificationId: notification.id,
59550
59843
  userId,
59551
59844
  type: notification.type
59552
59845
  });
59553
59846
  } catch (error) {
59554
- logger26.warn("Failed to deliver", {
59847
+ logger27.warn("Failed to deliver", {
59555
59848
  notificationId: notification.id,
59556
59849
  error
59557
59850
  });
@@ -59559,7 +59852,7 @@ class NotificationService {
59559
59852
  }
59560
59853
  }
59561
59854
  }
59562
- var logger26;
59855
+ var logger27;
59563
59856
  var init_notification_service = __esm(() => {
59564
59857
  init_drizzle_orm();
59565
59858
  init_src();
@@ -59568,7 +59861,7 @@ var init_notification_service = __esm(() => {
59568
59861
  init_events();
59569
59862
  init_notification();
59570
59863
  init_errors();
59571
- logger26 = log.scope("NotificationService");
59864
+ logger27 = log.scope("NotificationService");
59572
59865
  });
59573
59866
  function createPlayerServices(deps) {
59574
59867
  const { db: db2, realtime } = deps;
@@ -59687,7 +59980,7 @@ class CharacterService {
59687
59980
  createdAt: characterComponents.createdAt,
59688
59981
  updatedAt: characterComponents.updatedAt
59689
59982
  }).from(characterComponents).innerJoin(spriteSheets, eq(characterComponents.spriteSheetId, spriteSheets.id)).where(lte(characterComponents.unlockLevel, level)).orderBy(characterComponents.componentType, characterComponents.variant);
59690
- logger27.debug("Listed available components", {
59983
+ logger28.debug("Listed available components", {
59691
59984
  level,
59692
59985
  count: components.length
59693
59986
  });
@@ -59705,7 +59998,7 @@ class CharacterService {
59705
59998
  }
59706
59999
  }
59707
60000
  });
59708
- logger27.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
60001
+ logger28.debug("Retrieved character", { userId: user.id, found: Boolean(pc3) });
59709
60002
  return pc3 ?? null;
59710
60003
  }
59711
60004
  async getByUserId(userId) {
@@ -59720,7 +60013,7 @@ class CharacterService {
59720
60013
  }
59721
60014
  }
59722
60015
  });
59723
- logger27.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
60016
+ logger28.debug("Retrieved character by ID", { userId, found: Boolean(pc3) });
59724
60017
  return pc3 ?? null;
59725
60018
  }
59726
60019
  async create(input, user) {
@@ -59735,13 +60028,13 @@ class CharacterService {
59735
60028
  }
59736
60029
  const [characterRow] = await tx.insert(playerCharacters).values({ ...input, userId: user.id }).returning();
59737
60030
  if (!characterRow) {
59738
- logger27.error("Character insert returned no rows", { userId: user.id });
60031
+ logger28.error("Character insert returned no rows", { userId: user.id });
59739
60032
  throw new InternalError("Failed to create character in database");
59740
60033
  }
59741
60034
  await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
59742
60035
  return characterRow;
59743
60036
  });
59744
- logger27.info("Created character", { userId: user.id, characterId: result.id });
60037
+ logger28.info("Created character", { userId: user.id, characterId: result.id });
59745
60038
  return result;
59746
60039
  }
59747
60040
  async update(input, user) {
@@ -59753,7 +60046,7 @@ class CharacterService {
59753
60046
  if (!row) {
59754
60047
  throw new NotFoundError("Player character");
59755
60048
  }
59756
- logger27.info("Updated character", {
60049
+ logger28.info("Updated character", {
59757
60050
  userId: user.id,
59758
60051
  characterId: row.id,
59759
60052
  updatedFields: Object.keys(input)
@@ -59775,7 +60068,7 @@ class CharacterService {
59775
60068
  const availableComponents = await db2.select().from(characterComponents).where(lte(characterComponents.unlockLevel, playerLevel));
59776
60069
  const validation = validateAccessorySlot(accessoryComponentId, slot, playerLevel, availableComponents);
59777
60070
  if (!validation.isValid) {
59778
- logger27.warn("Accessory validation failed", {
60071
+ logger28.warn("Accessory validation failed", {
59779
60072
  userId: user.id,
59780
60073
  slot,
59781
60074
  accessoryComponentId,
@@ -59791,14 +60084,14 @@ class CharacterService {
59791
60084
  slot
59792
60085
  }).returning();
59793
60086
  if (!result) {
59794
- logger27.error("Accessory insert returned no rows", {
60087
+ logger28.error("Accessory insert returned no rows", {
59795
60088
  userId: user.id,
59796
60089
  slot,
59797
60090
  accessoryComponentId
59798
60091
  });
59799
60092
  throw new InternalError("Failed to equip accessory");
59800
60093
  }
59801
- logger27.info("Equipped accessory", {
60094
+ logger28.info("Equipped accessory", {
59802
60095
  userId: user.id,
59803
60096
  slot,
59804
60097
  accessoryComponentId
@@ -59819,7 +60112,7 @@ class CharacterService {
59819
60112
  const playerLevel = userLevel?.currentLevel ?? 1;
59820
60113
  const validation = validateAccessoryRemoval(slot, playerLevel);
59821
60114
  if (!validation.isValid) {
59822
- logger27.warn("Accessory removal validation failed", {
60115
+ logger28.warn("Accessory removal validation failed", {
59823
60116
  userId: user.id,
59824
60117
  slot,
59825
60118
  playerLevel,
@@ -59828,17 +60121,17 @@ class CharacterService {
59828
60121
  throw new ValidationError(validation.error ?? "Invalid accessory removal");
59829
60122
  }
59830
60123
  await db2.delete(playerCharacterAccessories).where(and(eq(playerCharacterAccessories.playerCharacterId, playerCharacter.id), eq(playerCharacterAccessories.slot, slot)));
59831
- logger27.info("Removed accessory", { userId: user.id, slot });
60124
+ logger28.info("Removed accessory", { userId: user.id, slot });
59832
60125
  }
59833
60126
  }
59834
- var logger27;
60127
+ var logger28;
59835
60128
  var init_character_service = __esm(() => {
59836
60129
  init_drizzle_orm();
59837
60130
  init_tables_index();
59838
60131
  init_src2();
59839
60132
  init_errors();
59840
60133
  init_accessory_util();
59841
- logger27 = log.scope("CharacterService");
60134
+ logger28 = log.scope("CharacterService");
59842
60135
  });
59843
60136
 
59844
60137
  class CurrencyService {
@@ -59849,7 +60142,7 @@ class CurrencyService {
59849
60142
  async list() {
59850
60143
  const db2 = this.deps.db;
59851
60144
  const allCurrencies = await db2.query.currencies.findMany();
59852
- logger28.debug("Listed currencies", { count: allCurrencies.length });
60145
+ logger29.debug("Listed currencies", { count: allCurrencies.length });
59853
60146
  return allCurrencies;
59854
60147
  }
59855
60148
  async getById(currencyId) {
@@ -59860,7 +60153,7 @@ class CurrencyService {
59860
60153
  if (!currency) {
59861
60154
  throw new NotFoundError("Currency", currencyId);
59862
60155
  }
59863
- logger28.debug("Retrieved currency", { currencyId });
60156
+ logger29.debug("Retrieved currency", { currencyId });
59864
60157
  return currency;
59865
60158
  }
59866
60159
  async create(data) {
@@ -59868,13 +60161,13 @@ class CurrencyService {
59868
60161
  try {
59869
60162
  const [newCurrency] = await db2.insert(currencies).values(data).returning();
59870
60163
  if (!newCurrency) {
59871
- logger28.error("Currency insert returned no rows", {
60164
+ logger29.error("Currency insert returned no rows", {
59872
60165
  itemId: data.itemId,
59873
60166
  symbol: data.symbol
59874
60167
  });
59875
60168
  throw new InternalError("Failed to create currency");
59876
60169
  }
59877
- logger28.info("Created currency", {
60170
+ logger29.info("Created currency", {
59878
60171
  currencyId: newCurrency.id,
59879
60172
  itemId: newCurrency.itemId,
59880
60173
  symbol: newCurrency.symbol,
@@ -59903,7 +60196,7 @@ class CurrencyService {
59903
60196
  if (!updatedCurrency) {
59904
60197
  throw new NotFoundError("Currency", currencyId);
59905
60198
  }
59906
- logger28.info("Updated currency", {
60199
+ logger29.info("Updated currency", {
59907
60200
  currencyId: updatedCurrency.id,
59908
60201
  updatedFields: Object.keys(data)
59909
60202
  });
@@ -59929,16 +60222,16 @@ class CurrencyService {
59929
60222
  if (result.length === 0) {
59930
60223
  throw new NotFoundError("Currency", currencyId);
59931
60224
  }
59932
- logger28.info("Deleted currency", { currencyId });
60225
+ logger29.info("Deleted currency", { currencyId });
59933
60226
  }
59934
60227
  }
59935
- var logger28;
60228
+ var logger29;
59936
60229
  var init_currency_service = __esm(() => {
59937
60230
  init_drizzle_orm();
59938
60231
  init_tables_index();
59939
60232
  init_src2();
59940
60233
  init_errors();
59941
- logger28 = log.scope("CurrencyService");
60234
+ logger29 = log.scope("CurrencyService");
59942
60235
  });
59943
60236
 
59944
60237
  class LogsService {
@@ -59956,11 +60249,11 @@ class LogsService {
59956
60249
  if (!game) {
59957
60250
  throw new NotFoundError("Game", slug2);
59958
60251
  }
59959
- logger29.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
60252
+ logger30.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
59960
60253
  } else {
59961
60254
  const isApprovedDev = user.developerStatus === "approved";
59962
60255
  if (!isApprovedDev) {
59963
- logger29.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
60256
+ logger30.warn("Unapproved developer attempted log access", { userId: user.id, slug: slug2 });
59964
60257
  throw new AccessDeniedError("Must be an approved developer");
59965
60258
  }
59966
60259
  const game = await db2.query.games.findFirst({
@@ -59975,7 +60268,7 @@ class LogsService {
59975
60268
  columns: { id: true }
59976
60269
  });
59977
60270
  if (!membership) {
59978
- logger29.warn("Developer attempted access to unowned game logs", {
60271
+ logger30.warn("Developer attempted access to unowned game logs", {
59979
60272
  userId: user.id,
59980
60273
  slug: slug2
59981
60274
  });
@@ -59984,7 +60277,7 @@ class LogsService {
59984
60277
  }
59985
60278
  const workerId = getDeploymentId(slug2, sstStage);
59986
60279
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
59987
- logger29.debug("Generated log stream token", {
60280
+ logger30.debug("Generated log stream token", {
59988
60281
  userId: user.id,
59989
60282
  slug: slug2,
59990
60283
  workerId
@@ -59992,14 +60285,14 @@ class LogsService {
59992
60285
  return { token, workerId };
59993
60286
  }
59994
60287
  }
59995
- var logger29;
60288
+ var logger30;
59996
60289
  var init_logs_service = __esm(() => {
59997
60290
  init_drizzle_orm();
59998
60291
  init_tables_index();
59999
60292
  init_src2();
60000
60293
  init_errors();
60001
60294
  init_deployment_util();
60002
- logger29 = log.scope("LogsService");
60295
+ logger30 = log.scope("LogsService");
60003
60296
  });
60004
60297
 
60005
60298
  class LtiService {
@@ -60050,7 +60343,7 @@ class MapService {
60050
60343
  if (!mapDetails) {
60051
60344
  throw new NotFoundError("Map", identifier);
60052
60345
  }
60053
- logger30.debug("Retrieved map", { identifier });
60346
+ logger31.debug("Retrieved map", { identifier });
60054
60347
  return mapDetails;
60055
60348
  }
60056
60349
  async getElements(mapId) {
@@ -60066,7 +60359,7 @@ class MapService {
60066
60359
  }
60067
60360
  }
60068
60361
  });
60069
- logger30.debug("Retrieved elements", { mapId, count: elements.length });
60362
+ logger31.debug("Retrieved elements", { mapId, count: elements.length });
60070
60363
  return elements;
60071
60364
  }
60072
60365
  async getObjects(mapId, userId) {
@@ -60087,7 +60380,7 @@ class MapService {
60087
60380
  }
60088
60381
  }
60089
60382
  });
60090
- logger30.debug("Retrieved objects", { mapId, userId, count: objects.length });
60383
+ logger31.debug("Retrieved objects", { mapId, userId, count: objects.length });
60091
60384
  return objects.map((object) => this.formatMapObjectWithItem(object));
60092
60385
  }
60093
60386
  async createObject(mapId, data, user) {
@@ -60114,7 +60407,7 @@ class MapService {
60114
60407
  throw new NotFoundError("Item", data.itemId);
60115
60408
  }
60116
60409
  if (!item.isPlaceable) {
60117
- logger30.warn("Attempted to place non-placeable item", {
60410
+ logger31.warn("Attempted to place non-placeable item", {
60118
60411
  userId: user.id,
60119
60412
  itemId: data.itemId,
60120
60413
  mapId
@@ -60128,7 +60421,7 @@ class MapService {
60128
60421
  };
60129
60422
  const [createdObject] = await db2.insert(mapObjects).values(objectData).returning();
60130
60423
  if (!createdObject) {
60131
- logger30.error("Map object insert returned no rows", {
60424
+ logger31.error("Map object insert returned no rows", {
60132
60425
  userId: user.id,
60133
60426
  mapId,
60134
60427
  itemId: data.itemId
@@ -60152,12 +60445,12 @@ class MapService {
60152
60445
  }
60153
60446
  });
60154
60447
  if (!objectWithItem) {
60155
- logger30.error("Map object query after insert returned no rows", {
60448
+ logger31.error("Map object query after insert returned no rows", {
60156
60449
  objectId: createdObject.id
60157
60450
  });
60158
60451
  throw new InternalError("Failed to retrieve created object");
60159
60452
  }
60160
- logger30.info("Created object", {
60453
+ logger31.info("Created object", {
60161
60454
  userId: user.id,
60162
60455
  mapId,
60163
60456
  objectId: createdObject.id,
@@ -60181,7 +60474,7 @@ class MapService {
60181
60474
  if (result.length === 0) {
60182
60475
  throw new NotFoundError("MapObject", objectId);
60183
60476
  }
60184
- logger30.info("Deleted object", {
60477
+ logger31.info("Deleted object", {
60185
60478
  userId: user.id,
60186
60479
  mapId,
60187
60480
  objectId
@@ -60210,13 +60503,13 @@ class MapService {
60210
60503
  };
60211
60504
  }
60212
60505
  }
60213
- var logger30;
60506
+ var logger31;
60214
60507
  var init_map_service = __esm(() => {
60215
60508
  init_drizzle_orm();
60216
60509
  init_tables_index();
60217
60510
  init_src2();
60218
60511
  init_errors();
60219
- logger30 = log.scope("MapService");
60512
+ logger31 = log.scope("MapService");
60220
60513
  });
60221
60514
 
60222
60515
  class RealtimeService {
@@ -60250,20 +60543,20 @@ class RealtimeService {
60250
60543
  }
60251
60544
  const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
60252
60545
  const token = await this.deps.mintRealtimeToken(user.id, resolvedGameId, displayName, user.role);
60253
- logger31.info("Generated token", {
60546
+ logger32.info("Generated token", {
60254
60547
  userId: user.id,
60255
60548
  gameId: resolvedGameId || "global"
60256
60549
  });
60257
60550
  return { token };
60258
60551
  }
60259
60552
  }
60260
- var logger31;
60553
+ var logger32;
60261
60554
  var init_realtime_service = __esm(() => {
60262
60555
  init_drizzle_orm();
60263
60556
  init_tables_index();
60264
60557
  init_src2();
60265
60558
  init_errors();
60266
- logger31 = log.scope("RealtimeService");
60559
+ logger32 = log.scope("RealtimeService");
60267
60560
  });
60268
60561
 
60269
60562
  class SessionService {
@@ -60297,10 +60590,10 @@ class SessionService {
60297
60590
  };
60298
60591
  const [newSession] = await db2.insert(gameSessions).values(sessionToInsert).returning({ sessionId: gameSessions.id });
60299
60592
  if (!newSession?.sessionId) {
60300
- logger32.error("Game session insert returned no rows", { userId, gameId });
60593
+ logger33.error("Game session insert returned no rows", { userId, gameId });
60301
60594
  throw new InternalError("Failed to create game session");
60302
60595
  }
60303
- logger32.info("Started new session", {
60596
+ logger33.info("Started new session", {
60304
60597
  sessionId: newSession.sessionId,
60305
60598
  gameId,
60306
60599
  userId
@@ -60321,23 +60614,23 @@ class SessionService {
60321
60614
  return { success: true, message: "Session already ended" };
60322
60615
  }
60323
60616
  await db2.update(gameSessions).set({ endedAt: new Date }).where(eq(gameSessions.id, sessionId));
60324
- logger32.info("Ended session", { sessionId, gameId, userId });
60617
+ logger33.info("Ended session", { sessionId, gameId, userId });
60325
60618
  return { success: true };
60326
60619
  }
60327
60620
  async mintToken(gameIdOrSlug, userId) {
60328
60621
  const gameId = await this.resolveGameId(gameIdOrSlug);
60329
60622
  const result = await this.deps.mintGameToken(gameId, userId);
60330
- logger32.debug("Minted game token", { gameId, userId });
60623
+ logger33.debug("Minted game token", { gameId, userId });
60331
60624
  return result;
60332
60625
  }
60333
60626
  }
60334
- var logger32;
60627
+ var logger33;
60335
60628
  var init_session_service = __esm(() => {
60336
60629
  init_drizzle_orm();
60337
60630
  init_tables_index();
60338
60631
  init_src2();
60339
60632
  init_errors();
60340
- logger32 = log.scope("SessionService");
60633
+ logger33 = log.scope("SessionService");
60341
60634
  });
60342
60635
 
60343
60636
  class ShopService {
@@ -60382,7 +60675,7 @@ class ShopService {
60382
60675
  const shopItems = [];
60383
60676
  for (const listing of listingsWithRelations) {
60384
60677
  if (!listing.item || !listing.currency) {
60385
- logger33.warn("Listing missing item or currency, skipping", {
60678
+ logger34.warn("Listing missing item or currency, skipping", {
60386
60679
  listingId: listing.id
60387
60680
  });
60388
60681
  } else {
@@ -60399,7 +60692,7 @@ class ShopService {
60399
60692
  });
60400
60693
  }
60401
60694
  }
60402
- logger33.debug("Retrieved shop view", {
60695
+ logger34.debug("Retrieved shop view", {
60403
60696
  userId: user.id,
60404
60697
  itemCount: shopItems.length,
60405
60698
  currencyCount: shopCurrencies.length
@@ -60410,12 +60703,12 @@ class ShopService {
60410
60703
  };
60411
60704
  }
60412
60705
  }
60413
- var logger33;
60706
+ var logger34;
60414
60707
  var init_shop_service = __esm(() => {
60415
60708
  init_drizzle_orm();
60416
60709
  init_tables_index();
60417
60710
  init_src2();
60418
- logger33 = log.scope("ShopService");
60711
+ logger34 = log.scope("ShopService");
60419
60712
  });
60420
60713
 
60421
60714
  class SpriteService {
@@ -60431,17 +60724,17 @@ class SpriteService {
60431
60724
  if (!template) {
60432
60725
  throw new NotFoundError("SpriteTemplate", slug2);
60433
60726
  }
60434
- logger34.debug("Retrieved sprite", { slug: slug2 });
60727
+ logger35.debug("Retrieved sprite", { slug: slug2 });
60435
60728
  return template;
60436
60729
  }
60437
60730
  }
60438
- var logger34;
60731
+ var logger35;
60439
60732
  var init_sprite_service = __esm(() => {
60440
60733
  init_drizzle_orm();
60441
60734
  init_tables_index();
60442
60735
  init_src2();
60443
60736
  init_errors();
60444
- logger34 = log.scope("SpriteService");
60737
+ logger35 = log.scope("SpriteService");
60445
60738
  });
60446
60739
 
60447
60740
  class UserService {
@@ -60455,12 +60748,12 @@ class UserService {
60455
60748
  where: eq(users.id, user.id)
60456
60749
  });
60457
60750
  if (!userData) {
60458
- logger35.error("User not found", { userId: user.id });
60751
+ logger36.error("User not found", { userId: user.id });
60459
60752
  throw new NotFoundError("User", user.id);
60460
60753
  }
60461
60754
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
60462
60755
  if (gameId) {
60463
- logger35.debug("Fetched user profile (game context)", { userId: user.id, gameId });
60756
+ logger36.debug("Fetched user profile (game context)", { userId: user.id, gameId });
60464
60757
  return {
60465
60758
  id: userData.id,
60466
60759
  name: userData.name,
@@ -60473,7 +60766,7 @@ class UserService {
60473
60766
  const timebackAccount = await db2.query.accounts.findFirst({
60474
60767
  where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
60475
60768
  });
60476
- logger35.debug("Fetched user profile (platform context)", { userId: user.id });
60769
+ logger36.debug("Fetched user profile (platform context)", { userId: user.id });
60477
60770
  return {
60478
60771
  id: userData.id,
60479
60772
  name: userData.name,
@@ -60496,7 +60789,7 @@ class UserService {
60496
60789
  columns: { name: true }
60497
60790
  });
60498
60791
  if (!userData) {
60499
- logger35.error("Demo user not found", { userId });
60792
+ logger36.error("Demo user not found", { userId });
60500
60793
  throw new NotFoundError("User", userId);
60501
60794
  }
60502
60795
  return {
@@ -60510,10 +60803,10 @@ class UserService {
60510
60803
  updatedAt: new Date
60511
60804
  }).where(eq(users.id, userId)).returning({ name: users.name });
60512
60805
  if (!updatedUser) {
60513
- logger35.error("Demo user not found for profile update", { userId });
60806
+ logger36.error("Demo user not found for profile update", { userId });
60514
60807
  throw new NotFoundError("User", userId);
60515
60808
  }
60516
- logger35.debug("Updated demo profile", { userId, displayName });
60809
+ logger36.debug("Updated demo profile", { userId, displayName });
60517
60810
  return {
60518
60811
  displayName: updatedUser.name,
60519
60812
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
@@ -60526,7 +60819,7 @@ class UserService {
60526
60819
  ]);
60527
60820
  const enrollments = gameId ? this.filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
60528
60821
  const organizations = gameId ? this.filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
60529
- logger35.debug("Fetched Timeback data", {
60822
+ logger36.debug("Fetched Timeback data", {
60530
60823
  timebackId,
60531
60824
  role,
60532
60825
  enrollmentCount: enrollments.length,
@@ -60535,9 +60828,9 @@ class UserService {
60535
60828
  return { id: timebackId, role, enrollments, organizations };
60536
60829
  }
60537
60830
  async fetchStudentProfile(timebackId) {
60538
- logger35.debug("Fetching student profile", { timebackId });
60831
+ logger36.debug("Fetching student profile", { timebackId });
60539
60832
  if (!this.deps.timeback) {
60540
- logger35.warn("Timeback client not available");
60833
+ logger36.warn("Timeback client not available");
60541
60834
  return { role: "student", organizations: [] };
60542
60835
  }
60543
60836
  try {
@@ -60565,14 +60858,14 @@ class UserService {
60565
60858
  }
60566
60859
  return { role, organizations: [...orgMap.values()] };
60567
60860
  } catch (error) {
60568
- logger35.warn("Failed to fetch student profile", { error, timebackId });
60861
+ logger36.warn("Failed to fetch student profile", { error, timebackId });
60569
60862
  return { role: "student", organizations: [] };
60570
60863
  }
60571
60864
  }
60572
60865
  async fetchEnrollments(timebackId) {
60573
- logger35.debug("Fetching enrollments", { timebackId });
60866
+ logger36.debug("Fetching enrollments", { timebackId });
60574
60867
  if (!this.deps.timeback) {
60575
- logger35.warn("Timeback client not available");
60868
+ logger36.warn("Timeback client not available");
60576
60869
  return [];
60577
60870
  }
60578
60871
  try {
@@ -60593,7 +60886,7 @@ class UserService {
60593
60886
  orgId: courseToSchool.get(i2.courseId)
60594
60887
  }));
60595
60888
  } catch (error) {
60596
- logger35.warn("Failed to fetch enrollments", { error, timebackId });
60889
+ logger36.warn("Failed to fetch enrollments", { error, timebackId });
60597
60890
  return [];
60598
60891
  }
60599
60892
  }
@@ -60608,14 +60901,14 @@ class UserService {
60608
60901
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
60609
60902
  }
60610
60903
  }
60611
- var logger35;
60904
+ var logger36;
60612
60905
  var init_user_service = __esm(() => {
60613
60906
  init_drizzle_orm();
60614
60907
  init_src();
60615
60908
  init_tables_index();
60616
60909
  init_src2();
60617
60910
  init_errors();
60618
- logger35 = log.scope("UserService");
60911
+ logger36 = log.scope("UserService");
60619
60912
  });
60620
60913
 
60621
60914
  class VerifyService {
@@ -60624,16 +60917,16 @@ class VerifyService {
60624
60917
  this.deps = deps;
60625
60918
  }
60626
60919
  async verifyGameToken(token) {
60627
- logger36.debug("Verifying game token");
60920
+ logger37.debug("Verifying game token");
60628
60921
  const payload = await this.deps.validateGameToken(token);
60629
60922
  if (!payload) {
60630
- logger36.warn("Invalid or expired game token presented");
60923
+ logger37.warn("Invalid or expired game token presented");
60631
60924
  throw new ValidationError("Invalid or expired token");
60632
60925
  }
60633
60926
  const gameId = payload.sub;
60634
60927
  const userId = payload.uid;
60635
60928
  if (typeof gameId !== "string" || typeof userId !== "string") {
60636
- logger36.warn("Game token missing required claims", {
60929
+ logger37.warn("Game token missing required claims", {
60637
60930
  hasGameId: typeof gameId === "string",
60638
60931
  hasUserId: typeof userId === "string"
60639
60932
  });
@@ -60644,7 +60937,7 @@ class VerifyService {
60644
60937
  where: eq(users.id, userId)
60645
60938
  });
60646
60939
  if (!userData) {
60647
- logger36.error("User not found for valid token", {
60940
+ logger37.error("User not found for valid token", {
60648
60941
  userId
60649
60942
  });
60650
60943
  throw new NotFoundError("User", userId);
@@ -60658,7 +60951,7 @@ class VerifyService {
60658
60951
  family_name: undefined,
60659
60952
  timeback_id: userData.timebackId || undefined
60660
60953
  };
60661
- logger36.info("Token verified", { gameId, userId });
60954
+ logger37.info("Token verified", { gameId, userId });
60662
60955
  return {
60663
60956
  claims: payload,
60664
60957
  gameId,
@@ -60666,13 +60959,13 @@ class VerifyService {
60666
60959
  };
60667
60960
  }
60668
60961
  }
60669
- var logger36;
60962
+ var logger37;
60670
60963
  var init_verify_service = __esm(() => {
60671
60964
  init_drizzle_orm();
60672
60965
  init_tables_index();
60673
60966
  init_src2();
60674
60967
  init_errors();
60675
- logger36 = log.scope("VerifyService");
60968
+ logger37 = log.scope("VerifyService");
60676
60969
  });
60677
60970
  function createStandaloneServices(deps) {
60678
60971
  const { db: db2, auth: auth2, timeback: timeback2 } = deps;
@@ -65804,7 +66097,7 @@ var colorStatus = async (status) => {
65804
66097
  }
65805
66098
  return `${status}`;
65806
66099
  };
65807
- var logger37 = (fn = console.log) => {
66100
+ var logger38 = (fn = console.log) => {
65808
66101
  return async function logger2(c, next) {
65809
66102
  const { method, url: url2 } = c.req;
65810
66103
  const path2 = url2.slice(url2.indexOf("/", 8));
@@ -65981,7 +66274,7 @@ function createApp(db2, options) {
65981
66274
  const app = new Hono2;
65982
66275
  app.use("*", cors({ origin: "*", credentials: true }));
65983
66276
  if (options.verbose && !options.quiet) {
65984
- app.use("*", logger37());
66277
+ app.use("*", logger38());
65985
66278
  }
65986
66279
  app.use("/api/*", async (c, next) => {
65987
66280
  c.set("db", db2);
@@ -72683,12 +72976,12 @@ var init_session2 = __esm(() => {
72683
72976
  init_utils();
72684
72977
  init_dist5();
72685
72978
  PglitePreparedQuery = class PglitePreparedQuery2 extends PgPreparedQuery {
72686
- constructor(client, queryString, params, logger38, fields, name3, _isResponseInArrayMode, customResultMapper) {
72979
+ constructor(client, queryString, params, logger39, fields, name3, _isResponseInArrayMode, customResultMapper) {
72687
72980
  super({ sql: queryString, params });
72688
72981
  this.client = client;
72689
72982
  this.queryString = queryString;
72690
72983
  this.params = params;
72691
- this.logger = logger38;
72984
+ this.logger = logger39;
72692
72985
  this.fields = fields;
72693
72986
  this._isResponseInArrayMode = _isResponseInArrayMode;
72694
72987
  this.customResultMapper = customResultMapper;
@@ -72790,11 +73083,11 @@ var init_session2 = __esm(() => {
72790
73083
  });
72791
73084
  function construct(client, config2 = {}) {
72792
73085
  const dialect2 = new PgDialect({ casing: config2.casing });
72793
- let logger38;
73086
+ let logger39;
72794
73087
  if (config2.logger === true) {
72795
- logger38 = new DefaultLogger;
73088
+ logger39 = new DefaultLogger;
72796
73089
  } else if (config2.logger !== false) {
72797
- logger38 = config2.logger;
73090
+ logger39 = config2.logger;
72798
73091
  }
72799
73092
  let schema2;
72800
73093
  if (config2.schema) {
@@ -72805,7 +73098,7 @@ function construct(client, config2 = {}) {
72805
73098
  tableNamesMap: tablesConfig.tableNamesMap
72806
73099
  };
72807
73100
  }
72808
- const driver = new PgliteDriver(client, dialect2, { logger: logger38 });
73101
+ const driver = new PgliteDriver(client, dialect2, { logger: logger39 });
72809
73102
  const session2 = driver.createSession(schema2);
72810
73103
  const db2 = new PgliteDatabase(dialect2, session2, schema2);
72811
73104
  db2.$client = client;
@@ -121410,8 +121703,8 @@ var init_currencies = __esm(() => {
121410
121703
  init_tables_index();
121411
121704
  init_constants();
121412
121705
  });
121413
- function setLogger(logger38) {
121414
- customLogger = logger38;
121706
+ function setLogger(logger39) {
121707
+ customLogger = logger39;
121415
121708
  }
121416
121709
  function getLogger() {
121417
121710
  if (customLogger) {
@@ -121424,10 +121717,10 @@ function getLogger() {
121424
121717
  };
121425
121718
  }
121426
121719
  var customLogger;
121427
- var logger38;
121720
+ var logger39;
121428
121721
  var init_adapter = __esm(() => {
121429
121722
  init_config();
121430
- logger38 = {
121723
+ logger39 = {
121431
121724
  info: (msg) => {
121432
121725
  if (customLogger || !config.embedded) {
121433
121726
  getLogger().info(msg);
@@ -121511,7 +121804,7 @@ async function seedCoreGames(db2) {
121511
121804
  role: "owner"
121512
121805
  }).onConflictDoNothing();
121513
121806
  } catch (error2) {
121514
- logger38.error(`Error seeding core game '${gameData.slug}': ${error2}`);
121807
+ logger39.error(`Error seeding core game '${gameData.slug}': ${error2}`);
121515
121808
  }
121516
121809
  }
121517
121810
  }
@@ -121561,7 +121854,7 @@ async function seedCurrentProjectGame(db2, project) {
121561
121854
  }
121562
121855
  return newGame;
121563
121856
  } catch (error2) {
121564
- logger38.error(`❌ Error seeding project game: ${error2}`);
121857
+ logger39.error(`❌ Error seeding project game: ${error2}`);
121565
121858
  throw error2;
121566
121859
  }
121567
121860
  }
@@ -122324,7 +122617,7 @@ async function provisionLtiUser(db2, claims) {
122324
122617
  where: eq(users.id, existingAccount.userId)
122325
122618
  });
122326
122619
  if (user) {
122327
- logger39.info("Found user by LTI account", {
122620
+ logger40.info("Found user by LTI account", {
122328
122621
  userId: user.id,
122329
122622
  ltiTimebackId
122330
122623
  });
@@ -122353,13 +122646,13 @@ async function provisionLtiUser(db2, claims) {
122353
122646
  updatedAt: new Date
122354
122647
  }).returning({ id: accounts.id });
122355
122648
  if (!account) {
122356
- logger39.error("LTI account link insert returned no rows", {
122649
+ logger40.error("LTI account link insert returned no rows", {
122357
122650
  userId: existingUser.id,
122358
122651
  ltiTimebackId
122359
122652
  });
122360
122653
  throw new InternalError("Failed to link LTI account");
122361
122654
  }
122362
- logger39.info("Linked LTI account to existing user", {
122655
+ logger40.info("Linked LTI account to existing user", {
122363
122656
  userId: existingUser.id,
122364
122657
  ltiTimebackId
122365
122658
  });
@@ -122379,7 +122672,7 @@ async function provisionLtiUser(db2, claims) {
122379
122672
  updatedAt: new Date
122380
122673
  }).returning();
122381
122674
  if (!insertedUser) {
122382
- logger39.error("LTI user insert returned no rows", { email, ltiTimebackId });
122675
+ logger40.error("LTI user insert returned no rows", { email, ltiTimebackId });
122383
122676
  throw new InternalError("Failed to create user");
122384
122677
  }
122385
122678
  await tx.insert(accounts).values({
@@ -122394,7 +122687,7 @@ async function provisionLtiUser(db2, claims) {
122394
122687
  createdAt: new Date,
122395
122688
  updatedAt: new Date
122396
122689
  });
122397
- logger39.info("Provisioned new user from LTI", {
122690
+ logger40.info("Provisioned new user from LTI", {
122398
122691
  userId: insertedUser.id,
122399
122692
  ltiTimebackId
122400
122693
  });
@@ -122402,7 +122695,7 @@ async function provisionLtiUser(db2, claims) {
122402
122695
  });
122403
122696
  return createdUser;
122404
122697
  }
122405
- var logger39;
122698
+ var logger40;
122406
122699
  var init_lti_provisioning = __esm(() => {
122407
122700
  init_drizzle_orm();
122408
122701
  init_src();
@@ -122410,7 +122703,7 @@ var init_lti_provisioning = __esm(() => {
122410
122703
  init_src2();
122411
122704
  init_errors();
122412
122705
  init_lti_util();
122413
- logger39 = log.scope("LtiProvisioning");
122706
+ logger40 = log.scope("LtiProvisioning");
122414
122707
  });
122415
122708
  function formatZodError(error2) {
122416
122709
  const flat = error2.flatten();
@@ -122453,7 +122746,7 @@ var init_utils11 = __esm(() => {
122453
122746
  init_timeback_util();
122454
122747
  init_validation_util();
122455
122748
  });
122456
- var logger40;
122749
+ var logger41;
122457
122750
  var listCurrent;
122458
122751
  var listHistory;
122459
122752
  var postProgress;
@@ -122464,14 +122757,14 @@ var init_achievement_controller = __esm(() => {
122464
122757
  init_src2();
122465
122758
  init_errors();
122466
122759
  init_utils11();
122467
- logger40 = log.scope("AchievementController");
122760
+ logger41 = log.scope("AchievementController");
122468
122761
  listCurrent = requireNonAnonymous(async (ctx) => {
122469
- logger40.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
122762
+ logger41.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
122470
122763
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
122471
122764
  });
122472
122765
  listHistory = requireNonAnonymous(async (ctx) => {
122473
122766
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
122474
- logger40.debug("Listing achievement history", { userId: ctx.user.id, limit });
122767
+ logger41.debug("Listing achievement history", { userId: ctx.user.id, limit });
122475
122768
  return ctx.services.achievement.listHistory(ctx.user, limit);
122476
122769
  });
122477
122770
  postProgress = requireNonAnonymous(async (ctx) => {
@@ -122482,12 +122775,12 @@ var init_achievement_controller = __esm(() => {
122482
122775
  } catch (error2) {
122483
122776
  if (error2 instanceof exports_external.ZodError) {
122484
122777
  const details = formatZodError(error2);
122485
- logger40.warn("Submit achievement progress validation failed", { details });
122778
+ logger41.warn("Submit achievement progress validation failed", { details });
122486
122779
  throw ApiError.unprocessableEntity("Invalid request body", details);
122487
122780
  }
122488
122781
  throw ApiError.badRequest("Invalid JSON body");
122489
122782
  }
122490
- logger40.debug("Submitting progress", {
122783
+ logger41.debug("Submitting progress", {
122491
122784
  userId: ctx.user.id,
122492
122785
  achievementId: body2.achievementId
122493
122786
  });
@@ -122499,15 +122792,15 @@ var init_achievement_controller = __esm(() => {
122499
122792
  postProgress
122500
122793
  };
122501
122794
  });
122502
- var logger41;
122795
+ var logger42;
122503
122796
  var getAllowedOrigins;
122504
122797
  var init_admin_controller = __esm(() => {
122505
122798
  init_src2();
122506
122799
  init_utils11();
122507
- logger41 = log.scope("AdminController");
122800
+ logger42 = log.scope("AdminController");
122508
122801
  getAllowedOrigins = requireAdmin(async (ctx) => {
122509
122802
  const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
122510
- logger41.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
122803
+ logger42.debug("Getting allowed origins", { userId: ctx.user.id, refresh: shouldRefresh });
122511
122804
  if (shouldRefresh) {
122512
122805
  await ctx.providers.cache.refreshGameOrigins();
122513
122806
  }
@@ -122520,7 +122813,7 @@ var init_admin_controller = __esm(() => {
122520
122813
  };
122521
122814
  });
122522
122815
  });
122523
- var logger42;
122816
+ var logger43;
122524
122817
  var listFiles;
122525
122818
  var getFile;
122526
122819
  var putFile;
@@ -122532,7 +122825,7 @@ var init_bucket_controller = __esm(() => {
122532
122825
  init_src2();
122533
122826
  init_errors();
122534
122827
  init_utils11();
122535
- logger42 = log.scope("BucketController");
122828
+ logger43 = log.scope("BucketController");
122536
122829
  listFiles = requireDeveloper(async (ctx) => {
122537
122830
  const slug2 = ctx.params.slug;
122538
122831
  if (!slug2) {
@@ -122540,7 +122833,7 @@ var init_bucket_controller = __esm(() => {
122540
122833
  }
122541
122834
  const url2 = ctx.url;
122542
122835
  const prefix2 = url2.searchParams.get("prefix") || undefined;
122543
- logger42.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122836
+ logger43.debug("Listing files", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
122544
122837
  const files = await ctx.services.bucket.listFiles(slug2, ctx.user, prefix2);
122545
122838
  return { files };
122546
122839
  });
@@ -122550,7 +122843,7 @@ var init_bucket_controller = __esm(() => {
122550
122843
  if (!slug2 || !key) {
122551
122844
  throw ApiError.badRequest("Missing game slug or file key");
122552
122845
  }
122553
- logger42.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
122846
+ logger43.debug("Getting file", { userId: ctx.user.id, slug: slug2, key });
122554
122847
  const object = await ctx.services.bucket.getFile(slug2, key, ctx.user);
122555
122848
  return new Response(Buffer.from(object.body), {
122556
122849
  status: 200,
@@ -122569,7 +122862,7 @@ var init_bucket_controller = __esm(() => {
122569
122862
  const arrayBuffer = await ctx.request.arrayBuffer();
122570
122863
  const body2 = new Uint8Array(arrayBuffer);
122571
122864
  const contentType = ctx.request.headers.get("content-type") || undefined;
122572
- logger42.debug("Uploading file", {
122865
+ logger43.debug("Uploading file", {
122573
122866
  userId: ctx.user.id,
122574
122867
  slug: slug2,
122575
122868
  key,
@@ -122585,7 +122878,7 @@ var init_bucket_controller = __esm(() => {
122585
122878
  if (!slug2 || !key) {
122586
122879
  throw ApiError.badRequest("Missing game slug or file key");
122587
122880
  }
122588
- logger42.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
122881
+ logger43.debug("Deleting file", { userId: ctx.user.id, slug: slug2, key });
122589
122882
  await ctx.services.bucket.deleteFile(slug2, key, ctx.user);
122590
122883
  return { success: true, key };
122591
122884
  });
@@ -122597,12 +122890,12 @@ var init_bucket_controller = __esm(() => {
122597
122890
  } catch (error2) {
122598
122891
  if (error2 instanceof exports_external.ZodError) {
122599
122892
  const details = formatZodError(error2);
122600
- logger42.warn("Initiate upload validation failed", { details });
122893
+ logger43.warn("Initiate upload validation failed", { details });
122601
122894
  throw ApiError.unprocessableEntity("Validation failed", details);
122602
122895
  }
122603
122896
  throw ApiError.badRequest("Invalid JSON body");
122604
122897
  }
122605
- logger42.debug("Initiating multipart upload", {
122898
+ logger43.debug("Initiating multipart upload", {
122606
122899
  userId: ctx.user.id,
122607
122900
  gameId: body2.gameId,
122608
122901
  fileName: body2.fileName
@@ -122617,10 +122910,10 @@ async function listComponents(ctx) {
122617
122910
  if (!isNaN(parsed) && isFinite(parsed)) {
122618
122911
  level = Math.floor(Math.max(0, parsed));
122619
122912
  }
122620
- logger43.debug("Listing components", { level });
122913
+ logger44.debug("Listing components", { level });
122621
122914
  return ctx.services.character.listAvailableComponents(level);
122622
122915
  }
122623
- var logger43;
122916
+ var logger44;
122624
122917
  var get;
122625
122918
  var getByUserId;
122626
122919
  var create;
@@ -122634,9 +122927,9 @@ var init_character_controller = __esm(() => {
122634
122927
  init_src2();
122635
122928
  init_errors();
122636
122929
  init_utils11();
122637
- logger43 = log.scope("CharacterController");
122930
+ logger44 = log.scope("CharacterController");
122638
122931
  get = requireNonAnonymous(async (ctx) => {
122639
- logger43.debug("Getting character", { userId: ctx.user.id });
122932
+ logger44.debug("Getting character", { userId: ctx.user.id });
122640
122933
  return ctx.services.character.getByUser(ctx.user);
122641
122934
  });
122642
122935
  getByUserId = requireNonAnonymous(async (ctx) => {
@@ -122644,7 +122937,7 @@ var init_character_controller = __esm(() => {
122644
122937
  if (!userId) {
122645
122938
  throw ApiError.badRequest("User ID is required in the URL path");
122646
122939
  }
122647
- logger43.debug("Getting character by user ID", { requestedUserId: userId });
122940
+ logger44.debug("Getting character by user ID", { requestedUserId: userId });
122648
122941
  return ctx.services.character.getByUserId(userId);
122649
122942
  });
122650
122943
  create = requireNonAnonymous(async (ctx) => {
@@ -122655,12 +122948,12 @@ var init_character_controller = __esm(() => {
122655
122948
  } catch (error2) {
122656
122949
  if (error2 instanceof exports_external.ZodError) {
122657
122950
  const details = formatZodError(error2);
122658
- logger43.warn("Create character validation failed", { details });
122951
+ logger44.warn("Create character validation failed", { details });
122659
122952
  throw ApiError.unprocessableEntity("Invalid request body", details);
122660
122953
  }
122661
122954
  throw ApiError.badRequest("Invalid JSON body");
122662
122955
  }
122663
- logger43.debug("Creating character", {
122956
+ logger44.debug("Creating character", {
122664
122957
  userId: ctx.user.id,
122665
122958
  bodyComponentId: body2.bodyComponentId,
122666
122959
  hairstyleComponentId: body2.hairstyleComponentId
@@ -122675,12 +122968,12 @@ var init_character_controller = __esm(() => {
122675
122968
  } catch (error2) {
122676
122969
  if (error2 instanceof exports_external.ZodError) {
122677
122970
  const details = formatZodError(error2);
122678
- logger43.warn("Update character validation failed", { details });
122971
+ logger44.warn("Update character validation failed", { details });
122679
122972
  throw ApiError.unprocessableEntity("Invalid request body", details);
122680
122973
  }
122681
122974
  throw ApiError.badRequest("Invalid JSON body");
122682
122975
  }
122683
- logger43.debug("Updating character", {
122976
+ logger44.debug("Updating character", {
122684
122977
  userId: ctx.user.id,
122685
122978
  bodyComponentId: body2.bodyComponentId,
122686
122979
  hairstyleComponentId: body2.hairstyleComponentId,
@@ -122696,12 +122989,12 @@ var init_character_controller = __esm(() => {
122696
122989
  } catch (error2) {
122697
122990
  if (error2 instanceof exports_external.ZodError) {
122698
122991
  const details = formatZodError(error2);
122699
- logger43.warn("Equip accessory validation failed", { details });
122992
+ logger44.warn("Equip accessory validation failed", { details });
122700
122993
  throw ApiError.unprocessableEntity("Invalid request body", details);
122701
122994
  }
122702
122995
  throw ApiError.badRequest("Invalid JSON body");
122703
122996
  }
122704
- logger43.debug("Equipping accessory", {
122997
+ logger44.debug("Equipping accessory", {
122705
122998
  userId: ctx.user.id,
122706
122999
  slot: body2.slot,
122707
123000
  accessoryComponentId: body2.accessoryComponentId
@@ -122713,7 +123006,7 @@ var init_character_controller = __esm(() => {
122713
123006
  if (!slot) {
122714
123007
  throw ApiError.badRequest("Slot is required in the URL path");
122715
123008
  }
122716
- logger43.debug("Removing accessory", { userId: ctx.user.id, slot });
123009
+ logger44.debug("Removing accessory", { userId: ctx.user.id, slot });
122717
123010
  await ctx.services.character.removeAccessory(slot, ctx.user);
122718
123011
  return { success: true };
122719
123012
  });
@@ -122727,7 +123020,7 @@ var init_character_controller = __esm(() => {
122727
123020
  removeAccessory
122728
123021
  };
122729
123022
  });
122730
- var logger44;
123023
+ var logger45;
122731
123024
  var list;
122732
123025
  var getById;
122733
123026
  var create2;
@@ -122741,9 +123034,9 @@ var init_currency_controller = __esm(() => {
122741
123034
  init_src4();
122742
123035
  init_errors();
122743
123036
  init_utils11();
122744
- logger44 = log.scope("CurrencyController");
123037
+ logger45 = log.scope("CurrencyController");
122745
123038
  list = requireNonAnonymous(async (ctx) => {
122746
- logger44.debug("Listing currencies", { userId: ctx.user.id });
123039
+ logger45.debug("Listing currencies", { userId: ctx.user.id });
122747
123040
  return ctx.services.currency.list();
122748
123041
  });
122749
123042
  getById = requireNonAnonymous(async (ctx) => {
@@ -122754,7 +123047,7 @@ var init_currency_controller = __esm(() => {
122754
123047
  if (!isValidUUID(currencyId)) {
122755
123048
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
122756
123049
  }
122757
- logger44.debug("Getting currency", { userId: ctx.user.id, currencyId });
123050
+ logger45.debug("Getting currency", { userId: ctx.user.id, currencyId });
122758
123051
  return ctx.services.currency.getById(currencyId);
122759
123052
  });
122760
123053
  create2 = requireAdmin(async (ctx) => {
@@ -122765,12 +123058,12 @@ var init_currency_controller = __esm(() => {
122765
123058
  } catch (error2) {
122766
123059
  if (error2 instanceof exports_external.ZodError) {
122767
123060
  const details = formatZodError(error2);
122768
- logger44.warn("Create currency validation failed", { details });
123061
+ logger45.warn("Create currency validation failed", { details });
122769
123062
  throw ApiError.unprocessableEntity("Validation failed", details);
122770
123063
  }
122771
123064
  throw ApiError.badRequest("Invalid JSON body");
122772
123065
  }
122773
- logger44.debug("Creating currency", {
123066
+ logger45.debug("Creating currency", {
122774
123067
  userId: ctx.user.id,
122775
123068
  symbol: body2.symbol,
122776
123069
  itemId: body2.itemId,
@@ -122793,12 +123086,12 @@ var init_currency_controller = __esm(() => {
122793
123086
  } catch (error2) {
122794
123087
  if (error2 instanceof exports_external.ZodError) {
122795
123088
  const details = formatZodError(error2);
122796
- logger44.warn("Update currency validation failed", { details });
123089
+ logger45.warn("Update currency validation failed", { details });
122797
123090
  throw ApiError.unprocessableEntity("Validation failed", details);
122798
123091
  }
122799
123092
  throw ApiError.badRequest("Invalid JSON body");
122800
123093
  }
122801
- logger44.debug("Updating currency", {
123094
+ logger45.debug("Updating currency", {
122802
123095
  userId: ctx.user.id,
122803
123096
  currencyId,
122804
123097
  symbol: body2.symbol,
@@ -122815,7 +123108,7 @@ var init_currency_controller = __esm(() => {
122815
123108
  if (!isValidUUID(currencyId)) {
122816
123109
  throw ApiError.unprocessableEntity("currencyId must be a valid UUID format");
122817
123110
  }
122818
- logger44.debug("Deleting currency", { userId: ctx.user.id, currencyId });
123111
+ logger45.debug("Deleting currency", { userId: ctx.user.id, currencyId });
122819
123112
  await ctx.services.currency.delete(currencyId);
122820
123113
  });
122821
123114
  currencyController = {
@@ -122826,7 +123119,7 @@ var init_currency_controller = __esm(() => {
122826
123119
  remove
122827
123120
  };
122828
123121
  });
122829
- var logger45;
123122
+ var logger46;
122830
123123
  var reset;
122831
123124
  var init_database_controller = __esm(() => {
122832
123125
  init_esm();
@@ -122834,7 +123127,7 @@ var init_database_controller = __esm(() => {
122834
123127
  init_src2();
122835
123128
  init_errors();
122836
123129
  init_utils11();
122837
- logger45 = log.scope("DatabaseController");
123130
+ logger46 = log.scope("DatabaseController");
122838
123131
  reset = requireDeveloper(async (ctx) => {
122839
123132
  const slug2 = ctx.params.slug;
122840
123133
  if (!slug2) {
@@ -122847,11 +123140,11 @@ var init_database_controller = __esm(() => {
122847
123140
  } catch (error2) {
122848
123141
  if (error2 instanceof exports_external.ZodError) {
122849
123142
  const details = formatZodError(error2);
122850
- logger45.warn("Database reset validation failed", { details });
123143
+ logger46.warn("Database reset validation failed", { details });
122851
123144
  throw ApiError.unprocessableEntity("Validation failed", details);
122852
123145
  }
122853
123146
  }
122854
- logger45.debug("Resetting database", {
123147
+ logger46.debug("Resetting database", {
122855
123148
  userId: ctx.user.id,
122856
123149
  slug: slug2,
122857
123150
  hasSchema: Boolean(body2.schema)
@@ -122867,7 +123160,7 @@ async function createJob(ctx) {
122867
123160
  let body2;
122868
123161
  try {
122869
123162
  const json4 = await ctx.request.json();
122870
- logger46.debug("Deploy request body", {
123163
+ logger47.debug("Deploy request body", {
122871
123164
  keys: Object.keys(json4 || {}),
122872
123165
  hasUploadToken: Boolean(json4?.uploadToken),
122873
123166
  hasCode: Boolean(json4?.code),
@@ -122877,7 +123170,7 @@ async function createJob(ctx) {
122877
123170
  } catch (error2) {
122878
123171
  if (error2 instanceof exports_external.ZodError) {
122879
123172
  const details = formatZodError(error2);
122880
- logger46.warn("Deploy validation failed", { details });
123173
+ logger47.warn("Deploy validation failed", { details });
122881
123174
  throw ApiError.unprocessableEntity("Invalid deploy request", details);
122882
123175
  }
122883
123176
  throw ApiError.badRequest("Invalid JSON body");
@@ -122898,7 +123191,7 @@ async function getJob(ctx) {
122898
123191
  }
122899
123192
  return ctx.services.deployJobs.get(jobId, slug2, ctx.user);
122900
123193
  }
122901
- var logger46;
123194
+ var logger47;
122902
123195
  var deploy;
122903
123196
  var init_deploy_controller = __esm(() => {
122904
123197
  init_esm();
@@ -122906,26 +123199,26 @@ var init_deploy_controller = __esm(() => {
122906
123199
  init_src2();
122907
123200
  init_errors();
122908
123201
  init_utils11();
122909
- logger46 = log.scope("DeployController");
123202
+ logger47 = log.scope("DeployController");
122910
123203
  deploy = {
122911
123204
  createJob: requireDeveloper(createJob),
122912
123205
  getJob: requireDeveloper(getJob)
122913
123206
  };
122914
123207
  });
122915
- var logger47;
123208
+ var logger48;
122916
123209
  var apply;
122917
123210
  var getStatus;
122918
123211
  var developer;
122919
123212
  var init_developer_controller = __esm(() => {
122920
123213
  init_src2();
122921
123214
  init_utils11();
122922
- logger47 = log.scope("DeveloperController");
123215
+ logger48 = log.scope("DeveloperController");
122923
123216
  apply = requireNonAnonymous(async (ctx) => {
122924
- logger47.debug("Applying for developer status", { userId: ctx.user.id });
123217
+ logger48.debug("Applying for developer status", { userId: ctx.user.id });
122925
123218
  await ctx.services.developer.apply(ctx.user);
122926
123219
  });
122927
123220
  getStatus = requireNonAnonymous(async (ctx) => {
122928
- logger47.debug("Getting developer status", { userId: ctx.user.id });
123221
+ logger48.debug("Getting developer status", { userId: ctx.user.id });
122929
123222
  const status = await ctx.services.developer.getStatus(ctx.user.id);
122930
123223
  return { status };
122931
123224
  });
@@ -122934,7 +123227,7 @@ var init_developer_controller = __esm(() => {
122934
123227
  getStatus
122935
123228
  };
122936
123229
  });
122937
- var logger48;
123230
+ var logger49;
122938
123231
  var add;
122939
123232
  var list2;
122940
123233
  var getStatus2;
@@ -122947,7 +123240,7 @@ var init_domain_controller = __esm(() => {
122947
123240
  init_config2();
122948
123241
  init_errors();
122949
123242
  init_utils11();
122950
- logger48 = log.scope("DomainController");
123243
+ logger49 = log.scope("DomainController");
122951
123244
  add = requireDeveloper(async (ctx) => {
122952
123245
  const slug2 = ctx.params.slug;
122953
123246
  if (!slug2) {
@@ -122960,13 +123253,13 @@ var init_domain_controller = __esm(() => {
122960
123253
  } catch (error2) {
122961
123254
  if (error2 instanceof exports_external.ZodError) {
122962
123255
  const details = formatZodError(error2);
122963
- logger48.warn("Add domain validation failed", { details });
123256
+ logger49.warn("Add domain validation failed", { details });
122964
123257
  throw ApiError.unprocessableEntity("Validation failed", details);
122965
123258
  }
122966
123259
  throw ApiError.badRequest("Invalid JSON body");
122967
123260
  }
122968
123261
  const environment = getPlatformEnvironment(ctx.config);
122969
- logger48.debug("Adding domain", {
123262
+ logger49.debug("Adding domain", {
122970
123263
  userId: ctx.user.id,
122971
123264
  slug: slug2,
122972
123265
  hostname: body2.hostname,
@@ -122980,7 +123273,7 @@ var init_domain_controller = __esm(() => {
122980
123273
  throw ApiError.badRequest("Missing game slug");
122981
123274
  }
122982
123275
  const environment = getPlatformEnvironment(ctx.config);
122983
- logger48.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
123276
+ logger49.debug("Listing domains", { userId: ctx.user.id, slug: slug2, environment });
122984
123277
  const domains22 = await ctx.services.domain.list(slug2, environment, ctx.user);
122985
123278
  return { domains: domains22 };
122986
123279
  });
@@ -122995,7 +123288,7 @@ var init_domain_controller = __esm(() => {
122995
123288
  }
122996
123289
  const refresh = ctx.url.searchParams.get("refresh") === "true";
122997
123290
  const environment = getPlatformEnvironment(ctx.config);
122998
- logger48.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
123291
+ logger49.debug("Getting domain status", { userId: ctx.user.id, slug: slug2, hostname, refresh });
122999
123292
  return ctx.services.domain.getStatus(slug2, hostname, environment, ctx.user, refresh);
123000
123293
  });
123001
123294
  remove2 = requireDeveloper(async (ctx) => {
@@ -123008,7 +123301,7 @@ var init_domain_controller = __esm(() => {
123008
123301
  throw ApiError.badRequest("Missing hostname");
123009
123302
  }
123010
123303
  const environment = getPlatformEnvironment(ctx.config);
123011
- logger48.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
123304
+ logger49.debug("Removing domain", { userId: ctx.user.id, slug: slug2, hostname, environment });
123012
123305
  await ctx.services.domain.delete(slug2, hostname, environment, ctx.user);
123013
123306
  });
123014
123307
  domains2 = {
@@ -123018,7 +123311,7 @@ var init_domain_controller = __esm(() => {
123018
123311
  remove: remove2
123019
123312
  };
123020
123313
  });
123021
- var logger49;
123314
+ var logger50;
123022
123315
  var list3;
123023
123316
  var listAccessible;
123024
123317
  var getSubjects;
@@ -123035,17 +123328,17 @@ var init_game_controller = __esm(() => {
123035
123328
  init_src4();
123036
123329
  init_errors();
123037
123330
  init_utils11();
123038
- logger49 = log.scope("GameController");
123331
+ logger50 = log.scope("GameController");
123039
123332
  list3 = requireNonAnonymous(async (ctx) => {
123040
- logger49.debug("Listing games", { userId: ctx.user.id });
123333
+ logger50.debug("Listing games", { userId: ctx.user.id });
123041
123334
  return ctx.services.game.list(ctx.user);
123042
123335
  });
123043
123336
  listAccessible = requireNonAnonymous(async (ctx) => {
123044
- logger49.debug("Listing accessible games", { userId: ctx.user.id });
123337
+ logger50.debug("Listing accessible games", { userId: ctx.user.id });
123045
123338
  return ctx.services.game.listAccessible(ctx.user);
123046
123339
  });
123047
123340
  getSubjects = requireNonAnonymous(async (ctx) => {
123048
- logger49.debug("Getting game subjects", { userId: ctx.user.id });
123341
+ logger50.debug("Getting game subjects", { userId: ctx.user.id });
123049
123342
  return ctx.services.game.getSubjects();
123050
123343
  });
123051
123344
  getById2 = requireNonAnonymous(async (ctx) => {
@@ -123056,7 +123349,7 @@ var init_game_controller = __esm(() => {
123056
123349
  if (!isValidUUID(gameId)) {
123057
123350
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123058
123351
  }
123059
- logger49.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
123352
+ logger50.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
123060
123353
  return ctx.services.game.getById(gameId, ctx.user);
123061
123354
  });
123062
123355
  getBySlug = requireNonAnonymous(async (ctx) => {
@@ -123064,7 +123357,7 @@ var init_game_controller = __esm(() => {
123064
123357
  if (!slug2) {
123065
123358
  throw ApiError.badRequest("Missing game slug");
123066
123359
  }
123067
- logger49.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
123360
+ logger50.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
123068
123361
  return ctx.services.game.getBySlug(slug2, ctx.user);
123069
123362
  });
123070
123363
  getManifest = requireNonAnonymous(async (ctx) => {
@@ -123075,7 +123368,7 @@ var init_game_controller = __esm(() => {
123075
123368
  if (!isValidUUID(gameId)) {
123076
123369
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123077
123370
  }
123078
- logger49.debug("Getting game manifest by ID", {
123371
+ logger50.debug("Getting game manifest by ID", {
123079
123372
  userId: ctx.user.id,
123080
123373
  gameId,
123081
123374
  launchId: ctx.launchId
@@ -123094,12 +123387,12 @@ var init_game_controller = __esm(() => {
123094
123387
  } catch (error2) {
123095
123388
  if (error2 instanceof exports_external.ZodError) {
123096
123389
  const details = formatZodError(error2);
123097
- logger49.warn("Upsert game validation failed", { details });
123390
+ logger50.warn("Upsert game validation failed", { details });
123098
123391
  throw ApiError.unprocessableEntity("Validation failed", details);
123099
123392
  }
123100
123393
  throw ApiError.badRequest("Invalid JSON body");
123101
123394
  }
123102
- logger49.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
123395
+ logger50.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
123103
123396
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
123104
123397
  });
123105
123398
  remove3 = requireNonAnonymous(async (ctx) => {
@@ -123110,7 +123403,7 @@ var init_game_controller = __esm(() => {
123110
123403
  if (!isValidUUID(gameId)) {
123111
123404
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123112
123405
  }
123113
- logger49.debug("Deleting game", { userId: ctx.user.id, gameId });
123406
+ logger50.debug("Deleting game", { userId: ctx.user.id, gameId });
123114
123407
  await ctx.services.game.delete(gameId, ctx.user);
123115
123408
  });
123116
123409
  games2 = {
@@ -123124,7 +123417,7 @@ var init_game_controller = __esm(() => {
123124
123417
  remove: remove3
123125
123418
  };
123126
123419
  });
123127
- var logger50;
123420
+ var logger51;
123128
123421
  var list4;
123129
123422
  var addItem;
123130
123423
  var removeItem;
@@ -123135,9 +123428,9 @@ var init_inventory_controller = __esm(() => {
123135
123428
  init_src2();
123136
123429
  init_errors();
123137
123430
  init_utils11();
123138
- logger50 = log.scope("InventoryController");
123431
+ logger51 = log.scope("InventoryController");
123139
123432
  list4 = requireNonAnonymous(async (ctx) => {
123140
- logger50.debug("Listing inventory", { userId: ctx.user.id });
123433
+ logger51.debug("Listing inventory", { userId: ctx.user.id });
123141
123434
  return ctx.services.inventory.list(ctx.user);
123142
123435
  });
123143
123436
  addItem = requireNonAnonymous(async (ctx) => {
@@ -123148,12 +123441,12 @@ var init_inventory_controller = __esm(() => {
123148
123441
  } catch (error2) {
123149
123442
  if (error2 instanceof exports_external.ZodError) {
123150
123443
  const details = formatZodError(error2);
123151
- logger50.warn("Add inventory item validation failed", { details });
123444
+ logger51.warn("Add inventory item validation failed", { details });
123152
123445
  throw ApiError.unprocessableEntity("Invalid request body", details);
123153
123446
  }
123154
123447
  throw ApiError.badRequest("Invalid JSON body");
123155
123448
  }
123156
- logger50.debug("Adding item", {
123449
+ logger51.debug("Adding item", {
123157
123450
  userId: ctx.user.id,
123158
123451
  itemId: body2.itemId,
123159
123452
  qty: body2.qty
@@ -123168,12 +123461,12 @@ var init_inventory_controller = __esm(() => {
123168
123461
  } catch (error2) {
123169
123462
  if (error2 instanceof exports_external.ZodError) {
123170
123463
  const details = formatZodError(error2);
123171
- logger50.warn("Remove inventory item validation failed", { details });
123464
+ logger51.warn("Remove inventory item validation failed", { details });
123172
123465
  throw ApiError.unprocessableEntity("Invalid request body", details);
123173
123466
  }
123174
123467
  throw ApiError.badRequest("Invalid JSON body");
123175
123468
  }
123176
- logger50.debug("Removing item", {
123469
+ logger51.debug("Removing item", {
123177
123470
  userId: ctx.user.id,
123178
123471
  itemId: body2.itemId,
123179
123472
  qty: body2.qty
@@ -123186,7 +123479,7 @@ var init_inventory_controller = __esm(() => {
123186
123479
  removeItem
123187
123480
  };
123188
123481
  });
123189
- var logger51;
123482
+ var logger52;
123190
123483
  var list5;
123191
123484
  var getById3;
123192
123485
  var resolve2;
@@ -123205,10 +123498,10 @@ var init_item_controller = __esm(() => {
123205
123498
  init_src4();
123206
123499
  init_errors();
123207
123500
  init_utils11();
123208
- logger51 = log.scope("ItemController");
123501
+ logger52 = log.scope("ItemController");
123209
123502
  list5 = requireNonAnonymous(async (ctx) => {
123210
123503
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
123211
- logger51.debug("Listing items", { userId: ctx.user.id, gameId });
123504
+ logger52.debug("Listing items", { userId: ctx.user.id, gameId });
123212
123505
  return ctx.services.item.list(gameId);
123213
123506
  });
123214
123507
  getById3 = requireNonAnonymous(async (ctx) => {
@@ -123219,7 +123512,7 @@ var init_item_controller = __esm(() => {
123219
123512
  if (!isValidUUID(itemId)) {
123220
123513
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123221
123514
  }
123222
- logger51.debug("Getting item", { userId: ctx.user.id, itemId });
123515
+ logger52.debug("Getting item", { userId: ctx.user.id, itemId });
123223
123516
  return ctx.services.item.getById(itemId);
123224
123517
  });
123225
123518
  resolve2 = requireNonAnonymous(async (ctx) => {
@@ -123231,7 +123524,7 @@ var init_item_controller = __esm(() => {
123231
123524
  if (gameId && !isValidUUID(gameId)) {
123232
123525
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123233
123526
  }
123234
- logger51.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
123527
+ logger52.debug("Resolving item", { userId: ctx.user.id, slug: slug2, gameId });
123235
123528
  return ctx.services.item.resolveBySlug(slug2, gameId);
123236
123529
  });
123237
123530
  create3 = requireRole(["admin"], async (ctx) => {
@@ -123242,12 +123535,12 @@ var init_item_controller = __esm(() => {
123242
123535
  } catch (error2) {
123243
123536
  if (error2 instanceof exports_external.ZodError) {
123244
123537
  const details = formatZodError(error2);
123245
- logger51.warn("Create item validation failed", { details });
123538
+ logger52.warn("Create item validation failed", { details });
123246
123539
  throw ApiError.unprocessableEntity("Validation failed", details);
123247
123540
  }
123248
123541
  throw ApiError.badRequest("Invalid JSON body");
123249
123542
  }
123250
- logger51.debug("Creating item", {
123543
+ logger52.debug("Creating item", {
123251
123544
  userId: ctx.user.id,
123252
123545
  slug: body2.slug,
123253
123546
  displayName: body2.displayName
@@ -123269,7 +123562,7 @@ var init_item_controller = __esm(() => {
123269
123562
  } catch (error2) {
123270
123563
  if (error2 instanceof exports_external.ZodError) {
123271
123564
  const details = formatZodError(error2);
123272
- logger51.warn("Update item validation failed", { details });
123565
+ logger52.warn("Update item validation failed", { details });
123273
123566
  throw ApiError.unprocessableEntity("Validation failed", details);
123274
123567
  }
123275
123568
  throw ApiError.badRequest("Invalid JSON body");
@@ -123277,7 +123570,7 @@ var init_item_controller = __esm(() => {
123277
123570
  if (Object.keys(body2).length === 0) {
123278
123571
  throw ApiError.badRequest("No update data provided");
123279
123572
  }
123280
- logger51.debug("Updating item", {
123573
+ logger52.debug("Updating item", {
123281
123574
  userId: ctx.user.id,
123282
123575
  itemId,
123283
123576
  slug: body2.slug,
@@ -123294,7 +123587,7 @@ var init_item_controller = __esm(() => {
123294
123587
  if (!isValidUUID(itemId)) {
123295
123588
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123296
123589
  }
123297
- logger51.debug("Deleting item", { userId: ctx.user.id, itemId });
123590
+ logger52.debug("Deleting item", { userId: ctx.user.id, itemId });
123298
123591
  await ctx.services.item.delete(itemId);
123299
123592
  });
123300
123593
  listByGame = requireNonAnonymous(async (ctx) => {
@@ -123305,7 +123598,7 @@ var init_item_controller = __esm(() => {
123305
123598
  if (!isValidUUID(gameId)) {
123306
123599
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
123307
123600
  }
123308
- logger51.debug("Listing game items", { userId: ctx.user.id, gameId });
123601
+ logger52.debug("Listing game items", { userId: ctx.user.id, gameId });
123309
123602
  return ctx.services.item.listByGame(gameId);
123310
123603
  });
123311
123604
  createForGame = requireNonAnonymous(async (ctx) => {
@@ -123323,12 +123616,12 @@ var init_item_controller = __esm(() => {
123323
123616
  } catch (error2) {
123324
123617
  if (error2 instanceof exports_external.ZodError) {
123325
123618
  const details = formatZodError(error2);
123326
- logger51.warn("Create game item validation failed", { details });
123619
+ logger52.warn("Create game item validation failed", { details });
123327
123620
  throw ApiError.unprocessableEntity("Validation failed", details);
123328
123621
  }
123329
123622
  throw ApiError.badRequest("Invalid JSON body");
123330
123623
  }
123331
- logger51.debug("Creating game item", {
123624
+ logger52.debug("Creating game item", {
123332
123625
  userId: ctx.user.id,
123333
123626
  gameId,
123334
123627
  slug: body2.slug,
@@ -123355,7 +123648,7 @@ var init_item_controller = __esm(() => {
123355
123648
  } catch (error2) {
123356
123649
  if (error2 instanceof exports_external.ZodError) {
123357
123650
  const details = formatZodError(error2);
123358
- logger51.warn("Update game item validation failed", { details });
123651
+ logger52.warn("Update game item validation failed", { details });
123359
123652
  throw ApiError.unprocessableEntity("Validation failed", details);
123360
123653
  }
123361
123654
  throw ApiError.badRequest("Invalid JSON body");
@@ -123363,7 +123656,7 @@ var init_item_controller = __esm(() => {
123363
123656
  if (Object.keys(body2).length === 0) {
123364
123657
  throw ApiError.badRequest("No update data provided");
123365
123658
  }
123366
- logger51.debug("Updating game item", {
123659
+ logger52.debug("Updating game item", {
123367
123660
  userId: ctx.user.id,
123368
123661
  gameId,
123369
123662
  itemId,
@@ -123385,7 +123678,7 @@ var init_item_controller = __esm(() => {
123385
123678
  if (!isValidUUID(itemId)) {
123386
123679
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
123387
123680
  }
123388
- logger51.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
123681
+ logger52.debug("Deleting game item", { userId: ctx.user.id, gameId, itemId });
123389
123682
  await ctx.services.item.deleteForGame(gameId, itemId, ctx.user);
123390
123683
  });
123391
123684
  items2 = {
@@ -123401,7 +123694,7 @@ var init_item_controller = __esm(() => {
123401
123694
  deleteForGame
123402
123695
  };
123403
123696
  });
123404
- var logger52;
123697
+ var logger53;
123405
123698
  var listKeys;
123406
123699
  var getStats;
123407
123700
  var seed;
@@ -123416,7 +123709,7 @@ var init_kv_controller = __esm(() => {
123416
123709
  init_src2();
123417
123710
  init_errors();
123418
123711
  init_utils11();
123419
- logger52 = log.scope("KVController");
123712
+ logger53 = log.scope("KVController");
123420
123713
  listKeys = requireDeveloper(async (ctx) => {
123421
123714
  const slug2 = ctx.params.slug;
123422
123715
  if (!slug2) {
@@ -123424,7 +123717,7 @@ var init_kv_controller = __esm(() => {
123424
123717
  }
123425
123718
  const url2 = ctx.url;
123426
123719
  const prefix2 = url2.searchParams.get("prefix") || undefined;
123427
- logger52.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
123720
+ logger53.debug("Listing keys", { userId: ctx.user.id, slug: slug2, prefix: prefix2 });
123428
123721
  const keys = await ctx.services.kv.listKeys(slug2, ctx.user, prefix2);
123429
123722
  return { keys };
123430
123723
  });
@@ -123433,7 +123726,7 @@ var init_kv_controller = __esm(() => {
123433
123726
  if (!slug2) {
123434
123727
  throw ApiError.badRequest("Missing game slug");
123435
123728
  }
123436
- logger52.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
123729
+ logger53.debug("Getting stats", { userId: ctx.user.id, slug: slug2 });
123437
123730
  return ctx.services.kv.getStats(slug2, ctx.user);
123438
123731
  });
123439
123732
  seed = requireDeveloper(async (ctx) => {
@@ -123448,12 +123741,12 @@ var init_kv_controller = __esm(() => {
123448
123741
  } catch (error2) {
123449
123742
  if (error2 instanceof exports_external.ZodError) {
123450
123743
  const details = formatZodError(error2);
123451
- logger52.warn("Seed validation failed", { details });
123744
+ logger53.warn("Seed validation failed", { details });
123452
123745
  throw ApiError.unprocessableEntity("Validation failed", details);
123453
123746
  }
123454
123747
  throw ApiError.badRequest("Invalid JSON body");
123455
123748
  }
123456
- logger52.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
123749
+ logger53.debug("Seeding values", { userId: ctx.user.id, slug: slug2, count: body2.entries.length });
123457
123750
  await ctx.services.kv.seed(slug2, body2.entries, ctx.user);
123458
123751
  return { success: true, count: body2.entries.length };
123459
123752
  });
@@ -123463,7 +123756,7 @@ var init_kv_controller = __esm(() => {
123463
123756
  if (!slug2 || !key) {
123464
123757
  throw ApiError.badRequest("Missing game slug or key");
123465
123758
  }
123466
- logger52.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
123759
+ logger53.debug("Getting value", { userId: ctx.user.id, slug: slug2, key });
123467
123760
  const value = await ctx.services.kv.getValue(slug2, key, ctx.user);
123468
123761
  return { key, value };
123469
123762
  });
@@ -123477,7 +123770,7 @@ var init_kv_controller = __esm(() => {
123477
123770
  if (!value) {
123478
123771
  throw ApiError.badRequest("Missing value in request body");
123479
123772
  }
123480
- logger52.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
123773
+ logger53.debug("Setting value", { userId: ctx.user.id, slug: slug2, key, size: value.length });
123481
123774
  await ctx.services.kv.setValue(slug2, key, value, ctx.user);
123482
123775
  return { success: true, key };
123483
123776
  });
@@ -123487,7 +123780,7 @@ var init_kv_controller = __esm(() => {
123487
123780
  if (!slug2 || !key) {
123488
123781
  throw ApiError.badRequest("Missing game slug or key");
123489
123782
  }
123490
- logger52.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
123783
+ logger53.debug("Deleting value", { userId: ctx.user.id, slug: slug2, key });
123491
123784
  await ctx.services.kv.deleteValue(slug2, key, ctx.user);
123492
123785
  return { success: true, key };
123493
123786
  });
@@ -123497,7 +123790,7 @@ var init_kv_controller = __esm(() => {
123497
123790
  if (!slug2 || !key) {
123498
123791
  throw ApiError.badRequest("Missing game slug or key");
123499
123792
  }
123500
- logger52.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
123793
+ logger53.debug("Getting metadata", { userId: ctx.user.id, slug: slug2, key });
123501
123794
  const metadata2 = await ctx.services.kv.getMetadata(slug2, key, ctx.user);
123502
123795
  return { key, metadata: metadata2 };
123503
123796
  });
@@ -123506,12 +123799,12 @@ var init_kv_controller = __esm(() => {
123506
123799
  if (!slug2) {
123507
123800
  throw ApiError.badRequest("Missing game slug");
123508
123801
  }
123509
- logger52.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
123802
+ logger53.debug("Clearing all keys", { userId: ctx.user.id, slug: slug2 });
123510
123803
  const deleted = await ctx.services.kv.clear(slug2, ctx.user);
123511
123804
  return { success: true, deleted };
123512
123805
  });
123513
123806
  });
123514
- var logger53;
123807
+ var logger54;
123515
123808
  var submitScore;
123516
123809
  var getGlobalLeaderboard;
123517
123810
  var getLeaderboard;
@@ -123525,7 +123818,7 @@ var init_leaderboard_controller = __esm(() => {
123525
123818
  init_src2();
123526
123819
  init_errors();
123527
123820
  init_utils11();
123528
- logger53 = log.scope("LeaderboardController");
123821
+ logger54 = log.scope("LeaderboardController");
123529
123822
  submitScore = requireAuth(async (ctx) => {
123530
123823
  const gameId = ctx.params.gameId;
123531
123824
  if (!gameId) {
@@ -123538,12 +123831,12 @@ var init_leaderboard_controller = __esm(() => {
123538
123831
  } catch (error2) {
123539
123832
  if (error2 instanceof exports_external.ZodError) {
123540
123833
  const details = formatZodError(error2);
123541
- logger53.warn("Submit score validation failed", { details });
123834
+ logger54.warn("Submit score validation failed", { details });
123542
123835
  throw ApiError.unprocessableEntity("Validation failed", details);
123543
123836
  }
123544
123837
  throw ApiError.badRequest("Invalid JSON body");
123545
123838
  }
123546
- logger53.debug("Submitting score", {
123839
+ logger54.debug("Submitting score", {
123547
123840
  userId: ctx.user.id,
123548
123841
  gameId,
123549
123842
  score: body2.score
@@ -123566,12 +123859,12 @@ var init_leaderboard_controller = __esm(() => {
123566
123859
  } catch (error2) {
123567
123860
  if (error2 instanceof exports_external.ZodError) {
123568
123861
  const details = formatZodError(error2);
123569
- logger53.warn("Get global leaderboard query validation failed", { details });
123862
+ logger54.warn("Get global leaderboard query validation failed", { details });
123570
123863
  throw ApiError.badRequest("Invalid query parameters", details);
123571
123864
  }
123572
123865
  throw ApiError.badRequest("Invalid query parameters");
123573
123866
  }
123574
- logger53.debug("Getting global leaderboard", {
123867
+ logger54.debug("Getting global leaderboard", {
123575
123868
  userId: ctx.user.id,
123576
123869
  gameId,
123577
123870
  ...query
@@ -123594,12 +123887,12 @@ var init_leaderboard_controller = __esm(() => {
123594
123887
  } catch (error2) {
123595
123888
  if (error2 instanceof exports_external.ZodError) {
123596
123889
  const details = formatZodError(error2);
123597
- logger53.warn("Get leaderboard query validation failed", { details });
123890
+ logger54.warn("Get leaderboard query validation failed", { details });
123598
123891
  throw ApiError.badRequest("Invalid query parameters", details);
123599
123892
  }
123600
123893
  throw ApiError.badRequest("Invalid query parameters");
123601
123894
  }
123602
- logger53.debug("Getting leaderboard", {
123895
+ logger54.debug("Getting leaderboard", {
123603
123896
  userId: ctx.user.id,
123604
123897
  gameId,
123605
123898
  ...query
@@ -123611,7 +123904,7 @@ var init_leaderboard_controller = __esm(() => {
123611
123904
  if (!gameId || !userId) {
123612
123905
  throw ApiError.badRequest("Game ID and User ID are required");
123613
123906
  }
123614
- logger53.debug("Getting user rank", {
123907
+ logger54.debug("Getting user rank", {
123615
123908
  requesterId: ctx.user.id,
123616
123909
  gameId,
123617
123910
  targetUserId: userId
@@ -123626,7 +123919,7 @@ var init_leaderboard_controller = __esm(() => {
123626
123919
  const url2 = ctx.url;
123627
123920
  const limit = Math.min(Number(url2.searchParams.get("limit") || "50"), 100);
123628
123921
  const gameId = url2.searchParams.get("gameId") || undefined;
123629
- logger53.debug("Getting user all scores", {
123922
+ logger54.debug("Getting user all scores", {
123630
123923
  requesterId: ctx.user.id,
123631
123924
  targetUserId: userId,
123632
123925
  gameId,
@@ -123641,7 +123934,7 @@ var init_leaderboard_controller = __esm(() => {
123641
123934
  }
123642
123935
  const url2 = ctx.url;
123643
123936
  const limit = Math.min(Number(url2.searchParams.get("limit") || "10"), 100);
123644
- logger53.debug("Getting user scores", {
123937
+ logger54.debug("Getting user scores", {
123645
123938
  requesterId: ctx.user.id,
123646
123939
  gameId,
123647
123940
  targetUserId: userId,
@@ -123659,7 +123952,7 @@ var init_leaderboard_controller = __esm(() => {
123659
123952
  };
123660
123953
  });
123661
123954
  async function listConfigs(ctx) {
123662
- logger54.debug("Listing level configs");
123955
+ logger55.debug("Listing level configs");
123663
123956
  return ctx.services.level.listConfigs();
123664
123957
  }
123665
123958
  async function getConfig(ctx) {
@@ -123671,10 +123964,10 @@ async function getConfig(ctx) {
123671
123964
  if (isNaN(level) || level < 1) {
123672
123965
  throw ApiError.badRequest("Level must be a positive integer");
123673
123966
  }
123674
- logger54.debug("Getting level config", { level });
123967
+ logger55.debug("Getting level config", { level });
123675
123968
  return ctx.services.level.getConfig(level);
123676
123969
  }
123677
- var logger54;
123970
+ var logger55;
123678
123971
  var getByUser;
123679
123972
  var getProgress;
123680
123973
  var levels;
@@ -123682,13 +123975,13 @@ var init_level_controller = __esm(() => {
123682
123975
  init_src2();
123683
123976
  init_errors();
123684
123977
  init_utils11();
123685
- logger54 = log.scope("LevelController");
123978
+ logger55 = log.scope("LevelController");
123686
123979
  getByUser = requireNonAnonymous(async (ctx) => {
123687
- logger54.debug("Getting user level", { userId: ctx.user.id });
123980
+ logger55.debug("Getting user level", { userId: ctx.user.id });
123688
123981
  return ctx.services.level.getByUser(ctx.user);
123689
123982
  });
123690
123983
  getProgress = requireNonAnonymous(async (ctx) => {
123691
- logger54.debug("Getting level progress", { userId: ctx.user.id });
123984
+ logger55.debug("Getting level progress", { userId: ctx.user.id });
123692
123985
  return ctx.services.level.getProgress(ctx.user);
123693
123986
  });
123694
123987
  levels = {
@@ -123698,14 +123991,14 @@ var init_level_controller = __esm(() => {
123698
123991
  getProgress
123699
123992
  };
123700
123993
  });
123701
- var logger55;
123994
+ var logger56;
123702
123995
  var generateToken;
123703
123996
  var logs;
123704
123997
  var init_logs_controller = __esm(() => {
123705
123998
  init_src2();
123706
123999
  init_errors();
123707
124000
  init_utils11();
123708
- logger55 = log.scope("LogsController");
124001
+ logger56 = log.scope("LogsController");
123709
124002
  generateToken = requireDeveloper(async (ctx) => {
123710
124003
  const slug2 = ctx.params.slug;
123711
124004
  if (!slug2) {
@@ -123724,7 +124017,7 @@ var init_logs_controller = __esm(() => {
123724
124017
  }
123725
124018
  throw ApiError.badRequest("Invalid JSON body");
123726
124019
  }
123727
- logger55.debug("Generating log stream token", {
124020
+ logger56.debug("Generating log stream token", {
123728
124021
  userId: ctx.user.id,
123729
124022
  slug: slug2,
123730
124023
  environment: body2.environment
@@ -123741,22 +124034,22 @@ var init_logs_controller = __esm(() => {
123741
124034
  generateToken
123742
124035
  };
123743
124036
  });
123744
- var logger56;
124037
+ var logger57;
123745
124038
  var getStatus3;
123746
124039
  var lti;
123747
124040
  var init_lti_controller = __esm(() => {
123748
124041
  init_src2();
123749
124042
  init_utils11();
123750
- logger56 = log.scope("LtiController");
124043
+ logger57 = log.scope("LtiController");
123751
124044
  getStatus3 = requireNonAnonymous(async (ctx) => {
123752
- logger56.debug("Getting status", { userId: ctx.user.id });
124045
+ logger57.debug("Getting status", { userId: ctx.user.id });
123753
124046
  return ctx.services.lti.getStatus(ctx.user);
123754
124047
  });
123755
124048
  lti = {
123756
124049
  getStatus: getStatus3
123757
124050
  };
123758
124051
  });
123759
- var logger57;
124052
+ var logger58;
123760
124053
  var getByIdentifier;
123761
124054
  var getElements;
123762
124055
  var getObjects;
@@ -123770,13 +124063,13 @@ var init_map_controller = __esm(() => {
123770
124063
  init_src4();
123771
124064
  init_errors();
123772
124065
  init_utils11();
123773
- logger57 = log.scope("MapController");
124066
+ logger58 = log.scope("MapController");
123774
124067
  getByIdentifier = requireNonAnonymous(async (ctx) => {
123775
124068
  const identifier = ctx.params.identifier;
123776
124069
  if (!identifier) {
123777
124070
  throw ApiError.badRequest("Missing map identifier");
123778
124071
  }
123779
- logger57.debug("Getting map", { userId: ctx.user.id, identifier });
124072
+ logger58.debug("Getting map", { userId: ctx.user.id, identifier });
123780
124073
  return ctx.services.map.getByIdentifier(identifier);
123781
124074
  });
123782
124075
  getElements = requireNonAnonymous(async (ctx) => {
@@ -123787,7 +124080,7 @@ var init_map_controller = __esm(() => {
123787
124080
  if (!isValidUUID(mapId)) {
123788
124081
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
123789
124082
  }
123790
- logger57.debug("Getting map elements", { userId: ctx.user.id, mapId });
124083
+ logger58.debug("Getting map elements", { userId: ctx.user.id, mapId });
123791
124084
  return ctx.services.map.getElements(mapId);
123792
124085
  });
123793
124086
  getObjects = requireNonAnonymous(async (ctx) => {
@@ -123798,7 +124091,7 @@ var init_map_controller = __esm(() => {
123798
124091
  if (!isValidUUID(mapId)) {
123799
124092
  throw ApiError.unprocessableEntity("mapId must be a valid UUID format");
123800
124093
  }
123801
- logger57.debug("Getting map objects", { userId: ctx.user.id, mapId });
124094
+ logger58.debug("Getting map objects", { userId: ctx.user.id, mapId });
123802
124095
  return ctx.services.map.getObjects(mapId, ctx.user.id);
123803
124096
  });
123804
124097
  createObject = requireNonAnonymous(async (ctx) => {
@@ -123820,12 +124113,12 @@ var init_map_controller = __esm(() => {
123820
124113
  } catch (error2) {
123821
124114
  if (error2 instanceof exports_external.ZodError) {
123822
124115
  const details = formatZodError(error2);
123823
- logger57.warn("Create map object validation failed", { details });
124116
+ logger58.warn("Create map object validation failed", { details });
123824
124117
  throw ApiError.unprocessableEntity("Validation failed", details);
123825
124118
  }
123826
124119
  throw ApiError.badRequest("Invalid JSON body");
123827
124120
  }
123828
- logger57.debug("Creating map object", {
124121
+ logger58.debug("Creating map object", {
123829
124122
  userId: ctx.user.id,
123830
124123
  mapId,
123831
124124
  itemId: body2.itemId,
@@ -123849,7 +124142,7 @@ var init_map_controller = __esm(() => {
123849
124142
  if (!isValidUUID(objectId)) {
123850
124143
  throw ApiError.unprocessableEntity("objectId must be a valid UUID format");
123851
124144
  }
123852
- logger57.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
124145
+ logger58.debug("Deleting map object", { userId: ctx.user.id, mapId, objectId });
123853
124146
  await ctx.services.map.deleteObject(mapId, objectId, ctx.user);
123854
124147
  });
123855
124148
  maps2 = {
@@ -123860,7 +124153,7 @@ var init_map_controller = __esm(() => {
123860
124153
  deleteObject
123861
124154
  };
123862
124155
  });
123863
- var logger58;
124156
+ var logger59;
123864
124157
  var list6;
123865
124158
  var updateStatus;
123866
124159
  var getStats2;
@@ -123873,7 +124166,7 @@ var init_notification_controller = __esm(() => {
123873
124166
  init_src2();
123874
124167
  init_errors();
123875
124168
  init_utils11();
123876
- logger58 = log.scope("NotificationController");
124169
+ logger59 = log.scope("NotificationController");
123877
124170
  list6 = requireNonAnonymous(async (ctx) => {
123878
124171
  const query = {
123879
124172
  status: ctx.url.searchParams.get("status") || undefined,
@@ -123884,10 +124177,10 @@ var init_notification_controller = __esm(() => {
123884
124177
  const result = NotificationListQuerySchema.omit({ userId: true }).safeParse(query);
123885
124178
  if (!result.success) {
123886
124179
  const details = formatZodError(result.error);
123887
- logger58.warn("List notifications query validation failed", { details });
124180
+ logger59.warn("List notifications query validation failed", { details });
123888
124181
  throw ApiError.badRequest("Invalid query parameters", details);
123889
124182
  }
123890
- logger58.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
124183
+ logger59.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
123891
124184
  return ctx.services.notification.list(ctx.user, result.data);
123892
124185
  });
123893
124186
  updateStatus = requireNonAnonymous(async (ctx) => {
@@ -123902,12 +124195,12 @@ var init_notification_controller = __esm(() => {
123902
124195
  } catch (error2) {
123903
124196
  if (error2 instanceof exports_external.ZodError) {
123904
124197
  const details = formatZodError(error2);
123905
- logger58.warn("Update notification status validation failed", { details });
124198
+ logger59.warn("Update notification status validation failed", { details });
123906
124199
  throw ApiError.unprocessableEntity("Invalid request body", details);
123907
124200
  }
123908
124201
  throw ApiError.badRequest("Invalid JSON body");
123909
124202
  }
123910
- logger58.debug("Updating status", {
124203
+ logger59.debug("Updating status", {
123911
124204
  userId: ctx.user.id,
123912
124205
  notificationId,
123913
124206
  status: body2.status
@@ -123917,7 +124210,7 @@ var init_notification_controller = __esm(() => {
123917
124210
  getStats2 = requireNonAnonymous(async (ctx) => {
123918
124211
  const startDate = ctx.url.searchParams.get("startDate");
123919
124212
  const endDate = ctx.url.searchParams.get("endDate");
123920
- logger58.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
124213
+ logger59.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
123921
124214
  return ctx.services.notification.getStats(ctx.user, {
123922
124215
  startDate: startDate ? new Date(startDate) : undefined,
123923
124216
  endDate: endDate ? new Date(endDate) : undefined
@@ -123931,12 +124224,12 @@ var init_notification_controller = __esm(() => {
123931
124224
  } catch (error2) {
123932
124225
  if (error2 instanceof exports_external.ZodError) {
123933
124226
  const details = formatZodError(error2);
123934
- logger58.warn("Create notification validation failed", { details });
124227
+ logger59.warn("Create notification validation failed", { details });
123935
124228
  throw ApiError.unprocessableEntity("Invalid request body", details);
123936
124229
  }
123937
124230
  throw ApiError.badRequest("Invalid JSON body");
123938
124231
  }
123939
- logger58.debug("Creating notification", {
124232
+ logger59.debug("Creating notification", {
123940
124233
  userId: ctx.user.id,
123941
124234
  targetUserId: body2.userId,
123942
124235
  type: body2.type
@@ -123954,12 +124247,12 @@ var init_notification_controller = __esm(() => {
123954
124247
  });
123955
124248
  });
123956
124249
  deliver = requireNonAnonymous(async (ctx) => {
123957
- logger58.debug("Delivering notifications", { userId: ctx.user.id });
124250
+ logger59.debug("Delivering notifications", { userId: ctx.user.id });
123958
124251
  try {
123959
124252
  await ctx.services.notification.deliverPending(ctx.user.id);
123960
124253
  return { success: true };
123961
124254
  } catch (error2) {
123962
- logger58.error("Failed to deliver notifications", { error: error2 });
124255
+ logger59.error("Failed to deliver notifications", { error: error2 });
123963
124256
  throw ApiError.internal("Failed to deliver notifications");
123964
124257
  }
123965
124258
  });
@@ -123971,16 +124264,16 @@ var init_notification_controller = __esm(() => {
123971
124264
  deliver
123972
124265
  };
123973
124266
  });
123974
- var logger59;
124267
+ var logger60;
123975
124268
  var generateToken2;
123976
124269
  var realtime;
123977
124270
  var init_realtime_controller = __esm(() => {
123978
124271
  init_src2();
123979
124272
  init_utils11();
123980
- logger59 = log.scope("RealtimeController");
124273
+ logger60 = log.scope("RealtimeController");
123981
124274
  generateToken2 = requireNonAnonymous(async (ctx) => {
123982
124275
  const gameIdOrSlug = ctx.params.gameId;
123983
- logger59.debug("Generating token", {
124276
+ logger60.debug("Generating token", {
123984
124277
  userId: ctx.user.id,
123985
124278
  gameId: gameIdOrSlug || "global",
123986
124279
  launchId: ctx.launchId
@@ -123991,7 +124284,7 @@ var init_realtime_controller = __esm(() => {
123991
124284
  generateToken: generateToken2
123992
124285
  };
123993
124286
  });
123994
- var logger60;
124287
+ var logger61;
123995
124288
  var listKeys2;
123996
124289
  var setSecrets;
123997
124290
  var deleteSecret;
@@ -124002,13 +124295,13 @@ var init_secrets_controller = __esm(() => {
124002
124295
  init_src2();
124003
124296
  init_errors();
124004
124297
  init_utils11();
124005
- logger60 = log.scope("SecretsController");
124298
+ logger61 = log.scope("SecretsController");
124006
124299
  listKeys2 = requireDeveloper(async (ctx) => {
124007
124300
  const slug2 = ctx.params.slug;
124008
124301
  if (!slug2) {
124009
124302
  throw ApiError.badRequest("Missing game slug");
124010
124303
  }
124011
- logger60.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
124304
+ logger61.debug("Listing secret keys", { userId: ctx.user.id, slug: slug2 });
124012
124305
  const keys = await ctx.services.secrets.listKeys(slug2, ctx.user);
124013
124306
  return { keys };
124014
124307
  });
@@ -124024,12 +124317,12 @@ var init_secrets_controller = __esm(() => {
124024
124317
  } catch (error2) {
124025
124318
  if (error2 instanceof exports_external.ZodError) {
124026
124319
  const details = formatZodError(error2);
124027
- logger60.warn("Set secrets validation failed", { details });
124320
+ logger61.warn("Set secrets validation failed", { details });
124028
124321
  throw ApiError.unprocessableEntity("Validation failed", details);
124029
124322
  }
124030
124323
  throw ApiError.badRequest("Invalid JSON body");
124031
124324
  }
124032
- logger60.debug("Setting secrets", {
124325
+ logger61.debug("Setting secrets", {
124033
124326
  userId: ctx.user.id,
124034
124327
  slug: slug2,
124035
124328
  keyCount: Object.keys(body2).length
@@ -124046,7 +124339,7 @@ var init_secrets_controller = __esm(() => {
124046
124339
  if (!key) {
124047
124340
  throw ApiError.badRequest("Missing secret key");
124048
124341
  }
124049
- logger60.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
124342
+ logger61.debug("Deleting secret", { userId: ctx.user.id, slug: slug2, key });
124050
124343
  await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
124051
124344
  return { success: true };
124052
124345
  });
@@ -124056,7 +124349,7 @@ var init_secrets_controller = __esm(() => {
124056
124349
  deleteSecret
124057
124350
  };
124058
124351
  });
124059
- var logger61;
124352
+ var logger62;
124060
124353
  var seed2;
124061
124354
  var init_seed_controller = __esm(() => {
124062
124355
  init_esm();
@@ -124064,7 +124357,7 @@ var init_seed_controller = __esm(() => {
124064
124357
  init_src2();
124065
124358
  init_errors();
124066
124359
  init_utils11();
124067
- logger61 = log.scope("SeedController");
124360
+ logger62 = log.scope("SeedController");
124068
124361
  seed2 = requireDeveloper(async (ctx) => {
124069
124362
  const slug2 = ctx.params.slug;
124070
124363
  if (!slug2) {
@@ -124077,12 +124370,12 @@ var init_seed_controller = __esm(() => {
124077
124370
  } catch (error2) {
124078
124371
  if (error2 instanceof exports_external.ZodError) {
124079
124372
  const details = formatZodError(error2);
124080
- logger61.warn("Seed database validation failed", { details });
124373
+ logger62.warn("Seed database validation failed", { details });
124081
124374
  throw ApiError.unprocessableEntity("Validation failed", details);
124082
124375
  }
124083
124376
  throw ApiError.badRequest("Invalid JSON body");
124084
124377
  }
124085
- logger61.debug("Seeding database", {
124378
+ logger62.debug("Seeding database", {
124086
124379
  userId: ctx.user.id,
124087
124380
  slug: slug2,
124088
124381
  codeLength: body2.code.length,
@@ -124091,7 +124384,7 @@ var init_seed_controller = __esm(() => {
124091
124384
  return ctx.services.seed.seed(slug2, body2.code, ctx.user, body2.secrets);
124092
124385
  });
124093
124386
  });
124094
- var logger62;
124387
+ var logger63;
124095
124388
  var start2;
124096
124389
  var end;
124097
124390
  var mintToken;
@@ -124101,13 +124394,13 @@ var init_session_controller = __esm(() => {
124101
124394
  init_tunnel();
124102
124395
  init_errors();
124103
124396
  init_utils11();
124104
- logger62 = log.scope("SessionController");
124397
+ logger63 = log.scope("SessionController");
124105
124398
  start2 = requireAuth(async (ctx) => {
124106
124399
  const gameIdOrSlug = ctx.params.gameId;
124107
124400
  if (!gameIdOrSlug) {
124108
124401
  throw ApiError.badRequest("Missing game ID or slug");
124109
124402
  }
124110
- logger62.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124403
+ logger63.debug("Starting session", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124111
124404
  return ctx.services.session.start(gameIdOrSlug, ctx.user.id);
124112
124405
  });
124113
124406
  end = requireAuth(async (ctx) => {
@@ -124119,7 +124412,7 @@ var init_session_controller = __esm(() => {
124119
124412
  if (!sessionId) {
124120
124413
  throw ApiError.badRequest("Missing session ID");
124121
124414
  }
124122
- logger62.debug("Ending session", {
124415
+ logger63.debug("Ending session", {
124123
124416
  userId: ctx.user.id,
124124
124417
  gameIdOrSlug,
124125
124418
  sessionId,
@@ -124132,7 +124425,7 @@ var init_session_controller = __esm(() => {
124132
124425
  if (!gameIdOrSlug) {
124133
124426
  throw ApiError.badRequest("Missing game ID or slug");
124134
124427
  }
124135
- logger62.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124428
+ logger63.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
124136
124429
  const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
124137
124430
  let baseUrl;
124138
124431
  if (ctx.config.isLocal) {
@@ -124148,22 +124441,22 @@ var init_session_controller = __esm(() => {
124148
124441
  mintToken
124149
124442
  };
124150
124443
  });
124151
- var logger63;
124444
+ var logger64;
124152
124445
  var getShopView;
124153
124446
  var shop;
124154
124447
  var init_shop_controller = __esm(() => {
124155
124448
  init_src2();
124156
124449
  init_utils11();
124157
- logger63 = log.scope("ShopController");
124450
+ logger64 = log.scope("ShopController");
124158
124451
  getShopView = requireNonAnonymous(async (ctx) => {
124159
- logger63.debug("Getting shop view", { userId: ctx.user.id });
124452
+ logger64.debug("Getting shop view", { userId: ctx.user.id });
124160
124453
  return ctx.services.shop.getShopView(ctx.user);
124161
124454
  });
124162
124455
  shop = {
124163
124456
  getShopView
124164
124457
  };
124165
124458
  });
124166
- var logger64;
124459
+ var logger65;
124167
124460
  var list7;
124168
124461
  var getById4;
124169
124462
  var create5;
@@ -124182,9 +124475,9 @@ var init_shop_listing_controller = __esm(() => {
124182
124475
  init_src4();
124183
124476
  init_errors();
124184
124477
  init_utils11();
124185
- logger64 = log.scope("ShopListingController");
124478
+ logger65 = log.scope("ShopListingController");
124186
124479
  list7 = requireAdmin(async (ctx) => {
124187
- logger64.debug("Listing shop listings", { userId: ctx.user.id });
124480
+ logger65.debug("Listing shop listings", { userId: ctx.user.id });
124188
124481
  return ctx.services.shopListing.list();
124189
124482
  });
124190
124483
  getById4 = requireAdmin(async (ctx) => {
@@ -124195,7 +124488,7 @@ var init_shop_listing_controller = __esm(() => {
124195
124488
  if (!isValidUUID(listingId)) {
124196
124489
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
124197
124490
  }
124198
- logger64.debug("Getting listing", { userId: ctx.user.id, listingId });
124491
+ logger65.debug("Getting listing", { userId: ctx.user.id, listingId });
124199
124492
  return ctx.services.shopListing.getById(listingId);
124200
124493
  });
124201
124494
  create5 = requireAdmin(async (ctx) => {
@@ -124206,12 +124499,12 @@ var init_shop_listing_controller = __esm(() => {
124206
124499
  } catch (error2) {
124207
124500
  if (error2 instanceof exports_external.ZodError) {
124208
124501
  const details = formatZodError(error2);
124209
- logger64.warn("Create shop listing validation failed", { details });
124502
+ logger65.warn("Create shop listing validation failed", { details });
124210
124503
  throw ApiError.unprocessableEntity("Validation failed", details);
124211
124504
  }
124212
124505
  throw ApiError.badRequest("Invalid JSON body");
124213
124506
  }
124214
- logger64.debug("Creating listing", {
124507
+ logger65.debug("Creating listing", {
124215
124508
  userId: ctx.user.id,
124216
124509
  itemId: body2.itemId,
124217
124510
  currencyId: body2.currencyId,
@@ -124234,12 +124527,12 @@ var init_shop_listing_controller = __esm(() => {
124234
124527
  } catch (error2) {
124235
124528
  if (error2 instanceof exports_external.ZodError) {
124236
124529
  const details = formatZodError(error2);
124237
- logger64.warn("Update shop listing validation failed", { details });
124530
+ logger65.warn("Update shop listing validation failed", { details });
124238
124531
  throw ApiError.unprocessableEntity("Validation failed", details);
124239
124532
  }
124240
124533
  throw ApiError.badRequest("Invalid JSON body");
124241
124534
  }
124242
- logger64.debug("Updating listing", {
124535
+ logger65.debug("Updating listing", {
124243
124536
  userId: ctx.user.id,
124244
124537
  listingId,
124245
124538
  price: body2.price,
@@ -124256,7 +124549,7 @@ var init_shop_listing_controller = __esm(() => {
124256
124549
  if (!isValidUUID(listingId)) {
124257
124550
  throw ApiError.unprocessableEntity("listingId must be a valid UUID format");
124258
124551
  }
124259
- logger64.debug("Deleting listing", { userId: ctx.user.id, listingId });
124552
+ logger65.debug("Deleting listing", { userId: ctx.user.id, listingId });
124260
124553
  await ctx.services.shopListing.delete(listingId);
124261
124554
  });
124262
124555
  listByGame2 = requireNonAnonymous(async (ctx) => {
@@ -124267,7 +124560,7 @@ var init_shop_listing_controller = __esm(() => {
124267
124560
  if (!isValidUUID(gameId)) {
124268
124561
  throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
124269
124562
  }
124270
- logger64.debug("Listing game listings", { userId: ctx.user.id, gameId });
124563
+ logger65.debug("Listing game listings", { userId: ctx.user.id, gameId });
124271
124564
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
124272
124565
  });
124273
124566
  getByGameItem = requireNonAnonymous(async (ctx) => {
@@ -124282,7 +124575,7 @@ var init_shop_listing_controller = __esm(() => {
124282
124575
  if (!isValidUUID(itemId)) {
124283
124576
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
124284
124577
  }
124285
- logger64.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
124578
+ logger65.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
124286
124579
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
124287
124580
  });
124288
124581
  createForGameItem = requireNonAnonymous(async (ctx) => {
@@ -124304,12 +124597,12 @@ var init_shop_listing_controller = __esm(() => {
124304
124597
  } catch (error2) {
124305
124598
  if (error2 instanceof exports_external.ZodError) {
124306
124599
  const details = formatZodError(error2);
124307
- logger64.warn("Create game item listing validation failed", { details });
124600
+ logger65.warn("Create game item listing validation failed", { details });
124308
124601
  throw ApiError.unprocessableEntity("Validation failed", details);
124309
124602
  }
124310
124603
  throw ApiError.badRequest("Invalid JSON body");
124311
124604
  }
124312
- logger64.debug("Creating game item listing", {
124605
+ logger65.debug("Creating game item listing", {
124313
124606
  userId: ctx.user.id,
124314
124607
  gameId,
124315
124608
  itemId,
@@ -124337,12 +124630,12 @@ var init_shop_listing_controller = __esm(() => {
124337
124630
  } catch (error2) {
124338
124631
  if (error2 instanceof exports_external.ZodError) {
124339
124632
  const details = formatZodError(error2);
124340
- logger64.warn("Update game item listing validation failed", { details });
124633
+ logger65.warn("Update game item listing validation failed", { details });
124341
124634
  throw ApiError.unprocessableEntity("Validation failed", details);
124342
124635
  }
124343
124636
  throw ApiError.badRequest("Invalid JSON body");
124344
124637
  }
124345
- logger64.debug("Updating game item listing", {
124638
+ logger65.debug("Updating game item listing", {
124346
124639
  userId: ctx.user.id,
124347
124640
  gameId,
124348
124641
  itemId,
@@ -124364,7 +124657,7 @@ var init_shop_listing_controller = __esm(() => {
124364
124657
  if (!isValidUUID(itemId)) {
124365
124658
  throw ApiError.unprocessableEntity("itemId must be a valid UUID format");
124366
124659
  }
124367
- logger64.debug("Deleting game item listing", {
124660
+ logger65.debug("Deleting game item listing", {
124368
124661
  userId: ctx.user.id,
124369
124662
  gameId,
124370
124663
  itemId
@@ -124389,20 +124682,20 @@ async function getBySlug2(ctx) {
124389
124682
  if (!slug2) {
124390
124683
  throw ApiError.badRequest("Template slug is required");
124391
124684
  }
124392
- logger65.debug("Getting sprite by slug", { slug: slug2 });
124685
+ logger66.debug("Getting sprite by slug", { slug: slug2 });
124393
124686
  return ctx.services.sprite.getBySlug(slug2);
124394
124687
  }
124395
- var logger65;
124688
+ var logger66;
124396
124689
  var sprites;
124397
124690
  var init_sprite_controller = __esm(() => {
124398
124691
  init_src2();
124399
124692
  init_errors();
124400
- logger65 = log.scope("SpriteController");
124693
+ logger66 = log.scope("SpriteController");
124401
124694
  sprites = {
124402
124695
  getBySlug: getBySlug2
124403
124696
  };
124404
124697
  });
124405
- var logger66;
124698
+ var logger67;
124406
124699
  var getTodayXp;
124407
124700
  var getTotalXp;
124408
124701
  var updateTodayXp;
@@ -124412,6 +124705,8 @@ var getUser;
124412
124705
  var getUserById;
124413
124706
  var setupIntegration;
124414
124707
  var getIntegrations;
124708
+ var updateIntegration;
124709
+ var getIntegrationConfig;
124415
124710
  var verifyIntegration;
124416
124711
  var getConfig2;
124417
124712
  var deleteIntegrations;
@@ -124427,6 +124722,7 @@ var getActivityDetail;
124427
124722
  var grantXp;
124428
124723
  var adjustTime;
124429
124724
  var adjustMastery;
124725
+ var reconcileMasteryForConfigChange;
124430
124726
  var searchStudents;
124431
124727
  var enrollStudent;
124432
124728
  var unenrollStudent;
@@ -124452,15 +124748,15 @@ var init_timeback_controller = __esm(() => {
124452
124748
  init_src4();
124453
124749
  init_errors();
124454
124750
  init_utils11();
124455
- logger66 = log.scope("TimebackController");
124751
+ logger67 = log.scope("TimebackController");
124456
124752
  getTodayXp = requireNonAnonymous(async (ctx) => {
124457
124753
  const date4 = ctx.url.searchParams.get("date") || undefined;
124458
124754
  const tz = ctx.url.searchParams.get("tz") || undefined;
124459
- logger66.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
124755
+ logger67.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
124460
124756
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
124461
124757
  });
124462
124758
  getTotalXp = requireNonAnonymous(async (ctx) => {
124463
- logger66.debug("Getting total XP", { userId: ctx.user.id });
124759
+ logger67.debug("Getting total XP", { userId: ctx.user.id });
124464
124760
  return ctx.services.timeback.getTotalXp(ctx.user.id);
124465
124761
  });
124466
124762
  updateTodayXp = requireNonAnonymous(async (ctx) => {
@@ -124471,18 +124767,18 @@ var init_timeback_controller = __esm(() => {
124471
124767
  } catch (error2) {
124472
124768
  if (error2 instanceof exports_external.ZodError) {
124473
124769
  const details = formatZodError(error2);
124474
- logger66.warn("Update today XP validation failed", { details });
124770
+ logger67.warn("Update today XP validation failed", { details });
124475
124771
  throw ApiError.unprocessableEntity("Validation failed", details);
124476
124772
  }
124477
124773
  throw ApiError.badRequest("Invalid JSON body");
124478
124774
  }
124479
- logger66.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
124775
+ logger67.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
124480
124776
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
124481
124777
  });
124482
124778
  getXpHistory = requireNonAnonymous(async (ctx) => {
124483
124779
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
124484
124780
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
124485
- logger66.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
124781
+ logger67.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
124486
124782
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
124487
124783
  });
124488
124784
  populateStudent = requireNonAnonymous(async (ctx) => {
@@ -124493,18 +124789,18 @@ var init_timeback_controller = __esm(() => {
124493
124789
  } catch (error2) {
124494
124790
  if (error2 instanceof exports_external.ZodError) {
124495
124791
  const details = formatZodError(error2);
124496
- logger66.warn("Populate student validation failed", { details });
124792
+ logger67.warn("Populate student validation failed", { details });
124497
124793
  throw ApiError.unprocessableEntity("Validation failed", details);
124498
124794
  }
124499
124795
  }
124500
- logger66.debug("Populating student", {
124796
+ logger67.debug("Populating student", {
124501
124797
  userId: ctx.user.id,
124502
124798
  hasProvidedNames: Boolean(providedNames)
124503
124799
  });
124504
124800
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
124505
124801
  });
124506
124802
  getUser = requireNonAnonymous(async (ctx) => {
124507
- logger66.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
124803
+ logger67.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
124508
124804
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
124509
124805
  });
124510
124806
  getUserById = requireNonAnonymous(async (ctx) => {
@@ -124512,7 +124808,7 @@ var init_timeback_controller = __esm(() => {
124512
124808
  if (!timebackId) {
124513
124809
  throw ApiError.badRequest("Missing timebackId parameter");
124514
124810
  }
124515
- logger66.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
124811
+ logger67.debug("Getting user by ID", { requesterId: ctx.user.id, timebackId });
124516
124812
  return ctx.services.timeback.getUserDataByTimebackId(timebackId);
124517
124813
  });
124518
124814
  setupIntegration = requireDeveloper(async (ctx) => {
@@ -124523,12 +124819,12 @@ var init_timeback_controller = __esm(() => {
124523
124819
  } catch (error2) {
124524
124820
  if (error2 instanceof exports_external.ZodError) {
124525
124821
  const details = formatZodError(error2);
124526
- logger66.warn("Setup integration validation failed", { details });
124822
+ logger67.warn("Setup integration validation failed", { details });
124527
124823
  throw ApiError.unprocessableEntity("Validation failed", details);
124528
124824
  }
124529
124825
  throw ApiError.badRequest("Invalid JSON body");
124530
124826
  }
124531
- logger66.debug("Setting up integration", {
124827
+ logger67.debug("Setting up integration", {
124532
124828
  userId: ctx.user.id,
124533
124829
  gameId: body2.gameId
124534
124830
  });
@@ -124542,9 +124838,37 @@ var init_timeback_controller = __esm(() => {
124542
124838
  if (!isValidUUID(gameId)) {
124543
124839
  throw ApiError.unprocessableEntity("Invalid gameId format");
124544
124840
  }
124545
- logger66.debug("Getting integrations", { userId: ctx.user.id, gameId });
124841
+ logger67.debug("Getting integrations", { userId: ctx.user.id, gameId });
124546
124842
  return ctx.services.timeback.getIntegrations(gameId, ctx.user);
124547
124843
  });
124844
+ updateIntegration = requireDeveloper(async (ctx) => {
124845
+ const { gameId, courseId } = ctx.params;
124846
+ if (!gameId || !courseId) {
124847
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
124848
+ }
124849
+ if (!isValidUUID(gameId)) {
124850
+ throw ApiError.unprocessableEntity("Invalid gameId format");
124851
+ }
124852
+ const body2 = await parseRequestBody(ctx.request, UpdateGameTimebackIntegrationRequestSchema);
124853
+ logger67.debug("Updating integration", {
124854
+ userId: ctx.user.id,
124855
+ gameId,
124856
+ courseId,
124857
+ fields: Object.keys(body2)
124858
+ });
124859
+ return ctx.services.timeback.updateIntegration(gameId, courseId, ctx.user, body2);
124860
+ });
124861
+ getIntegrationConfig = requireGameManagementAccess(async (ctx) => {
124862
+ const { gameId, courseId } = ctx.params;
124863
+ if (!gameId || !courseId) {
124864
+ throw ApiError.badRequest("Missing gameId or courseId parameter");
124865
+ }
124866
+ if (!isValidUUID(gameId)) {
124867
+ throw ApiError.unprocessableEntity("Invalid gameId format");
124868
+ }
124869
+ logger67.debug("Getting integration config", { userId: ctx.user.id, gameId, courseId });
124870
+ return ctx.services.timeback.getIntegrationConfig(gameId, courseId, ctx.user);
124871
+ });
124548
124872
  verifyIntegration = requireDeveloper(async (ctx) => {
124549
124873
  const gameId = ctx.params.gameId;
124550
124874
  if (!gameId) {
@@ -124553,7 +124877,7 @@ var init_timeback_controller = __esm(() => {
124553
124877
  if (!isValidUUID(gameId)) {
124554
124878
  throw ApiError.unprocessableEntity("Invalid gameId format");
124555
124879
  }
124556
- logger66.debug("Verifying integration", { userId: ctx.user.id, gameId });
124880
+ logger67.debug("Verifying integration", { userId: ctx.user.id, gameId });
124557
124881
  return ctx.services.timeback.verifyIntegration(gameId, ctx.user);
124558
124882
  });
124559
124883
  getConfig2 = requireDeveloper(async (ctx) => {
@@ -124564,7 +124888,7 @@ var init_timeback_controller = __esm(() => {
124564
124888
  if (!isValidUUID(gameId)) {
124565
124889
  throw ApiError.unprocessableEntity("Invalid gameId format");
124566
124890
  }
124567
- logger66.debug("Getting config", { userId: ctx.user.id, gameId });
124891
+ logger67.debug("Getting config", { userId: ctx.user.id, gameId });
124568
124892
  return ctx.services.timeback.getConfig(gameId, ctx.user);
124569
124893
  });
124570
124894
  deleteIntegrations = requireDeveloper(async (ctx) => {
@@ -124575,7 +124899,7 @@ var init_timeback_controller = __esm(() => {
124575
124899
  if (!isValidUUID(gameId)) {
124576
124900
  throw ApiError.unprocessableEntity("Invalid gameId format");
124577
124901
  }
124578
- logger66.debug("Deleting integrations", { userId: ctx.user.id, gameId });
124902
+ logger67.debug("Deleting integrations", { userId: ctx.user.id, gameId });
124579
124903
  await ctx.services.timeback.deleteIntegrations(gameId, ctx.user);
124580
124904
  });
124581
124905
  endActivity = requireDeveloper(async (ctx) => {
@@ -124586,7 +124910,7 @@ var init_timeback_controller = __esm(() => {
124586
124910
  } catch (error2) {
124587
124911
  if (error2 instanceof exports_external.ZodError) {
124588
124912
  const details = formatZodError(error2);
124589
- logger66.warn("End activity validation failed", { details });
124913
+ logger67.warn("End activity validation failed", { details });
124590
124914
  throw ApiError.unprocessableEntity("Validation failed", details);
124591
124915
  }
124592
124916
  throw ApiError.badRequest("Invalid JSON body");
@@ -124604,7 +124928,7 @@ var init_timeback_controller = __esm(() => {
124604
124928
  masteredUnits,
124605
124929
  extensions
124606
124930
  } = body2;
124607
- logger66.debug("Ending activity", { userId: ctx.user.id, gameId });
124931
+ logger67.debug("Ending activity", { userId: ctx.user.id, gameId });
124608
124932
  return ctx.services.timeback.endActivity({
124609
124933
  gameId,
124610
124934
  studentId,
@@ -124628,7 +124952,7 @@ var init_timeback_controller = __esm(() => {
124628
124952
  } catch (error2) {
124629
124953
  if (error2 instanceof exports_external.ZodError) {
124630
124954
  const details = formatZodError(error2);
124631
- logger66.warn("Heartbeat validation failed", { details });
124955
+ logger67.warn("Heartbeat validation failed", { details });
124632
124956
  throw ApiError.unprocessableEntity("Validation failed", details);
124633
124957
  }
124634
124958
  throw ApiError.badRequest("Invalid JSON body");
@@ -124644,7 +124968,7 @@ var init_timeback_controller = __esm(() => {
124644
124968
  windowSequence,
124645
124969
  isFinal
124646
124970
  } = body2;
124647
- logger66.debug("Recording heartbeat", {
124971
+ logger67.debug("Recording heartbeat", {
124648
124972
  userId: ctx.user.id,
124649
124973
  gameId,
124650
124974
  runId,
@@ -124669,7 +124993,7 @@ var init_timeback_controller = __esm(() => {
124669
124993
  });
124670
124994
  advanceCourse = requireDeveloper(async (ctx) => {
124671
124995
  const body2 = await parseRequestBody(ctx.request, AdvanceCourseRequestSchema);
124672
- logger66.debug("Advancing student manually", {
124996
+ logger67.debug("Advancing student manually", {
124673
124997
  userId: ctx.user.id,
124674
124998
  gameId: body2.gameId,
124675
124999
  studentId: body2.studentId,
@@ -124710,7 +125034,7 @@ var init_timeback_controller = __esm(() => {
124710
125034
  perCourse: includeOptions.includes("percourse"),
124711
125035
  today: includeOptions.includes("today")
124712
125036
  };
124713
- logger66.debug("Getting student XP", {
125037
+ logger67.debug("Getting student XP", {
124714
125038
  requesterId: ctx.user.id,
124715
125039
  timebackId,
124716
125040
  gameId,
@@ -124732,7 +125056,7 @@ var init_timeback_controller = __esm(() => {
124732
125056
  if (!gameId || !courseId) {
124733
125057
  throw ApiError.badRequest("Missing gameId or courseId parameter");
124734
125058
  }
124735
- logger66.debug("Getting course roster", {
125059
+ logger67.debug("Getting course roster", {
124736
125060
  requesterId: ctx.user.id,
124737
125061
  gameId,
124738
125062
  courseId,
@@ -124749,7 +125073,7 @@ var init_timeback_controller = __esm(() => {
124749
125073
  if (!timebackId || !gameId) {
124750
125074
  throw ApiError.badRequest("Missing timebackId parameter or gameId query parameter");
124751
125075
  }
124752
- logger66.debug("Getting student overview", {
125076
+ logger67.debug("Getting student overview", {
124753
125077
  requesterId: ctx.user.id,
124754
125078
  timebackId,
124755
125079
  gameId,
@@ -124763,7 +125087,7 @@ var init_timeback_controller = __esm(() => {
124763
125087
  if (!gameId || !timebackId) {
124764
125088
  throw ApiError.badRequest("Missing gameId or timebackId path parameter");
124765
125089
  }
124766
- logger66.debug("Getting game metrics", {
125090
+ logger67.debug("Getting game metrics", {
124767
125091
  requesterId: ctx.user.id,
124768
125092
  gameId,
124769
125093
  timebackId
@@ -124781,7 +125105,7 @@ var init_timeback_controller = __esm(() => {
124781
125105
  if (!timebackId || !courseId || !gameId) {
124782
125106
  throw ApiError.badRequest("Missing timebackId or courseId path parameter, or gameId query parameter");
124783
125107
  }
124784
- logger66.debug("Getting student activity", {
125108
+ logger67.debug("Getting student activity", {
124785
125109
  requesterId: ctx.user.id,
124786
125110
  timebackId,
124787
125111
  courseId,
@@ -124806,7 +125130,7 @@ var init_timeback_controller = __esm(() => {
124806
125130
  if (!timebackId || !courseId || !activityId || !gameId) {
124807
125131
  throw ApiError.badRequest("Missing timebackId, courseId, or activityId path parameter, or gameId query parameter");
124808
125132
  }
124809
- logger66.debug("Getting activity detail", {
125133
+ logger67.debug("Getting activity detail", {
124810
125134
  requesterId: ctx.user.id,
124811
125135
  timebackId,
124812
125136
  courseId,
@@ -124824,7 +125148,7 @@ var init_timeback_controller = __esm(() => {
124824
125148
  });
124825
125149
  grantXp = requireDeveloper(async (ctx) => {
124826
125150
  const body2 = await parseRequestBody(ctx.request, GrantTimebackXpRequestSchema);
124827
- logger66.debug("Granting manual XP", {
125151
+ logger67.debug("Granting manual XP", {
124828
125152
  requesterId: ctx.user.id,
124829
125153
  gameId: body2.gameId,
124830
125154
  courseId: body2.courseId,
@@ -124836,7 +125160,7 @@ var init_timeback_controller = __esm(() => {
124836
125160
  });
124837
125161
  adjustTime = requireDeveloper(async (ctx) => {
124838
125162
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackTimeRequestSchema);
124839
- logger66.debug("Adjusting time spent", {
125163
+ logger67.debug("Adjusting time spent", {
124840
125164
  requesterId: ctx.user.id,
124841
125165
  gameId: body2.gameId,
124842
125166
  courseId: body2.courseId,
@@ -124848,7 +125172,7 @@ var init_timeback_controller = __esm(() => {
124848
125172
  });
124849
125173
  adjustMastery = requireDeveloper(async (ctx) => {
124850
125174
  const body2 = await parseRequestBody(ctx.request, AdjustTimebackMasteryRequestSchema);
124851
- logger66.debug("Adjusting mastered units", {
125175
+ logger67.debug("Adjusting mastered units", {
124852
125176
  requesterId: ctx.user.id,
124853
125177
  gameId: body2.gameId,
124854
125178
  courseId: body2.courseId,
@@ -124858,6 +125182,22 @@ var init_timeback_controller = __esm(() => {
124858
125182
  });
124859
125183
  return ctx.services.timebackAdmin.adjustMasteredUnits(body2, ctx.user);
124860
125184
  });
125185
+ reconcileMasteryForConfigChange = requireDeveloper(async (ctx) => {
125186
+ const body2 = await parseRequestBody(ctx.request, ReconcileMasteryForConfigChangeSchema);
125187
+ logger67.debug("Reconciling mastery completion for config change", {
125188
+ requesterId: ctx.user.id,
125189
+ gameId: body2.gameId,
125190
+ courseId: body2.courseId,
125191
+ oldMasterableUnits: body2.oldMasterableUnits,
125192
+ newMasterableUnits: body2.newMasterableUnits,
125193
+ affectedCount: body2.affectedStudentIds.length
125194
+ });
125195
+ return ctx.services.timebackAdmin.reconcileMasteryForConfigChange(body2.gameId, body2.courseId, ctx.user, {
125196
+ oldMasterableUnits: body2.oldMasterableUnits,
125197
+ newMasterableUnits: body2.newMasterableUnits,
125198
+ affectedStudentIds: body2.affectedStudentIds
125199
+ });
125200
+ });
124861
125201
  searchStudents = requireGameManagementAccess(async (ctx) => {
124862
125202
  const gameId = ctx.params.gameId;
124863
125203
  const courseId = ctx.params.courseId;
@@ -124865,7 +125205,7 @@ var init_timeback_controller = __esm(() => {
124865
125205
  if (!gameId || !courseId) {
124866
125206
  throw ApiError.badRequest("Missing gameId or courseId parameter");
124867
125207
  }
124868
- logger66.debug("Searching students for enrollment", {
125208
+ logger67.debug("Searching students for enrollment", {
124869
125209
  requesterId: ctx.user.id,
124870
125210
  gameId,
124871
125211
  courseId,
@@ -124875,7 +125215,7 @@ var init_timeback_controller = __esm(() => {
124875
125215
  });
124876
125216
  enrollStudent = requireGameManagementAccess(async (ctx) => {
124877
125217
  const body2 = await parseRequestBody(ctx.request, EnrollStudentRequestSchema);
124878
- logger66.debug("Enrolling student", {
125218
+ logger67.debug("Enrolling student", {
124879
125219
  requesterId: ctx.user.id,
124880
125220
  gameId: body2.gameId,
124881
125221
  courseId: body2.courseId,
@@ -124885,7 +125225,7 @@ var init_timeback_controller = __esm(() => {
124885
125225
  });
124886
125226
  unenrollStudent = requireGameManagementAccess(async (ctx) => {
124887
125227
  const body2 = await parseRequestBody(ctx.request, UnenrollStudentRequestSchema);
124888
- logger66.debug("Unenrolling student", {
125228
+ logger67.debug("Unenrolling student", {
124889
125229
  requesterId: ctx.user.id,
124890
125230
  gameId: body2.gameId,
124891
125231
  courseId: body2.courseId,
@@ -124895,7 +125235,7 @@ var init_timeback_controller = __esm(() => {
124895
125235
  });
124896
125236
  reactivateEnrollment = requireGameManagementAccess(async (ctx) => {
124897
125237
  const body2 = await parseRequestBody(ctx.request, ReactivateEnrollmentRequestSchema);
124898
- logger66.debug("Reactivating enrollment", {
125238
+ logger67.debug("Reactivating enrollment", {
124899
125239
  requesterId: ctx.user.id,
124900
125240
  gameId: body2.gameId,
124901
125241
  courseId: body2.courseId,
@@ -125035,6 +125375,8 @@ var init_timeback_controller = __esm(() => {
125035
125375
  getUserById,
125036
125376
  setupIntegration,
125037
125377
  getIntegrations,
125378
+ updateIntegration,
125379
+ getIntegrationConfig,
125038
125380
  verifyIntegration,
125039
125381
  getConfig: getConfig2,
125040
125382
  deleteIntegrations,
@@ -125050,6 +125392,7 @@ var init_timeback_controller = __esm(() => {
125050
125392
  grantXp,
125051
125393
  adjustTime,
125052
125394
  adjustMastery,
125395
+ reconcileMasteryForConfigChange,
125053
125396
  searchStudents,
125054
125397
  enrollStudent,
125055
125398
  unenrollStudent,
@@ -125069,7 +125412,7 @@ var init_timeback_controller = __esm(() => {
125069
125412
  destroyAssessmentBank
125070
125413
  };
125071
125414
  });
125072
- var logger67;
125415
+ var logger68;
125073
125416
  var initiate;
125074
125417
  var init_upload_controller = __esm(() => {
125075
125418
  init_esm();
@@ -125077,7 +125420,7 @@ var init_upload_controller = __esm(() => {
125077
125420
  init_src2();
125078
125421
  init_errors();
125079
125422
  init_utils11();
125080
- logger67 = log.scope("UploadController");
125423
+ logger68 = log.scope("UploadController");
125081
125424
  initiate = requireDeveloper(async (ctx) => {
125082
125425
  let body2;
125083
125426
  try {
@@ -125086,16 +125429,16 @@ var init_upload_controller = __esm(() => {
125086
125429
  } catch (error2) {
125087
125430
  if (error2 instanceof exports_external.ZodError) {
125088
125431
  const details = formatZodError(error2);
125089
- logger67.warn("Initiate upload validation failed", { details });
125432
+ logger68.warn("Initiate upload validation failed", { details });
125090
125433
  throw ApiError.unprocessableEntity("Validation failed", details);
125091
125434
  }
125092
125435
  throw ApiError.badRequest("Invalid JSON body");
125093
125436
  }
125094
- logger67.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
125437
+ logger68.debug("Initiating upload", { userId: ctx.user.id, gameId: body2.gameId });
125095
125438
  return ctx.services.upload.initiate(body2, ctx.user);
125096
125439
  });
125097
125440
  });
125098
- var logger68;
125441
+ var logger69;
125099
125442
  var getMe;
125100
125443
  var getDemoProfile;
125101
125444
  var updateDemoProfile;
@@ -125104,18 +125447,18 @@ var init_user_controller = __esm(() => {
125104
125447
  init_schemas_index();
125105
125448
  init_src2();
125106
125449
  init_utils11();
125107
- logger68 = log.scope("UserController");
125450
+ logger69 = log.scope("UserController");
125108
125451
  getMe = requireNonAnonymous(async (ctx) => {
125109
- logger68.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
125452
+ logger69.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
125110
125453
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
125111
125454
  });
125112
125455
  getDemoProfile = requireAnonymous(async (ctx) => {
125113
- logger68.debug("Getting demo profile", { userId: ctx.user.id });
125456
+ logger69.debug("Getting demo profile", { userId: ctx.user.id });
125114
125457
  return ctx.services.user.getDemoProfile(ctx.user.id);
125115
125458
  });
125116
125459
  updateDemoProfile = requireAnonymous(async (ctx) => {
125117
125460
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
125118
- logger68.debug("Updating demo profile", {
125461
+ logger69.debug("Updating demo profile", {
125119
125462
  userId: ctx.user.id,
125120
125463
  displayName: body2.displayName
125121
125464
  });
@@ -125127,13 +125470,13 @@ var init_user_controller = __esm(() => {
125127
125470
  updateDemoProfile
125128
125471
  };
125129
125472
  });
125130
- var logger69;
125473
+ var logger70;
125131
125474
  var init_verify_controller = __esm(() => {
125132
125475
  init_schemas_index();
125133
125476
  init_src2();
125134
125477
  init_errors();
125135
125478
  init_utils11();
125136
- logger69 = log.scope("VerifyController");
125479
+ logger70 = log.scope("VerifyController");
125137
125480
  });
125138
125481
  var init_controllers = __esm(() => {
125139
125482
  init_achievement_controller();
@@ -125431,7 +125774,7 @@ var init_uploads = __esm(() => {
125431
125774
  gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
125432
125775
  gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
125433
125776
  });
125434
- var logger70;
125777
+ var logger71;
125435
125778
  var gameDeployRouter;
125436
125779
  var init_deploy = __esm(() => {
125437
125780
  init_drizzle_orm();
@@ -125442,7 +125785,7 @@ var init_deploy = __esm(() => {
125442
125785
  init_src2();
125443
125786
  init_api();
125444
125787
  init_uploads();
125445
- logger70 = log.scope("SandboxDeploy");
125788
+ logger71 = log.scope("SandboxDeploy");
125446
125789
  gameDeployRouter = new Hono2;
125447
125790
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
125448
125791
  const user = c2.get("user");
@@ -125568,7 +125911,7 @@ var init_deploy = __esm(() => {
125568
125911
  completedAt: now2
125569
125912
  };
125570
125913
  const [insertedJob] = await db2.insert(gameDeployJobs).values([jobValues]).returning();
125571
- logger70.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
125914
+ logger71.info("Mock deploy job completed", { jobId: insertedJob.id, slug: slug2 });
125572
125915
  return c2.json({
125573
125916
  id: insertedJob.id,
125574
125917
  status: "succeeded",
@@ -126166,7 +126509,7 @@ function verifyMockToken(idToken) {
126166
126509
  throw new Error("Invalid LTI token format");
126167
126510
  }
126168
126511
  }
126169
- var logger71;
126512
+ var logger72;
126170
126513
  var ltiRouter;
126171
126514
  var init_lti = __esm(() => {
126172
126515
  init_drizzle_orm();
@@ -126177,7 +126520,7 @@ var init_lti = __esm(() => {
126177
126520
  init_src2();
126178
126521
  init_constants();
126179
126522
  init_api();
126180
- logger71 = log.scope("SandboxLti");
126523
+ logger72 = log.scope("SandboxLti");
126181
126524
  ltiRouter = new Hono2;
126182
126525
  ltiRouter.post("/launch", async (c2) => {
126183
126526
  const db2 = c2.get("db");
@@ -126195,7 +126538,7 @@ var init_lti = __esm(() => {
126195
126538
  claims = verifyMockToken(idToken);
126196
126539
  } catch (error2) {
126197
126540
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
126198
- logger71.error("LTI token verification failed", { error: errorMessage });
126541
+ logger72.error("LTI token verification failed", { error: errorMessage });
126199
126542
  return c2.json({
126200
126543
  error: "invalid_token",
126201
126544
  message: errorMessage
@@ -126203,7 +126546,7 @@ var init_lti = __esm(() => {
126203
126546
  }
126204
126547
  const validationError = validateLtiClaims(claims);
126205
126548
  if (validationError) {
126206
- logger71.warn("LTI claims validation failed", {
126549
+ logger72.warn("LTI claims validation failed", {
126207
126550
  error: validationError,
126208
126551
  sub: claims.sub
126209
126552
  });
@@ -126223,7 +126566,7 @@ var init_lti = __esm(() => {
126223
126566
  createdAt: new Date,
126224
126567
  updatedAt: new Date
126225
126568
  });
126226
- logger71.info("LTI launch successful", { userId: user.id });
126569
+ logger72.info("LTI launch successful", { userId: user.id });
126227
126570
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
126228
126571
  const currentHost = new URL(c2.req.url).hostname;
126229
126572
  const redirectPath = extractRedirectPath(targetUri, currentHost);
@@ -126231,7 +126574,7 @@ var init_lti = __esm(() => {
126231
126574
  return c2.redirect(redirectPath);
126232
126575
  } catch (error2) {
126233
126576
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
126234
- logger71.error("Unexpected error during LTI launch", { error: errorMessage });
126577
+ logger72.error("Unexpected error during LTI launch", { error: errorMessage });
126235
126578
  return c2.json({
126236
126579
  error: "unexpected_error",
126237
126580
  message: "An unexpected error occurred during LTI launch"
@@ -127905,7 +128248,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
127905
128248
  // package.json
127906
128249
  var package_default2 = {
127907
128250
  name: "@playcademy/vite-plugin",
127908
- version: "0.2.34",
128251
+ version: "0.2.35-beta.2",
127909
128252
  type: "module",
127910
128253
  exports: {
127911
128254
  ".": {