@playcademy/vite-plugin 0.2.22-beta.3 → 0.2.22-beta.5

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 +321 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25334,7 +25334,7 @@ var package_default;
25334
25334
  var init_package = __esm(() => {
25335
25335
  package_default = {
25336
25336
  name: "@playcademy/sandbox",
25337
- version: "0.3.16-beta.3",
25337
+ version: "0.3.16-beta.5",
25338
25338
  description: "Local development server for Playcademy game development",
25339
25339
  type: "module",
25340
25340
  exports: {
@@ -50501,9 +50501,37 @@ var init_developer_service = __esm(() => {
50501
50501
 
50502
50502
  class GameService {
50503
50503
  deps;
50504
+ static MANIFEST_FETCH_TIMEOUT_MS = 5000;
50505
+ static MAX_FETCH_ERROR_MESSAGE_LENGTH = 512;
50504
50506
  constructor(deps) {
50505
50507
  this.deps = deps;
50506
50508
  }
50509
+ static getManifestHost(manifestUrl) {
50510
+ try {
50511
+ return new URL(manifestUrl).host;
50512
+ } catch {
50513
+ return manifestUrl;
50514
+ }
50515
+ }
50516
+ static getFetchErrorMessage(error) {
50517
+ let raw;
50518
+ if (error instanceof Error) {
50519
+ raw = error.message;
50520
+ } else if (typeof error === "string") {
50521
+ raw = error;
50522
+ }
50523
+ if (!raw) {
50524
+ return;
50525
+ }
50526
+ const normalized = raw.replace(/\s+/g, " ").trim();
50527
+ if (!normalized) {
50528
+ return;
50529
+ }
50530
+ return normalized.slice(0, GameService.MAX_FETCH_ERROR_MESSAGE_LENGTH);
50531
+ }
50532
+ static isRetryableStatus(status) {
50533
+ return status === 429 || status >= 500;
50534
+ }
50507
50535
  async list(caller) {
50508
50536
  const db2 = this.deps.db;
50509
50537
  const isAdmin = caller?.role === "admin";
@@ -50565,6 +50593,109 @@ class GameService {
50565
50593
  this.enforceVisibility(game, caller, slug);
50566
50594
  return game;
50567
50595
  }
50596
+ async getManifest(gameId, caller) {
50597
+ const game = await this.getById(gameId, caller);
50598
+ if (game.gameType !== "hosted" || !game.deploymentUrl) {
50599
+ throw new BadRequestError("Game does not have a deployment manifest");
50600
+ }
50601
+ const deploymentUrl = game.deploymentUrl;
50602
+ const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
50603
+ const manifestHost = GameService.getManifestHost(manifestUrl);
50604
+ const startedAt = Date.now();
50605
+ const controller = new AbortController;
50606
+ const timeout = setTimeout(() => controller.abort(), GameService.MANIFEST_FETCH_TIMEOUT_MS);
50607
+ function buildDetails(fetchOutcome, manifestErrorKind, extra = {}) {
50608
+ return {
50609
+ manifestUrl,
50610
+ manifestHost,
50611
+ deploymentUrl,
50612
+ fetchOutcome,
50613
+ retryCount: 0,
50614
+ durationMs: Date.now() - startedAt,
50615
+ manifestErrorKind,
50616
+ ...extra
50617
+ };
50618
+ }
50619
+ let response;
50620
+ try {
50621
+ response = await fetch(manifestUrl, {
50622
+ method: "GET",
50623
+ headers: {
50624
+ Accept: "application/json"
50625
+ },
50626
+ signal: controller.signal
50627
+ });
50628
+ } catch (error) {
50629
+ clearTimeout(timeout);
50630
+ const fetchErrorMessage = GameService.getFetchErrorMessage(error);
50631
+ const details = buildDetails("network_error", "temporary", fetchErrorMessage ? { fetchErrorMessage } : {});
50632
+ logger5.error("Failed to fetch game manifest", {
50633
+ gameId,
50634
+ manifestUrl,
50635
+ error,
50636
+ details
50637
+ });
50638
+ if (error instanceof Error && error.name === "AbortError") {
50639
+ throw new TimeoutError("Timed out loading game manifest", details);
50640
+ }
50641
+ throw new ServiceUnavailableError("Failed to load game manifest", details);
50642
+ } finally {
50643
+ clearTimeout(timeout);
50644
+ }
50645
+ if (!response.ok) {
50646
+ const resolvedManifestUrl = response.url || manifestUrl;
50647
+ const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
50648
+ const manifestErrorKind = GameService.isRetryableStatus(response.status) ? "temporary" : "permanent";
50649
+ const details = buildDetails("bad_status", manifestErrorKind, {
50650
+ manifestUrl: resolvedManifestUrl,
50651
+ manifestHost: resolvedManifestHost,
50652
+ status: response.status,
50653
+ contentType: response.headers.get("content-type") ?? undefined,
50654
+ cfRay: response.headers.get("cf-ray") ?? undefined,
50655
+ redirected: response.redirected,
50656
+ ...response.redirected ? {
50657
+ originalManifestUrl: manifestUrl,
50658
+ originalManifestHost: manifestHost
50659
+ } : {}
50660
+ });
50661
+ const message = `Failed to fetch manifest: ${response.status} ${response.statusText}`;
50662
+ logger5.error("Game manifest returned non-ok response", {
50663
+ gameId,
50664
+ manifestUrl,
50665
+ status: response.status,
50666
+ details
50667
+ });
50668
+ if (manifestErrorKind === "temporary") {
50669
+ throw new ServiceUnavailableError(message, details);
50670
+ }
50671
+ throw new BadRequestError(message, details);
50672
+ }
50673
+ try {
50674
+ return await response.json();
50675
+ } catch (error) {
50676
+ const resolvedManifestUrl = response.url || manifestUrl;
50677
+ const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
50678
+ const details = buildDetails("invalid_body", "permanent", {
50679
+ manifestUrl: resolvedManifestUrl,
50680
+ manifestHost: resolvedManifestHost,
50681
+ status: response.status,
50682
+ contentType: response.headers.get("content-type") ?? undefined,
50683
+ cfRay: response.headers.get("cf-ray") ?? undefined,
50684
+ redirected: response.redirected,
50685
+ ...response.redirected ? {
50686
+ originalManifestUrl: manifestUrl,
50687
+ originalManifestHost: manifestHost
50688
+ } : {}
50689
+ });
50690
+ logger5.error("Failed to parse game manifest", {
50691
+ gameId,
50692
+ manifestUrl,
50693
+ error,
50694
+ details
50695
+ });
50696
+ throw new BadRequestError("Failed to parse game manifest", details);
50697
+ }
50698
+ }
50568
50699
  enforceVisibility(game, caller, lookupIdentifier) {
50569
50700
  if (game.visibility !== "internal") {
50570
50701
  return;
@@ -54095,16 +54226,7 @@ function mapAssessmentResultToRecentActivity(assessment, relevantCourseIds, cour
54095
54226
  return null;
54096
54227
  }
54097
54228
  if (isMasteryCompletionEntry(assessment)) {
54098
- const metadata3 = isRecord2(assessment.metadata) ? assessment.metadata : undefined;
54099
- const isResume = Boolean(metadata3?.resumedAt);
54100
- return {
54101
- id: assessment.sourcedId || `${assessment.assessmentLineItem.sourcedId}:${assessment.scoreDate}`,
54102
- kind: "admin-completion",
54103
- occurredAt: assessment.scoreDate,
54104
- courseId,
54105
- title: isResume ? "Course resumed" : "Course marked complete",
54106
- reason: metadata3?.adminAction ? "Admin action" : undefined
54107
- };
54229
+ return null;
54108
54230
  }
54109
54231
  const metadata2 = isRecord2(assessment.metadata) ? assessment.metadata : undefined;
54110
54232
  const activityName = getStringValue(metadata2?.activityName);
@@ -54137,6 +54259,7 @@ function parseCaliperEventContext(event, relevantCourseIds) {
54137
54259
  courseId,
54138
54260
  occurredAt,
54139
54261
  eventKind: getStringValue(playcademy?.eventKind),
54262
+ source: getStringValue(playcademy?.source),
54140
54263
  reason: getStringValue(playcademy?.reason),
54141
54264
  titleFromEvent: getStringValue(event.object.activity?.name),
54142
54265
  appName: getStringValue(event.object.app?.name),
@@ -54160,30 +54283,59 @@ function mapTimeSpentRemediation(event, ctx) {
54160
54283
  };
54161
54284
  }
54162
54285
  function mapActivityRemediation(event, ctx) {
54163
- let kind;
54164
54286
  if (ctx.eventKind === "remediation-xp") {
54165
- kind = "remediation-xp";
54166
- } else if (ctx.eventKind === "remediation-mastery") {
54167
- kind = "remediation-mastery";
54168
- } else {
54169
- return null;
54287
+ return {
54288
+ id: event.externalId,
54289
+ kind: "remediation-xp",
54290
+ occurredAt: ctx.occurredAt,
54291
+ courseId: ctx.courseId,
54292
+ title: "XP Adjustment",
54293
+ activityId: ctx.activityId,
54294
+ appName: ctx.appName,
54295
+ reason: ctx.reason,
54296
+ xpDelta: getGeneratedMetricValue(event, "xpEarned"),
54297
+ masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
54298
+ };
54170
54299
  }
54171
- const titleMap = {
54172
- "remediation-xp": "XP Adjustment",
54173
- "remediation-mastery": "Mastery Adjustment"
54174
- };
54175
- return {
54176
- id: event.externalId,
54177
- kind,
54178
- occurredAt: ctx.occurredAt,
54179
- courseId: ctx.courseId,
54180
- title: titleMap[kind] || "Remediation Activity",
54181
- activityId: ctx.activityId,
54182
- appName: ctx.appName,
54183
- reason: ctx.reason,
54184
- xpDelta: getGeneratedMetricValue(event, "xpEarned"),
54185
- masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
54186
- };
54300
+ if (ctx.eventKind === "remediation-mastery") {
54301
+ return {
54302
+ id: event.externalId,
54303
+ kind: "remediation-mastery",
54304
+ occurredAt: ctx.occurredAt,
54305
+ courseId: ctx.courseId,
54306
+ title: "Mastery Adjustment",
54307
+ activityId: ctx.activityId,
54308
+ appName: ctx.appName,
54309
+ reason: ctx.reason,
54310
+ xpDelta: getGeneratedMetricValue(event, "xpEarned"),
54311
+ masteredUnitsDelta: getGeneratedMetricValue(event, "masteredUnits")
54312
+ };
54313
+ }
54314
+ if (ctx.eventKind === "course-completed") {
54315
+ return {
54316
+ id: event.externalId,
54317
+ kind: "course-completed",
54318
+ occurredAt: ctx.occurredAt,
54319
+ courseId: ctx.courseId,
54320
+ title: ctx.source === "admin" ? "Course marked complete" : "Course completed",
54321
+ activityId: ctx.activityId,
54322
+ appName: ctx.appName,
54323
+ reason: ctx.reason
54324
+ };
54325
+ }
54326
+ if (ctx.eventKind === "course-resumed") {
54327
+ return {
54328
+ id: event.externalId,
54329
+ kind: "course-resumed",
54330
+ occurredAt: ctx.occurredAt,
54331
+ courseId: ctx.courseId,
54332
+ title: "Course resumed",
54333
+ activityId: ctx.activityId,
54334
+ appName: ctx.appName,
54335
+ reason: ctx.reason
54336
+ };
54337
+ }
54338
+ return null;
54187
54339
  }
54188
54340
  function mapCaliperEventToRemediationActivity(event, relevantCourseIds) {
54189
54341
  const ctx = parseCaliperEventContext(event, relevantCourseIds);
@@ -54204,6 +54356,7 @@ var init_timeback_util = __esm(() => {
54204
54356
 
54205
54357
  class TimebackAdminService {
54206
54358
  deps;
54359
+ static XP_PRECISION_FACTOR = 10;
54207
54360
  static RECENT_ACTIVITY_LIMIT = 20;
54208
54361
  static MAX_STUDENT_ACTIVITY_LIMIT = 200;
54209
54362
  static MAX_STUDENT_ACTIVITY_OFFSET = 1000;
@@ -54215,6 +54368,10 @@ class TimebackAdminService {
54215
54368
  constructor(deps) {
54216
54369
  this.deps = deps;
54217
54370
  }
54371
+ static roundXpToTenths(value) {
54372
+ const rounded = Math.round(value * TimebackAdminService.XP_PRECISION_FACTOR) / TimebackAdminService.XP_PRECISION_FACTOR;
54373
+ return Object.is(rounded, -0) ? 0 : rounded;
54374
+ }
54218
54375
  requireClient() {
54219
54376
  if (!this.deps.timeback) {
54220
54377
  logger16.error("Timeback client not available in context");
@@ -54222,6 +54379,17 @@ class TimebackAdminService {
54222
54379
  }
54223
54380
  return this.deps.timeback;
54224
54381
  }
54382
+ async recordCourseCompletionHistory(client, data) {
54383
+ await client.recordAdminCourseCompletionChange(data).catch((error) => {
54384
+ logger16.error("Failed to record admin course completion history event", {
54385
+ gameId: data.gameId,
54386
+ courseId: data.courseId,
54387
+ studentId: data.studentId,
54388
+ action: data.action,
54389
+ error: error instanceof Error ? error.message : String(error)
54390
+ });
54391
+ });
54392
+ }
54225
54393
  async resolveAdminMutationContext(gameId, courseId, user, studentId) {
54226
54394
  const client = this.requireClient();
54227
54395
  await this.deps.validateDeveloperAccess(user, gameId);
@@ -54261,8 +54429,8 @@ class TimebackAdminService {
54261
54429
  }
54262
54430
  const today = formatDateYMD();
54263
54431
  const history = [];
54264
- let totalXp = 0;
54265
- let todayXp = 0;
54432
+ let totalXpRaw = 0;
54433
+ let todayXpRaw = 0;
54266
54434
  let activeTimeSeconds = 0;
54267
54435
  let masteredUnits = 0;
54268
54436
  for (const [date3, subjectFacts] of Object.entries(facts)) {
@@ -54279,15 +54447,16 @@ class TimebackAdminService {
54279
54447
  masteredUnitsForDay += masteredUnitsFromFact;
54280
54448
  }
54281
54449
  }
54282
- totalXp += xpForDay;
54450
+ const roundedXpForDay = TimebackAdminService.roundXpToTenths(xpForDay);
54451
+ totalXpRaw += xpForDay;
54283
54452
  activeTimeSeconds += activeSecondsForDay;
54284
54453
  masteredUnits += masteredUnitsForDay;
54285
54454
  if (date3 === today) {
54286
- todayXp += xpForDay;
54455
+ todayXpRaw += xpForDay;
54287
54456
  }
54288
54457
  history.push({
54289
54458
  date: date3,
54290
- xpEarned: xpForDay,
54459
+ xpEarned: roundedXpForDay,
54291
54460
  activeTimeSeconds: activeSecondsForDay,
54292
54461
  masteredUnits: masteredUnitsForDay
54293
54462
  });
@@ -54295,8 +54464,8 @@ class TimebackAdminService {
54295
54464
  history.sort((a, b) => a.date.localeCompare(b.date));
54296
54465
  return {
54297
54466
  analyticsAvailable: true,
54298
- totalXp,
54299
- todayXp,
54467
+ totalXp: TimebackAdminService.roundXpToTenths(totalXpRaw),
54468
+ todayXp: TimebackAdminService.roundXpToTenths(todayXpRaw),
54300
54469
  activeTimeSeconds,
54301
54470
  masteredUnits,
54302
54471
  history
@@ -54638,6 +54807,7 @@ class TimebackAdminService {
54638
54807
  }
54639
54808
  async toggleCourseCompletion(data, user) {
54640
54809
  const { client, sensorUrl, appName, actor } = await this.resolveAdminMutationContext(data.gameId, data.courseId, user, data.studentId);
54810
+ const historyClient = client;
54641
54811
  const ids = deriveSourcedIds(data.courseId);
54642
54812
  const lineItemId = `${ids.course}-mastery-completion-assessment`;
54643
54813
  const resultId = `${lineItemId}:${data.studentId}:completion`;
@@ -54688,11 +54858,19 @@ class TimebackAdminService {
54688
54858
  inProgress: "false",
54689
54859
  metadata: {
54690
54860
  isMasteryCompletion: true,
54691
- completedAt: new Date().toISOString(),
54692
54861
  adminAction: true,
54693
54862
  appName
54694
54863
  }
54695
54864
  });
54865
+ await this.recordCourseCompletionHistory(historyClient, {
54866
+ gameId: data.gameId,
54867
+ courseId: data.courseId,
54868
+ studentId: data.studentId,
54869
+ action: "complete",
54870
+ actor,
54871
+ appName,
54872
+ sensorUrl
54873
+ });
54696
54874
  } else {
54697
54875
  await client.oneroster.assessmentResults.upsert(resultId, {
54698
54876
  sourcedId: resultId,
@@ -54705,11 +54883,19 @@ class TimebackAdminService {
54705
54883
  inProgress: "true",
54706
54884
  metadata: {
54707
54885
  isMasteryCompletion: true,
54708
- resumedAt: new Date().toISOString(),
54709
54886
  adminAction: true,
54710
54887
  appName
54711
54888
  }
54712
54889
  });
54890
+ await this.recordCourseCompletionHistory(historyClient, {
54891
+ gameId: data.gameId,
54892
+ courseId: data.courseId,
54893
+ studentId: data.studentId,
54894
+ action: "resume",
54895
+ actor,
54896
+ appName,
54897
+ sensorUrl
54898
+ });
54713
54899
  }
54714
54900
  return { status: "ok" };
54715
54901
  }
@@ -58934,7 +59120,8 @@ function buildAdminEventMetadata({
58934
59120
  return {
58935
59121
  playcademy: {
58936
59122
  eventKind,
58937
- reason
59123
+ reason,
59124
+ source: "admin"
58938
59125
  }
58939
59126
  };
58940
59127
  }
@@ -59050,6 +59237,30 @@ class AdminEventRecorder {
59050
59237
  eventExtensions: ctx.metadata
59051
59238
  });
59052
59239
  }
59240
+ async recordCourseCompletionChange(data) {
59241
+ const isResume = data.action === "resume";
59242
+ const ctx = await this.prepareAdminEvent({
59243
+ ...data,
59244
+ defaultActivityId: isResume ? "playcademy-admin-course-resumed" : "playcademy-admin-course-completed",
59245
+ reason: "Admin action",
59246
+ eventKind: isResume ? "course-resumed" : "course-completed"
59247
+ });
59248
+ await this.caliper.emitActivityEvent({
59249
+ studentId: ctx.student.id,
59250
+ studentEmail: ctx.student.email,
59251
+ activityId: ctx.activityId,
59252
+ activityName: isResume ? "Course resumed" : "Course marked complete",
59253
+ courseId: data.courseId,
59254
+ courseName: ctx.courseContext.courseName,
59255
+ subject: ctx.courseContext.subject,
59256
+ appName: ctx.appName,
59257
+ sensorUrl: ctx.sensorUrl,
59258
+ process: false,
59259
+ includeAttempt: false,
59260
+ generatedExtensions: ctx.metadata,
59261
+ eventExtensions: ctx.metadata
59262
+ });
59263
+ }
59053
59264
  }
59054
59265
 
59055
59266
  class TimebackCache {
@@ -59263,7 +59474,7 @@ class MasteryTracker {
59263
59474
  const totalMastered = historicalMasteredUnits + masteredUnits;
59264
59475
  const rawPct = totalMastered / masterableUnits * 100;
59265
59476
  const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
59266
- const masteryAchieved = totalMastered >= masterableUnits;
59477
+ const masteryAchieved = historicalMasteredUnits < masterableUnits && totalMastered >= masterableUnits;
59267
59478
  return { pctCompleteApp, masteryAchieved };
59268
59479
  }
59269
59480
  async createCompletionEntry(studentId, courseId, classId, appName) {
@@ -59289,7 +59500,6 @@ class MasteryTracker {
59289
59500
  inProgress: "false",
59290
59501
  metadata: {
59291
59502
  isMasteryCompletion: true,
59292
- completedAt: new Date().toISOString(),
59293
59503
  appName
59294
59504
  }
59295
59505
  });
@@ -59505,6 +59715,16 @@ class ProgressRecorder {
59505
59715
  }
59506
59716
  if (masteryAchieved) {
59507
59717
  await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
59718
+ await this.emitCourseCompletionHistoryEvent({
59719
+ studentId,
59720
+ studentEmail,
59721
+ activityId,
59722
+ courseId: ids.course,
59723
+ courseName,
59724
+ subject: progressData.subject,
59725
+ appName: progressData.appName,
59726
+ sensorUrl: progressData.sensorUrl
59727
+ });
59508
59728
  }
59509
59729
  await this.emitCaliperEvent({
59510
59730
  studentId,
@@ -59679,6 +59899,38 @@ class ProgressRecorder {
59679
59899
  log.error("[ProgressRecorder] Failed to emit activity event", { error });
59680
59900
  });
59681
59901
  }
59902
+ async emitCourseCompletionHistoryEvent(data) {
59903
+ await this.caliperNamespace.emitActivityEvent({
59904
+ studentId: data.studentId,
59905
+ studentEmail: data.studentEmail,
59906
+ activityId: data.activityId,
59907
+ activityName: "Course completed",
59908
+ courseId: data.courseId,
59909
+ courseName: data.courseName,
59910
+ subject: data.subject,
59911
+ appName: data.appName,
59912
+ sensorUrl: data.sensorUrl,
59913
+ process: false,
59914
+ includeAttempt: false,
59915
+ eventExtensions: {
59916
+ playcademy: {
59917
+ eventKind: "course-completed",
59918
+ source: "gameplay"
59919
+ }
59920
+ },
59921
+ generatedExtensions: {
59922
+ playcademy: {
59923
+ eventKind: "course-completed",
59924
+ source: "gameplay",
59925
+ activityId: data.activityId
59926
+ }
59927
+ }
59928
+ }).catch((error) => {
59929
+ log.error("[ProgressRecorder] Failed to emit course completion history event", {
59930
+ error
59931
+ });
59932
+ });
59933
+ }
59682
59934
  }
59683
59935
 
59684
59936
  class SessionRecorder {
@@ -60073,6 +60325,10 @@ class TimebackClient {
60073
60325
  await this._ensureAuthenticated();
60074
60326
  return this.adminEventRecorder.recordMasteryAdjustment(data);
60075
60327
  }
60328
+ async recordAdminCourseCompletionChange(data) {
60329
+ await this._ensureAuthenticated();
60330
+ return this.adminEventRecorder.recordCourseCompletionChange(data);
60331
+ }
60076
60332
  clearCaches() {
60077
60333
  this.cacheManager.clearAll();
60078
60334
  }
@@ -120597,6 +120853,7 @@ var listManageable;
120597
120853
  var getSubjects;
120598
120854
  var getById2;
120599
120855
  var getBySlug;
120856
+ var getManifest;
120600
120857
  var upsertBySlug;
120601
120858
  var remove3;
120602
120859
  var games2;
@@ -120639,6 +120896,21 @@ var init_game_controller = __esm(() => {
120639
120896
  logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
120640
120897
  return ctx.services.game.getBySlug(slug2, ctx.user);
120641
120898
  });
120899
+ getManifest = requireAuth(async (ctx) => {
120900
+ const gameId = ctx.params.gameId;
120901
+ if (!gameId) {
120902
+ throw ApiError.badRequest("Missing game ID");
120903
+ }
120904
+ if (!isValidUUID(gameId)) {
120905
+ throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
120906
+ }
120907
+ logger46.debug("Getting game manifest by ID", {
120908
+ userId: ctx.user.id,
120909
+ gameId,
120910
+ launchId: ctx.launchId
120911
+ });
120912
+ return ctx.services.game.getManifest(gameId, ctx.user);
120913
+ });
120642
120914
  upsertBySlug = requireAuth(async (ctx) => {
120643
120915
  const slug2 = ctx.params.slug;
120644
120916
  if (!slug2) {
@@ -120675,6 +120947,7 @@ var init_game_controller = __esm(() => {
120675
120947
  listManageable,
120676
120948
  getSubjects,
120677
120949
  getById: getById2,
120950
+ getManifest,
120678
120951
  getBySlug,
120679
120952
  upsertBySlug,
120680
120953
  remove: remove3
@@ -122549,7 +122822,8 @@ var init_crud = __esm(() => {
122549
122822
  init_api();
122550
122823
  gameCrudRouter = new Hono2;
122551
122824
  gameCrudRouter.get("/", handle2(games2.list));
122552
- gameCrudRouter.get("/:gameId{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}", handle2(games2.getById));
122825
+ gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}/manifest", handle2(games2.getManifest));
122826
+ gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}", handle2(games2.getById));
122553
122827
  gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
122554
122828
  gameCrudRouter.put("/:slug", handle2(games2.upsertBySlug));
122555
122829
  gameCrudRouter.delete("/:gameId", handle2(games2.remove, { status: 204 }));
@@ -124862,7 +125136,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
124862
125136
  // package.json
124863
125137
  var package_default2 = {
124864
125138
  name: "@playcademy/vite-plugin",
124865
- version: "0.2.22-beta.3",
125139
+ version: "0.2.22-beta.5",
124866
125140
  type: "module",
124867
125141
  exports: {
124868
125142
  ".": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "0.2.22-beta.3",
3
+ "version": "0.2.22-beta.5",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {