@playcademy/vite-plugin 0.2.24 → 0.2.25-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23954,6 +23954,20 @@ import fs7 from "node:fs";
23954
23954
  import path4 from "node:path";
23955
23955
  import { CONFIG_FILE_NAMES } from "playcademy/constants";
23956
23956
 
23957
+ // src/server/modes.ts
23958
+ function isShellMode(mode) {
23959
+ return mode !== "standalone";
23960
+ }
23961
+ function getNextMode(mode) {
23962
+ if (mode === "platform") {
23963
+ return "demo";
23964
+ }
23965
+ if (mode === "demo") {
23966
+ return "standalone";
23967
+ }
23968
+ return "platform";
23969
+ }
23970
+
23957
23971
  // src/server/recreate-sandbox.ts
23958
23972
  var import_picocolors7 = __toESM(require_picocolors(), 1);
23959
23973
 
@@ -24306,6 +24320,7 @@ var init_achievements = __esm(() => {
24306
24320
  ];
24307
24321
  });
24308
24322
  var AUTH_PROVIDER_IDS;
24323
+ var DEMO_DISPLAY_NAME_PLACEHOLDER = "Demo Player";
24309
24324
  var init_auth = __esm(() => {
24310
24325
  AUTH_PROVIDER_IDS = {
24311
24326
  TIMEBACK: "timeback",
@@ -24391,7 +24406,8 @@ var init_overworld = __esm(() => {
24391
24406
  ERROR: 4000
24392
24407
  };
24393
24408
  CORE_GAME_UUIDS = {
24394
- PLAYGROUND: "00000000-0000-0000-0000-000000000001"
24409
+ PLAYGROUND: "00000000-0000-0000-0000-000000000001",
24410
+ DEMO: "00000000-0000-0000-0000-000000000002"
24395
24411
  };
24396
24412
  });
24397
24413
  var PLATFORM_TIMEZONE = "America/New_York";
@@ -24471,13 +24487,15 @@ var DEMO_USER_IDS;
24471
24487
  var DEMO_USERS;
24472
24488
  var DEMO_USER;
24473
24489
  var init_demo_users = __esm(() => {
24490
+ init_src();
24474
24491
  now = new Date;
24475
24492
  DEMO_USER_IDS = {
24476
24493
  player: "00000000-0000-0000-0000-000000000001",
24477
24494
  developer: "00000000-0000-0000-0000-000000000002",
24478
24495
  admin: "00000000-0000-0000-0000-000000000003",
24479
24496
  pendingDeveloper: "00000000-0000-0000-0000-000000000004",
24480
- unverifiedPlayer: "00000000-0000-0000-0000-000000000005"
24497
+ unverifiedPlayer: "00000000-0000-0000-0000-000000000005",
24498
+ anonymousPlayer: "00000000-0000-0000-0000-000000000006"
24481
24499
  };
24482
24500
  DEMO_USERS = {
24483
24501
  admin: {
@@ -24539,6 +24557,19 @@ var init_demo_users = __esm(() => {
24539
24557
  developerStatus: "none",
24540
24558
  createdAt: now,
24541
24559
  updatedAt: now
24560
+ },
24561
+ anonymousPlayer: {
24562
+ id: DEMO_USER_IDS.anonymousPlayer,
24563
+ name: DEMO_DISPLAY_NAME_PLACEHOLDER,
24564
+ username: "anonymous_demo_player",
24565
+ email: "player@anon.demo.playcademy.gg",
24566
+ emailVerified: false,
24567
+ image: null,
24568
+ isAnonymous: true,
24569
+ role: "player",
24570
+ developerStatus: "none",
24571
+ createdAt: now,
24572
+ updatedAt: now
24542
24573
  }
24543
24574
  };
24544
24575
  DEMO_USER = DEMO_USERS.player;
@@ -24553,6 +24584,7 @@ var init_demo_tokens = __esm(() => {
24553
24584
  "sandbox-demo-token": DEMO_USERS.player,
24554
24585
  "sandbox-admin-token": DEMO_USERS.admin,
24555
24586
  "sandbox-player-token": DEMO_USERS.player,
24587
+ "sandbox-anonymous-token": DEMO_USERS.anonymousPlayer,
24556
24588
  "sandbox-developer-token": DEMO_USERS.developer,
24557
24589
  "sandbox-pending-dev-token": DEMO_USERS.pendingDeveloper,
24558
24590
  "sandbox-unverified-token": DEMO_USERS.unverifiedPlayer,
@@ -24561,6 +24593,7 @@ var init_demo_tokens = __esm(() => {
24561
24593
  };
24562
24594
  SANDBOX_TOKENS = {
24563
24595
  player: "sandbox-player-token",
24596
+ anonymous: "sandbox-anonymous-token",
24564
24597
  developer: "sandbox-developer-token",
24565
24598
  admin: "sandbox-admin-token",
24566
24599
  pendingDeveloper: "sandbox-pending-dev-token",
@@ -25336,7 +25369,7 @@ var package_default;
25336
25369
  var init_package = __esm(() => {
25337
25370
  package_default = {
25338
25371
  name: "@playcademy/sandbox",
25339
- version: "0.3.16",
25372
+ version: "0.3.17-beta.14",
25340
25373
  description: "Local development server for Playcademy game development",
25341
25374
  type: "module",
25342
25375
  exports: {
@@ -35621,6 +35654,7 @@ var init_table6 = __esm(() => {
35621
35654
  name: text("name").notNull(),
35622
35655
  username: text("username").unique(),
35623
35656
  email: text("email").notNull().unique(),
35657
+ isAnonymous: boolean("is_anonymous").notNull().default(false),
35624
35658
  timebackId: text("timeback_id").unique(),
35625
35659
  emailVerified: boolean("email_verified").notNull().default(false),
35626
35660
  image: text("image"),
@@ -56465,7 +56499,7 @@ class LeaderboardService {
56465
56499
  constructor(deps) {
56466
56500
  this.deps = deps;
56467
56501
  }
56468
- async submitScore(gameId, userId, input, sessionId) {
56502
+ async submitScore(gameId, userId, input, isAnonymousUser, sessionId) {
56469
56503
  const db2 = this.deps.db;
56470
56504
  const game = await db2.select({ displayName: games.displayName }).from(games).where(eq(games.id, gameId)).limit(1).then((rows) => rows[0]);
56471
56505
  if (!game) {
@@ -56476,7 +56510,7 @@ class LeaderboardService {
56476
56510
  const prevBestScore = prevBestScoreRows[0]?.score ?? null;
56477
56511
  let prevRank = null;
56478
56512
  if (prevBestScore !== null && prevBestScore !== undefined) {
56479
- const higherCountRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), sql`${gameScores.userId} != ${userId}`, sql`${gameScores.score} > ${prevBestScore}`));
56513
+ const higherCountRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(users.isAnonymous, isAnonymousUser), sql`${gameScores.userId} != ${userId}`, sql`${gameScores.score} > ${prevBestScore}`));
56480
56514
  const higherCount = higherCountRows[0]?.count ?? 0;
56481
56515
  prevRank = Number(higherCount) + 1;
56482
56516
  }
@@ -56493,13 +56527,13 @@ class LeaderboardService {
56493
56527
  }
56494
56528
  const bestScoreRows = await db2.select({ score: sql`MAX(${gameScores.score})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId)));
56495
56529
  const bestScore = bestScoreRows[0]?.score ?? input.score;
56496
- const higherCountNowRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), sql`${gameScores.userId} != ${userId}`, sql`${gameScores.score} > ${bestScore}`));
56530
+ const higherCountNowRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(users.isAnonymous, isAnonymousUser), sql`${gameScores.userId} != ${userId}`, sql`${gameScores.score} > ${bestScore}`));
56497
56531
  const higherCountNow = higherCountNowRows[0]?.count ?? 0;
56498
56532
  const newRank = Number(higherCountNow) + 1;
56499
- const totalPlayersRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).where(eq(gameScores.gameId, gameId));
56533
+ const totalPlayersRows = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(users.isAnonymous, isAnonymousUser)));
56500
56534
  const totalPlayers = Number(totalPlayersRows[0]?.count ?? 1);
56501
56535
  const { shouldNotify, crossedIntoTop3, movedUpWithinTop3, isFirstScore, isPersonalBest } = shouldNotifyRankChange(prevRank, newRank, prevBestScore, input.score);
56502
- if (shouldNotify) {
56536
+ if (shouldNotify && !isAnonymousUser) {
56503
56537
  await this.publishScoreNotifications({
56504
56538
  userId,
56505
56539
  gameId,
@@ -56519,6 +56553,7 @@ class LeaderboardService {
56519
56553
  logger21.info("Score submitted", {
56520
56554
  gameId,
56521
56555
  userId,
56556
+ isAnonymousUser,
56522
56557
  score: input.score,
56523
56558
  newRank,
56524
56559
  isFirstScore,
@@ -56595,7 +56630,7 @@ class LeaderboardService {
56595
56630
  logger21.warn("Failed to publish notification", { error });
56596
56631
  }
56597
56632
  }
56598
- async getLeaderboard(gameId, query) {
56633
+ async getLeaderboard(gameId, query, isAnonymousUser) {
56599
56634
  const { timeframe, limit, offset } = query;
56600
56635
  const db2 = this.deps.db;
56601
56636
  const dateFilter = getTimeframeFilter(timeframe);
@@ -56607,31 +56642,35 @@ class LeaderboardService {
56607
56642
  score: gameScores.score,
56608
56643
  achievedAt: gameScores.achievedAt,
56609
56644
  metadata: gameScores.metadata
56610
- }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), dateFilter)).orderBy(desc(gameScores.score)).limit(limit).offset(offset);
56645
+ }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), dateFilter, eq(users.isAnonymous, isAnonymousUser))).orderBy(desc(gameScores.score)).limit(limit).offset(offset);
56611
56646
  return scores.map((score, index2) => ({
56612
56647
  rank: offset + index2 + 1,
56613
56648
  userId: score.userId,
56614
- username: score.username || score.name?.split(" ")[0] || "Unknown User",
56649
+ username: score.name || score.username || "Unknown User",
56615
56650
  userImage: score.userImage,
56616
56651
  score: score.score,
56617
56652
  achievedAt: score.achievedAt,
56618
56653
  metadata: score.metadata
56619
56654
  }));
56620
56655
  }
56621
- async getGlobalLeaderboard(gameId, query) {
56656
+ async getGlobalLeaderboard(gameId, query, isAnonymousUser) {
56622
56657
  if (!gameId) {
56623
56658
  return [];
56624
56659
  }
56625
56660
  const { timeframe, limit, offset } = query;
56626
56661
  const db2 = this.deps.db;
56627
56662
  const dateFilter = getTimeframeFilter(timeframe);
56628
- const conditions2 = [dateFilter, eq(gameScores.gameId, gameId)];
56663
+ const conditions2 = [
56664
+ dateFilter,
56665
+ eq(gameScores.gameId, gameId),
56666
+ eq(users.isAnonymous, isAnonymousUser)
56667
+ ];
56629
56668
  const bestScoresSubquery = db2.select({
56630
56669
  userId: gameScores.userId,
56631
56670
  gameId: gameScores.gameId,
56632
56671
  score: sql`MAX(${gameScores.score})`.as("max_score"),
56633
56672
  achievedAt: sql`MAX(${gameScores.achievedAt})`.as("max_achieved_at")
56634
- }).from(gameScores).where(and(...conditions2)).groupBy(gameScores.userId, gameScores.gameId).as("best_scores");
56673
+ }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(...conditions2)).groupBy(gameScores.userId, gameScores.gameId).as("best_scores");
56635
56674
  const scores = await db2.select({
56636
56675
  userId: users.id,
56637
56676
  username: users.username,
@@ -56647,7 +56686,7 @@ class LeaderboardService {
56647
56686
  return scores.map((score, index2) => ({
56648
56687
  rank: offset + index2 + 1,
56649
56688
  userId: score.userId,
56650
- username: score.username || score.name?.split(" ")[0] || "Unknown User",
56689
+ username: score.name || score.username || "Unknown User",
56651
56690
  userImage: score.userImage,
56652
56691
  score: score.score,
56653
56692
  achievedAt: score.achievedAt,
@@ -56657,15 +56696,15 @@ class LeaderboardService {
56657
56696
  gameSlug: score.gameSlug || ""
56658
56697
  }));
56659
56698
  }
56660
- async getUserRank(gameId, userId) {
56699
+ async getUserRank(gameId, userId, isAnonymousUser) {
56661
56700
  const db2 = this.deps.db;
56662
- const userScoreResult = await db2.select({ score: gameScores.score }).from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.score)).limit(1);
56701
+ const userScoreResult = await db2.select({ score: gameScores.score }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId), eq(users.isAnonymous, isAnonymousUser))).orderBy(desc(gameScores.score)).limit(1);
56663
56702
  if (!userScoreResult.length || !userScoreResult[0]) {
56664
56703
  return null;
56665
56704
  }
56666
56705
  const score = userScoreResult[0].score;
56667
- const countResult = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).where(and(eq(gameScores.gameId, gameId), sql`${gameScores.score} > ${score}`));
56668
- const totalResult = await db2.select({ total: sql`count(distinct ${gameScores.userId})` }).from(gameScores).where(eq(gameScores.gameId, gameId));
56706
+ const countResult = await db2.select({ count: sql`count(distinct ${gameScores.userId})` }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(users.isAnonymous, isAnonymousUser), sql`${gameScores.score} > ${score}`));
56707
+ const totalResult = await db2.select({ total: sql`count(distinct ${gameScores.userId})` }).from(gameScores).innerJoin(users, eq(gameScores.userId, users.id)).where(and(eq(gameScores.gameId, gameId), eq(users.isAnonymous, isAnonymousUser)));
56669
56708
  const count = Number(countResult[0]?.count ?? 0);
56670
56709
  const total = Number(totalResult[0]?.total ?? 0);
56671
56710
  const rank = count + 1;
@@ -58100,6 +58139,35 @@ class UserService {
58100
58139
  timeback: timeback2
58101
58140
  };
58102
58141
  }
58142
+ async getDemoProfile(userId) {
58143
+ const userData = await this.deps.db.query.users.findFirst({
58144
+ where: eq(users.id, userId),
58145
+ columns: { name: true }
58146
+ });
58147
+ if (!userData) {
58148
+ logger32.error("Demo user not found", { userId });
58149
+ throw new NotFoundError("User", userId);
58150
+ }
58151
+ return {
58152
+ displayName: userData.name,
58153
+ isDefault: userData.name === DEMO_DISPLAY_NAME_PLACEHOLDER
58154
+ };
58155
+ }
58156
+ async updateDemoProfile(userId, displayName) {
58157
+ const [updatedUser] = await this.deps.db.update(users).set({
58158
+ name: displayName,
58159
+ updatedAt: new Date
58160
+ }).where(eq(users.id, userId)).returning({ name: users.name });
58161
+ if (!updatedUser) {
58162
+ logger32.error("Demo user not found for profile update", { userId });
58163
+ throw new NotFoundError("User", userId);
58164
+ }
58165
+ logger32.debug("Updated demo profile", { userId, displayName });
58166
+ return {
58167
+ displayName: updatedUser.name,
58168
+ isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
58169
+ };
58170
+ }
58103
58171
  async fetchTimebackData(timebackId, gameId) {
58104
58172
  const [{ role, organizations: allOrganizations }, allEnrollments] = await Promise.all([
58105
58173
  this.fetchStudentProfile(timebackId),
@@ -58192,6 +58260,7 @@ class UserService {
58192
58260
  var logger32;
58193
58261
  var init_user_service = __esm(() => {
58194
58262
  init_drizzle_orm();
58263
+ init_src();
58195
58264
  init_tables_index();
58196
58265
  init_src2();
58197
58266
  init_errors();
@@ -119909,7 +119978,11 @@ var init_drizzle_zod = __esm(() => {
119909
119978
  nullable: (column6) => !column6.notNull
119910
119979
  };
119911
119980
  });
119981
+ function normalizeDisplayName(value) {
119982
+ return value.trim().replace(/\s+/g, " ");
119983
+ }
119912
119984
  var InsertUserSchema;
119985
+ var DemoProfileSchema;
119913
119986
  var init_schemas = __esm(() => {
119914
119987
  init_drizzle_zod();
119915
119988
  init_esm();
@@ -119918,6 +119991,9 @@ var init_schemas = __esm(() => {
119918
119991
  email: exports_external.string().email(),
119919
119992
  name: exports_external.string().optional()
119920
119993
  });
119994
+ DemoProfileSchema = exports_external.object({
119995
+ displayName: exports_external.string().transform(normalizeDisplayName).refine((value) => value.length >= 2, "Display name must be at least 2 characters").refine((value) => value.length <= 12, "Display name must be 12 characters or fewer")
119996
+ });
119921
119997
  });
119922
119998
  var InsertGameSchema;
119923
119999
  var UpdateGameSchema;
@@ -120572,6 +120648,28 @@ function requireAuth(handler) {
120572
120648
  return handler(ctx);
120573
120649
  };
120574
120650
  }
120651
+ function requireNonAnonymous(handler) {
120652
+ return async (ctx) => {
120653
+ if (!isAuthenticated(ctx)) {
120654
+ throw ApiError.unauthorized("Valid session or bearer token required");
120655
+ }
120656
+ if (ctx.user.isAnonymous) {
120657
+ throw ApiError.forbidden("This operation is not available for demo/anonymous users");
120658
+ }
120659
+ return handler(ctx);
120660
+ };
120661
+ }
120662
+ function requireAnonymous(handler) {
120663
+ return async (ctx) => {
120664
+ if (!isAuthenticated(ctx)) {
120665
+ throw ApiError.unauthorized("Valid session or bearer token required");
120666
+ }
120667
+ if (!ctx.user.isAnonymous) {
120668
+ throw ApiError.forbidden("This operation is only available for demo/anonymous users");
120669
+ }
120670
+ return handler(ctx);
120671
+ };
120672
+ }
120575
120673
  function requireRole(roles2, handler) {
120576
120674
  return async (ctx) => {
120577
120675
  if (!isAuthenticated(ctx)) {
@@ -120806,16 +120904,16 @@ var init_achievement_controller = __esm(() => {
120806
120904
  init_errors();
120807
120905
  init_utils11();
120808
120906
  logger37 = log.scope("AchievementController");
120809
- listCurrent = requireAuth(async (ctx) => {
120907
+ listCurrent = requireNonAnonymous(async (ctx) => {
120810
120908
  logger37.debug("Listing current achievements", { userId: ctx.user.id, gameId: ctx.gameId });
120811
120909
  return ctx.services.achievement.listCurrent(ctx.user, ctx.gameId);
120812
120910
  });
120813
- listHistory = requireAuth(async (ctx) => {
120911
+ listHistory = requireNonAnonymous(async (ctx) => {
120814
120912
  const limit = Math.max(1, Math.min(100, Number(ctx.url.searchParams.get("limit")) || 20));
120815
120913
  logger37.debug("Listing achievement history", { userId: ctx.user.id, limit });
120816
120914
  return ctx.services.achievement.listHistory(ctx.user, limit);
120817
120915
  });
120818
- postProgress = requireAuth(async (ctx) => {
120916
+ postProgress = requireNonAnonymous(async (ctx) => {
120819
120917
  let body2;
120820
120918
  try {
120821
120919
  const json4 = await ctx.request.json();
@@ -120976,11 +121074,11 @@ var init_character_controller = __esm(() => {
120976
121074
  init_errors();
120977
121075
  init_utils11();
120978
121076
  logger40 = log.scope("CharacterController");
120979
- get = requireAuth(async (ctx) => {
121077
+ get = requireNonAnonymous(async (ctx) => {
120980
121078
  logger40.debug("Getting character", { userId: ctx.user.id });
120981
121079
  return ctx.services.character.getByUser(ctx.user);
120982
121080
  });
120983
- getByUserId = requireAuth(async (ctx) => {
121081
+ getByUserId = requireNonAnonymous(async (ctx) => {
120984
121082
  const userId = ctx.params.userId;
120985
121083
  if (!userId) {
120986
121084
  throw ApiError.badRequest("User ID is required in the URL path");
@@ -120988,7 +121086,7 @@ var init_character_controller = __esm(() => {
120988
121086
  logger40.debug("Getting character by user ID", { requestedUserId: userId });
120989
121087
  return ctx.services.character.getByUserId(userId);
120990
121088
  });
120991
- create = requireAuth(async (ctx) => {
121089
+ create = requireNonAnonymous(async (ctx) => {
120992
121090
  let body2;
120993
121091
  try {
120994
121092
  const json4 = await ctx.request.json();
@@ -121008,7 +121106,7 @@ var init_character_controller = __esm(() => {
121008
121106
  });
121009
121107
  return ctx.services.character.create(body2, ctx.user);
121010
121108
  });
121011
- update2 = requireAuth(async (ctx) => {
121109
+ update2 = requireNonAnonymous(async (ctx) => {
121012
121110
  let body2;
121013
121111
  try {
121014
121112
  const json4 = await ctx.request.json();
@@ -121029,7 +121127,7 @@ var init_character_controller = __esm(() => {
121029
121127
  });
121030
121128
  return ctx.services.character.update(body2, ctx.user);
121031
121129
  });
121032
- equipAccessory = requireAuth(async (ctx) => {
121130
+ equipAccessory = requireNonAnonymous(async (ctx) => {
121033
121131
  let body2;
121034
121132
  try {
121035
121133
  const json4 = await ctx.request.json();
@@ -121049,7 +121147,7 @@ var init_character_controller = __esm(() => {
121049
121147
  });
121050
121148
  return ctx.services.character.equipAccessory(body2.slot, body2.accessoryComponentId, ctx.user);
121051
121149
  });
121052
- removeAccessory = requireAuth(async (ctx) => {
121150
+ removeAccessory = requireNonAnonymous(async (ctx) => {
121053
121151
  const slot = ctx.params.slot;
121054
121152
  if (!slot) {
121055
121153
  throw ApiError.badRequest("Slot is required in the URL path");
@@ -121083,11 +121181,11 @@ var init_currency_controller = __esm(() => {
121083
121181
  init_errors();
121084
121182
  init_utils11();
121085
121183
  logger41 = log.scope("CurrencyController");
121086
- list = requireAuth(async (ctx) => {
121184
+ list = requireNonAnonymous(async (ctx) => {
121087
121185
  logger41.debug("Listing currencies", { userId: ctx.user.id });
121088
121186
  return ctx.services.currency.list();
121089
121187
  });
121090
- getById = requireAuth(async (ctx) => {
121188
+ getById = requireNonAnonymous(async (ctx) => {
121091
121189
  const currencyId = ctx.params.currencyId;
121092
121190
  if (!currencyId) {
121093
121191
  throw ApiError.badRequest("Missing currency ID");
@@ -121261,11 +121359,11 @@ var init_developer_controller = __esm(() => {
121261
121359
  init_src2();
121262
121360
  init_utils11();
121263
121361
  logger44 = log.scope("DeveloperController");
121264
- apply = requireAuth(async (ctx) => {
121362
+ apply = requireNonAnonymous(async (ctx) => {
121265
121363
  logger44.debug("Applying for developer status", { userId: ctx.user.id });
121266
121364
  await ctx.services.developer.apply(ctx.user);
121267
121365
  });
121268
- getStatus = requireAuth(async (ctx) => {
121366
+ getStatus = requireNonAnonymous(async (ctx) => {
121269
121367
  logger44.debug("Getting developer status", { userId: ctx.user.id });
121270
121368
  const status = await ctx.services.developer.getStatus(ctx.user.id);
121271
121369
  return { status };
@@ -121377,19 +121475,19 @@ var init_game_controller = __esm(() => {
121377
121475
  init_errors();
121378
121476
  init_utils11();
121379
121477
  logger46 = log.scope("GameController");
121380
- list3 = requireAuth(async (ctx) => {
121478
+ list3 = requireNonAnonymous(async (ctx) => {
121381
121479
  logger46.debug("Listing games", { userId: ctx.user.id });
121382
121480
  return ctx.services.game.list(ctx.user);
121383
121481
  });
121384
- listManageable = requireAuth(async (ctx) => {
121482
+ listManageable = requireNonAnonymous(async (ctx) => {
121385
121483
  logger46.debug("Listing manageable games", { userId: ctx.user.id });
121386
121484
  return ctx.services.game.listManageable(ctx.user);
121387
121485
  });
121388
- getSubjects = requireAuth(async (ctx) => {
121486
+ getSubjects = requireNonAnonymous(async (ctx) => {
121389
121487
  logger46.debug("Getting game subjects", { userId: ctx.user.id });
121390
121488
  return ctx.services.game.getSubjects();
121391
121489
  });
121392
- getById2 = requireAuth(async (ctx) => {
121490
+ getById2 = requireNonAnonymous(async (ctx) => {
121393
121491
  const gameId = ctx.params.gameId;
121394
121492
  if (!gameId) {
121395
121493
  throw ApiError.badRequest("Missing game ID");
@@ -121400,7 +121498,7 @@ var init_game_controller = __esm(() => {
121400
121498
  logger46.debug("Getting game by ID", { userId: ctx.user.id, gameId, launchId: ctx.launchId });
121401
121499
  return ctx.services.game.getById(gameId, ctx.user);
121402
121500
  });
121403
- getBySlug = requireAuth(async (ctx) => {
121501
+ getBySlug = requireNonAnonymous(async (ctx) => {
121404
121502
  const slug2 = ctx.params.slug;
121405
121503
  if (!slug2) {
121406
121504
  throw ApiError.badRequest("Missing game slug");
@@ -121408,7 +121506,7 @@ var init_game_controller = __esm(() => {
121408
121506
  logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
121409
121507
  return ctx.services.game.getBySlug(slug2, ctx.user);
121410
121508
  });
121411
- getManifest = requireAuth(async (ctx) => {
121509
+ getManifest = requireNonAnonymous(async (ctx) => {
121412
121510
  const gameId = ctx.params.gameId;
121413
121511
  if (!gameId) {
121414
121512
  throw ApiError.badRequest("Missing game ID");
@@ -121423,7 +121521,7 @@ var init_game_controller = __esm(() => {
121423
121521
  });
121424
121522
  return ctx.services.game.getManifest(gameId, ctx.user);
121425
121523
  });
121426
- upsertBySlug = requireAuth(async (ctx) => {
121524
+ upsertBySlug = requireNonAnonymous(async (ctx) => {
121427
121525
  const slug2 = ctx.params.slug;
121428
121526
  if (!slug2) {
121429
121527
  throw ApiError.badRequest("Missing game slug");
@@ -121443,7 +121541,7 @@ var init_game_controller = __esm(() => {
121443
121541
  logger46.debug("Upserting game", { userId: ctx.user.id, slug: slug2, displayName: body2.displayName });
121444
121542
  return ctx.services.game.upsertBySlug(slug2, body2, ctx.user);
121445
121543
  });
121446
- remove3 = requireAuth(async (ctx) => {
121544
+ remove3 = requireNonAnonymous(async (ctx) => {
121447
121545
  const gameId = ctx.params.gameId;
121448
121546
  if (!gameId) {
121449
121547
  throw ApiError.badRequest("Missing game ID");
@@ -121477,11 +121575,11 @@ var init_inventory_controller = __esm(() => {
121477
121575
  init_errors();
121478
121576
  init_utils11();
121479
121577
  logger47 = log.scope("InventoryController");
121480
- list4 = requireAuth(async (ctx) => {
121578
+ list4 = requireNonAnonymous(async (ctx) => {
121481
121579
  logger47.debug("Listing inventory", { userId: ctx.user.id });
121482
121580
  return ctx.services.inventory.list(ctx.user);
121483
121581
  });
121484
- addItem = requireAuth(async (ctx) => {
121582
+ addItem = requireNonAnonymous(async (ctx) => {
121485
121583
  let body2;
121486
121584
  try {
121487
121585
  const json4 = await ctx.request.json();
@@ -121501,7 +121599,7 @@ var init_inventory_controller = __esm(() => {
121501
121599
  });
121502
121600
  return ctx.services.inventory.addItem(body2.itemId, body2.qty, ctx.user);
121503
121601
  });
121504
- removeItem = requireAuth(async (ctx) => {
121602
+ removeItem = requireNonAnonymous(async (ctx) => {
121505
121603
  let body2;
121506
121604
  try {
121507
121605
  const json4 = await ctx.request.json();
@@ -121547,12 +121645,12 @@ var init_item_controller = __esm(() => {
121547
121645
  init_errors();
121548
121646
  init_utils11();
121549
121647
  logger48 = log.scope("ItemController");
121550
- list5 = requireAuth(async (ctx) => {
121648
+ list5 = requireNonAnonymous(async (ctx) => {
121551
121649
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
121552
121650
  logger48.debug("Listing items", { userId: ctx.user.id, gameId });
121553
121651
  return ctx.services.item.list(gameId);
121554
121652
  });
121555
- getById3 = requireAuth(async (ctx) => {
121653
+ getById3 = requireNonAnonymous(async (ctx) => {
121556
121654
  const itemId = ctx.params.itemId;
121557
121655
  if (!itemId) {
121558
121656
  throw ApiError.badRequest("Missing item ID");
@@ -121563,7 +121661,7 @@ var init_item_controller = __esm(() => {
121563
121661
  logger48.debug("Getting item", { userId: ctx.user.id, itemId });
121564
121662
  return ctx.services.item.getById(itemId);
121565
121663
  });
121566
- resolve2 = requireAuth(async (ctx) => {
121664
+ resolve2 = requireNonAnonymous(async (ctx) => {
121567
121665
  const slug2 = ctx.url.searchParams.get("slug");
121568
121666
  const gameId = ctx.url.searchParams.get("gameId") || undefined;
121569
121667
  if (!slug2) {
@@ -121638,7 +121736,7 @@ var init_item_controller = __esm(() => {
121638
121736
  logger48.debug("Deleting item", { userId: ctx.user.id, itemId });
121639
121737
  await ctx.services.item.delete(itemId);
121640
121738
  });
121641
- listByGame = requireAuth(async (ctx) => {
121739
+ listByGame = requireNonAnonymous(async (ctx) => {
121642
121740
  const gameId = ctx.params.gameId;
121643
121741
  if (!gameId) {
121644
121742
  throw ApiError.badRequest("Missing game ID");
@@ -121649,7 +121747,7 @@ var init_item_controller = __esm(() => {
121649
121747
  logger48.debug("Listing game items", { userId: ctx.user.id, gameId });
121650
121748
  return ctx.services.item.listByGame(gameId);
121651
121749
  });
121652
- createForGame = requireAuth(async (ctx) => {
121750
+ createForGame = requireNonAnonymous(async (ctx) => {
121653
121751
  const gameId = ctx.params.gameId;
121654
121752
  if (!gameId) {
121655
121753
  throw ApiError.badRequest("Missing game ID");
@@ -121677,7 +121775,7 @@ var init_item_controller = __esm(() => {
121677
121775
  });
121678
121776
  return ctx.services.item.createForGame(gameId, body2, ctx.user);
121679
121777
  });
121680
- updateForGame = requireAuth(async (ctx) => {
121778
+ updateForGame = requireNonAnonymous(async (ctx) => {
121681
121779
  const gameId = ctx.params.gameId;
121682
121780
  const itemId = ctx.params.itemId;
121683
121781
  if (!gameId || !itemId) {
@@ -121714,7 +121812,7 @@ var init_item_controller = __esm(() => {
121714
121812
  });
121715
121813
  return ctx.services.item.updateForGame(gameId, itemId, body2, ctx.user);
121716
121814
  });
121717
- deleteForGame = requireAuth(async (ctx) => {
121815
+ deleteForGame = requireNonAnonymous(async (ctx) => {
121718
121816
  const gameId = ctx.params.gameId;
121719
121817
  const itemId = ctx.params.itemId;
121720
121818
  if (!gameId || !itemId) {
@@ -121889,7 +121987,7 @@ var init_leaderboard_controller = __esm(() => {
121889
121987
  gameId,
121890
121988
  score: body2.score
121891
121989
  });
121892
- return ctx.services.leaderboard.submitScore(gameId, ctx.user.id, { score: body2.score, metadata: body2.metadata }, ctx.params.sessionId);
121990
+ return ctx.services.leaderboard.submitScore(gameId, ctx.user.id, { score: body2.score, metadata: body2.metadata }, ctx.user.isAnonymous, ctx.params.sessionId);
121893
121991
  });
121894
121992
  getGlobalLeaderboard = requireAuth(async (ctx) => {
121895
121993
  const url = ctx.url;
@@ -121917,7 +122015,7 @@ var init_leaderboard_controller = __esm(() => {
121917
122015
  gameId,
121918
122016
  ...query
121919
122017
  });
121920
- return ctx.services.leaderboard.getGlobalLeaderboard(gameId, query);
122018
+ return ctx.services.leaderboard.getGlobalLeaderboard(gameId, query, ctx.user.isAnonymous);
121921
122019
  });
121922
122020
  getLeaderboard = requireAuth(async (ctx) => {
121923
122021
  const gameId = ctx.params.gameId;
@@ -121945,9 +122043,9 @@ var init_leaderboard_controller = __esm(() => {
121945
122043
  gameId,
121946
122044
  ...query
121947
122045
  });
121948
- return ctx.services.leaderboard.getLeaderboard(gameId, query);
122046
+ return ctx.services.leaderboard.getLeaderboard(gameId, query, ctx.user.isAnonymous);
121949
122047
  });
121950
- getUserRank = requireAuth(async (ctx) => {
122048
+ getUserRank = requireNonAnonymous(async (ctx) => {
121951
122049
  const { gameId, userId } = ctx.params;
121952
122050
  if (!gameId || !userId) {
121953
122051
  throw ApiError.badRequest("Game ID and User ID are required");
@@ -121957,9 +122055,9 @@ var init_leaderboard_controller = __esm(() => {
121957
122055
  gameId,
121958
122056
  targetUserId: userId
121959
122057
  });
121960
- return ctx.services.leaderboard.getUserRank(gameId, userId);
122058
+ return ctx.services.leaderboard.getUserRank(gameId, userId, ctx.user.isAnonymous);
121961
122059
  });
121962
- getUserAllScores = requireAuth(async (ctx) => {
122060
+ getUserAllScores = requireNonAnonymous(async (ctx) => {
121963
122061
  const userId = ctx.params.userId;
121964
122062
  if (!userId) {
121965
122063
  throw ApiError.badRequest("User ID is required");
@@ -121975,7 +122073,7 @@ var init_leaderboard_controller = __esm(() => {
121975
122073
  });
121976
122074
  return ctx.services.leaderboard.getUserAllScores(userId, { limit, gameId });
121977
122075
  });
121978
- getUserScores = requireAuth(async (ctx) => {
122076
+ getUserScores = requireNonAnonymous(async (ctx) => {
121979
122077
  const { gameId, userId } = ctx.params;
121980
122078
  if (!gameId || !userId) {
121981
122079
  throw ApiError.badRequest("Game ID and User ID are required");
@@ -122024,11 +122122,11 @@ var init_level_controller = __esm(() => {
122024
122122
  init_errors();
122025
122123
  init_utils11();
122026
122124
  logger51 = log.scope("LevelController");
122027
- getByUser = requireAuth(async (ctx) => {
122125
+ getByUser = requireNonAnonymous(async (ctx) => {
122028
122126
  logger51.debug("Getting user level", { userId: ctx.user.id });
122029
122127
  return ctx.services.level.getByUser(ctx.user);
122030
122128
  });
122031
- getProgress = requireAuth(async (ctx) => {
122129
+ getProgress = requireNonAnonymous(async (ctx) => {
122032
122130
  logger51.debug("Getting level progress", { userId: ctx.user.id });
122033
122131
  return ctx.services.level.getProgress(ctx.user);
122034
122132
  });
@@ -122083,7 +122181,7 @@ var init_lti_controller = __esm(() => {
122083
122181
  init_src2();
122084
122182
  init_utils11();
122085
122183
  logger53 = log.scope("LtiController");
122086
- getStatus3 = requireAuth(async (ctx) => {
122184
+ getStatus3 = requireNonAnonymous(async (ctx) => {
122087
122185
  logger53.debug("Getting status", { userId: ctx.user.id });
122088
122186
  return ctx.services.lti.getStatus(ctx.user);
122089
122187
  });
@@ -122106,7 +122204,7 @@ var init_map_controller = __esm(() => {
122106
122204
  init_errors();
122107
122205
  init_utils11();
122108
122206
  logger54 = log.scope("MapController");
122109
- getByIdentifier = requireAuth(async (ctx) => {
122207
+ getByIdentifier = requireNonAnonymous(async (ctx) => {
122110
122208
  const identifier = ctx.params.identifier;
122111
122209
  if (!identifier) {
122112
122210
  throw ApiError.badRequest("Missing map identifier");
@@ -122114,7 +122212,7 @@ var init_map_controller = __esm(() => {
122114
122212
  logger54.debug("Getting map", { userId: ctx.user.id, identifier });
122115
122213
  return ctx.services.map.getByIdentifier(identifier);
122116
122214
  });
122117
- getElements = requireAuth(async (ctx) => {
122215
+ getElements = requireNonAnonymous(async (ctx) => {
122118
122216
  const mapId = ctx.url.searchParams.get("mapId");
122119
122217
  if (!mapId) {
122120
122218
  throw ApiError.badRequest("Missing required query parameter: mapId");
@@ -122125,7 +122223,7 @@ var init_map_controller = __esm(() => {
122125
122223
  logger54.debug("Getting map elements", { userId: ctx.user.id, mapId });
122126
122224
  return ctx.services.map.getElements(mapId);
122127
122225
  });
122128
- getObjects = requireAuth(async (ctx) => {
122226
+ getObjects = requireNonAnonymous(async (ctx) => {
122129
122227
  const mapId = ctx.params.mapId;
122130
122228
  if (!mapId) {
122131
122229
  throw ApiError.badRequest("Missing mapId");
@@ -122136,7 +122234,7 @@ var init_map_controller = __esm(() => {
122136
122234
  logger54.debug("Getting map objects", { userId: ctx.user.id, mapId });
122137
122235
  return ctx.services.map.getObjects(mapId, ctx.user.id);
122138
122236
  });
122139
- createObject = requireAuth(async (ctx) => {
122237
+ createObject = requireNonAnonymous(async (ctx) => {
122140
122238
  const mapId = ctx.params.mapId;
122141
122239
  if (!mapId) {
122142
122240
  throw ApiError.badRequest("Missing mapId");
@@ -122170,7 +122268,7 @@ var init_map_controller = __esm(() => {
122170
122268
  const { mapId: _mapId, ...data } = body2;
122171
122269
  return ctx.services.map.createObject(mapId, data, ctx.user);
122172
122270
  });
122173
- deleteObject = requireAuth(async (ctx) => {
122271
+ deleteObject = requireNonAnonymous(async (ctx) => {
122174
122272
  const { mapId, objectId } = ctx.params;
122175
122273
  if (!mapId) {
122176
122274
  throw ApiError.badRequest("Missing mapId");
@@ -122209,7 +122307,7 @@ var init_notification_controller = __esm(() => {
122209
122307
  init_errors();
122210
122308
  init_utils11();
122211
122309
  logger55 = log.scope("NotificationController");
122212
- list6 = requireAuth(async (ctx) => {
122310
+ list6 = requireNonAnonymous(async (ctx) => {
122213
122311
  const query = {
122214
122312
  status: ctx.url.searchParams.get("status") || undefined,
122215
122313
  type: ctx.url.searchParams.get("type") || undefined,
@@ -122225,7 +122323,7 @@ var init_notification_controller = __esm(() => {
122225
122323
  logger55.debug("Listing notifications", { userId: ctx.user.id, ...result.data });
122226
122324
  return ctx.services.notification.list(ctx.user, result.data);
122227
122325
  });
122228
- updateStatus = requireAuth(async (ctx) => {
122326
+ updateStatus = requireNonAnonymous(async (ctx) => {
122229
122327
  const notificationId = ctx.params.notificationId;
122230
122328
  if (!notificationId) {
122231
122329
  throw ApiError.badRequest("Notification ID required");
@@ -122249,7 +122347,7 @@ var init_notification_controller = __esm(() => {
122249
122347
  });
122250
122348
  return ctx.services.notification.updateStatus(notificationId, body2.status, body2.method);
122251
122349
  });
122252
- getStats2 = requireAuth(async (ctx) => {
122350
+ getStats2 = requireNonAnonymous(async (ctx) => {
122253
122351
  const startDate = ctx.url.searchParams.get("startDate");
122254
122352
  const endDate = ctx.url.searchParams.get("endDate");
122255
122353
  logger55.debug("Getting stats", { userId: ctx.user.id, startDate, endDate });
@@ -122258,7 +122356,7 @@ var init_notification_controller = __esm(() => {
122258
122356
  endDate: endDate ? new Date(endDate) : undefined
122259
122357
  });
122260
122358
  });
122261
- create4 = requireAuth(async (ctx) => {
122359
+ create4 = requireNonAnonymous(async (ctx) => {
122262
122360
  let body2;
122263
122361
  try {
122264
122362
  const json4 = await ctx.request.json();
@@ -122288,7 +122386,7 @@ var init_notification_controller = __esm(() => {
122288
122386
  metadata: body2.metadata
122289
122387
  });
122290
122388
  });
122291
- deliver = requireAuth(async (ctx) => {
122389
+ deliver = requireNonAnonymous(async (ctx) => {
122292
122390
  logger55.debug("Delivering notifications", { userId: ctx.user.id });
122293
122391
  try {
122294
122392
  await ctx.services.notification.deliverPending(ctx.user.id);
@@ -122313,7 +122411,7 @@ var init_realtime_controller = __esm(() => {
122313
122411
  init_src2();
122314
122412
  init_utils11();
122315
122413
  logger56 = log.scope("RealtimeController");
122316
- generateToken2 = requireAuth(async (ctx) => {
122414
+ generateToken2 = requireNonAnonymous(async (ctx) => {
122317
122415
  const gameIdOrSlug = ctx.params.gameId;
122318
122416
  logger56.debug("Generating token", {
122319
122417
  userId: ctx.user.id,
@@ -122482,7 +122580,7 @@ var init_shop_controller = __esm(() => {
122482
122580
  init_src2();
122483
122581
  init_utils11();
122484
122582
  logger60 = log.scope("ShopController");
122485
- getShopView = requireAuth(async (ctx) => {
122583
+ getShopView = requireNonAnonymous(async (ctx) => {
122486
122584
  logger60.debug("Getting shop view", { userId: ctx.user.id });
122487
122585
  return ctx.services.shop.getShopView(ctx.user);
122488
122586
  });
@@ -122586,7 +122684,7 @@ var init_shop_listing_controller = __esm(() => {
122586
122684
  logger61.debug("Deleting listing", { userId: ctx.user.id, listingId });
122587
122685
  await ctx.services.shopListing.delete(listingId);
122588
122686
  });
122589
- listByGame2 = requireAuth(async (ctx) => {
122687
+ listByGame2 = requireNonAnonymous(async (ctx) => {
122590
122688
  const gameId = ctx.params.gameId;
122591
122689
  if (!gameId) {
122592
122690
  throw ApiError.badRequest("Missing game ID");
@@ -122597,7 +122695,7 @@ var init_shop_listing_controller = __esm(() => {
122597
122695
  logger61.debug("Listing game listings", { userId: ctx.user.id, gameId });
122598
122696
  return ctx.services.shopListing.listByGame(gameId, ctx.user);
122599
122697
  });
122600
- getByGameItem = requireAuth(async (ctx) => {
122698
+ getByGameItem = requireNonAnonymous(async (ctx) => {
122601
122699
  const gameId = ctx.params.gameId;
122602
122700
  const itemId = ctx.params.itemId;
122603
122701
  if (!gameId || !itemId) {
@@ -122612,7 +122710,7 @@ var init_shop_listing_controller = __esm(() => {
122612
122710
  logger61.debug("Getting game item listing", { userId: ctx.user.id, gameId, itemId });
122613
122711
  return ctx.services.shopListing.getByGameItem(gameId, itemId, ctx.user);
122614
122712
  });
122615
- createForGameItem = requireAuth(async (ctx) => {
122713
+ createForGameItem = requireNonAnonymous(async (ctx) => {
122616
122714
  const gameId = ctx.params.gameId;
122617
122715
  const itemId = ctx.params.itemId;
122618
122716
  if (!gameId || !itemId) {
@@ -122645,7 +122743,7 @@ var init_shop_listing_controller = __esm(() => {
122645
122743
  });
122646
122744
  return ctx.services.shopListing.createForGameItem(gameId, itemId, body2, ctx.user);
122647
122745
  });
122648
- updateForGameItem = requireAuth(async (ctx) => {
122746
+ updateForGameItem = requireNonAnonymous(async (ctx) => {
122649
122747
  const gameId = ctx.params.gameId;
122650
122748
  const itemId = ctx.params.itemId;
122651
122749
  if (!gameId || !itemId) {
@@ -122679,7 +122777,7 @@ var init_shop_listing_controller = __esm(() => {
122679
122777
  });
122680
122778
  return ctx.services.shopListing.updateForGameItem(gameId, itemId, body2, ctx.user);
122681
122779
  });
122682
- deleteForGameItem = requireAuth(async (ctx) => {
122780
+ deleteForGameItem = requireNonAnonymous(async (ctx) => {
122683
122781
  const gameId = ctx.params.gameId;
122684
122782
  const itemId = ctx.params.itemId;
122685
122783
  if (!gameId || !itemId) {
@@ -122764,17 +122862,17 @@ var init_timeback_controller = __esm(() => {
122764
122862
  init_errors();
122765
122863
  init_utils11();
122766
122864
  logger63 = log.scope("TimebackController");
122767
- getTodayXp = requireAuth(async (ctx) => {
122865
+ getTodayXp = requireNonAnonymous(async (ctx) => {
122768
122866
  const date4 = ctx.url.searchParams.get("date") || undefined;
122769
122867
  const tz = ctx.url.searchParams.get("tz") || undefined;
122770
122868
  logger63.debug("Getting today XP", { userId: ctx.user.id, date: date4, tz });
122771
122869
  return ctx.services.timeback.getTodayXp(ctx.user.id, date4, tz);
122772
122870
  });
122773
- getTotalXp = requireAuth(async (ctx) => {
122871
+ getTotalXp = requireNonAnonymous(async (ctx) => {
122774
122872
  logger63.debug("Getting total XP", { userId: ctx.user.id });
122775
122873
  return ctx.services.timeback.getTotalXp(ctx.user.id);
122776
122874
  });
122777
- updateTodayXp = requireAuth(async (ctx) => {
122875
+ updateTodayXp = requireNonAnonymous(async (ctx) => {
122778
122876
  let body2;
122779
122877
  try {
122780
122878
  const json4 = await ctx.request.json();
@@ -122790,13 +122888,13 @@ var init_timeback_controller = __esm(() => {
122790
122888
  logger63.debug("Updating today XP", { userId: ctx.user.id, xp: body2.xp });
122791
122889
  return ctx.services.timeback.updateTodayXp(ctx.user.id, body2);
122792
122890
  });
122793
- getXpHistory = requireAuth(async (ctx) => {
122891
+ getXpHistory = requireNonAnonymous(async (ctx) => {
122794
122892
  const startDate = ctx.url.searchParams.get("startDate") || undefined;
122795
122893
  const endDate = ctx.url.searchParams.get("endDate") || undefined;
122796
122894
  logger63.debug("Getting XP history", { userId: ctx.user.id, startDate, endDate });
122797
122895
  return ctx.services.timeback.getXpHistory(ctx.user.id, startDate, endDate);
122798
122896
  });
122799
- populateStudent = requireAuth(async (ctx) => {
122897
+ populateStudent = requireNonAnonymous(async (ctx) => {
122800
122898
  let providedNames;
122801
122899
  try {
122802
122900
  const json4 = await ctx.request.json();
@@ -122814,11 +122912,11 @@ var init_timeback_controller = __esm(() => {
122814
122912
  });
122815
122913
  return ctx.services.timeback.populateStudent(ctx.user, providedNames);
122816
122914
  });
122817
- getUser = requireAuth(async (ctx) => {
122915
+ getUser = requireNonAnonymous(async (ctx) => {
122818
122916
  logger63.debug("Getting user", { userId: ctx.user.id, gameId: ctx.gameId });
122819
122917
  return ctx.services.timeback.getUserData(ctx.user.id, ctx.gameId);
122820
122918
  });
122821
- getUserById = requireAuth(async (ctx) => {
122919
+ getUserById = requireNonAnonymous(async (ctx) => {
122822
122920
  const timebackId = ctx.params.timebackId;
122823
122921
  if (!timebackId) {
122824
122922
  throw ApiError.badRequest("Missing timebackId parameter");
@@ -123216,17 +123314,34 @@ var init_upload_controller = __esm(() => {
123216
123314
  });
123217
123315
  var logger65;
123218
123316
  var getMe;
123317
+ var getDemoProfile;
123318
+ var updateDemoProfile;
123219
123319
  var users2;
123220
123320
  var init_user_controller = __esm(() => {
123321
+ init_schemas_index();
123221
123322
  init_src2();
123222
123323
  init_utils11();
123223
123324
  logger65 = log.scope("UserController");
123224
- getMe = requireAuth(async (ctx) => {
123325
+ getMe = requireNonAnonymous(async (ctx) => {
123225
123326
  logger65.debug("Getting current user", { userId: ctx.user.id, gameId: ctx.gameId });
123226
123327
  return ctx.services.user.getMe(ctx.user, ctx.gameId);
123227
123328
  });
123329
+ getDemoProfile = requireAnonymous(async (ctx) => {
123330
+ logger65.debug("Getting demo profile", { userId: ctx.user.id });
123331
+ return ctx.services.user.getDemoProfile(ctx.user.id);
123332
+ });
123333
+ updateDemoProfile = requireAnonymous(async (ctx) => {
123334
+ const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
123335
+ logger65.debug("Updating demo profile", {
123336
+ userId: ctx.user.id,
123337
+ displayName: body2.displayName
123338
+ });
123339
+ return ctx.services.user.updateDemoProfile(ctx.user.id, body2.displayName);
123340
+ });
123228
123341
  users2 = {
123229
- getMe
123342
+ getMe,
123343
+ getDemoProfile,
123344
+ updateDemoProfile
123230
123345
  };
123231
123346
  });
123232
123347
  var logger66;
@@ -123422,6 +123537,8 @@ var init_users = __esm(() => {
123422
123537
  }
123423
123538
  return handle2(users2.getMe)(c2);
123424
123539
  });
123540
+ usersRouter.get("/demo-profile", handle2(users2.getDemoProfile));
123541
+ usersRouter.patch("/demo-profile", handle2(users2.updateDemoProfile));
123425
123542
  usersRouter.get("/level", handle2(levels.getByUser));
123426
123543
  usersRouter.get("/level/progress", handle2(levels.getProgress));
123427
123544
  usersRouter.get("/:userId/scores", handle2(leaderboard.getUserAllScores));
@@ -123753,6 +123870,8 @@ var init_scores = __esm(() => {
123753
123870
  init_api();
123754
123871
  gameScoresRouter = new Hono2;
123755
123872
  gameScoresRouter.post("/:gameId/scores", handle2(leaderboard.submitScore, { status: 201 }));
123873
+ gameScoresRouter.get("/:gameId/leaderboard", handle2(leaderboard.getLeaderboard));
123874
+ gameScoresRouter.get("/:gameId/users/:userId/rank", handle2(leaderboard.getUserRank));
123756
123875
  gameScoresRouter.get("/:gameId/users/:userId/scores", handle2(leaderboard.getUserScores));
123757
123876
  });
123758
123877
  var gameSecretsRouter;
@@ -124450,6 +124569,7 @@ async function startServer(port, project, options = {}) {
124450
124569
  const mainServer = serve({ fetch: app.fetch, port });
124451
124570
  return {
124452
124571
  main: mainServer,
124572
+ db: db2,
124453
124573
  gameId,
124454
124574
  timebackMode: getTimebackDisplayMode(),
124455
124575
  setRole: (role) => {
@@ -125201,6 +125321,7 @@ var init_achievements4 = __esm7(() => {
125201
125321
  ];
125202
125322
  });
125203
125323
  var AUTH_PROVIDER_IDS2;
125324
+ var DEMO_DISPLAY_NAME_PLACEHOLDER2 = "Demo Player";
125204
125325
  var init_auth3 = __esm7(() => {
125205
125326
  AUTH_PROVIDER_IDS2 = {
125206
125327
  TIMEBACK: "timeback",
@@ -125285,7 +125406,8 @@ var init_overworld2 = __esm7(() => {
125285
125406
  ERROR: 4000
125286
125407
  };
125287
125408
  CORE_GAME_UUIDS2 = {
125288
- PLAYGROUND: "00000000-0000-0000-0000-000000000001"
125409
+ PLAYGROUND: "00000000-0000-0000-0000-000000000001",
125410
+ DEMO: "00000000-0000-0000-0000-000000000002"
125289
125411
  };
125290
125412
  });
125291
125413
  var TIMEBACK_ROUTES2;
@@ -125359,13 +125481,15 @@ var DEMO_USER_IDS2;
125359
125481
  var DEMO_USERS2;
125360
125482
  var DEMO_USER2;
125361
125483
  var init_demo_users2 = __esm7(() => {
125484
+ init_src5();
125362
125485
  now2 = new Date;
125363
125486
  DEMO_USER_IDS2 = {
125364
125487
  player: "00000000-0000-0000-0000-000000000001",
125365
125488
  developer: "00000000-0000-0000-0000-000000000002",
125366
125489
  admin: "00000000-0000-0000-0000-000000000003",
125367
125490
  pendingDeveloper: "00000000-0000-0000-0000-000000000004",
125368
- unverifiedPlayer: "00000000-0000-0000-0000-000000000005"
125491
+ unverifiedPlayer: "00000000-0000-0000-0000-000000000005",
125492
+ anonymousPlayer: "00000000-0000-0000-0000-000000000006"
125369
125493
  };
125370
125494
  DEMO_USERS2 = {
125371
125495
  admin: {
@@ -125427,6 +125551,19 @@ var init_demo_users2 = __esm7(() => {
125427
125551
  developerStatus: "none",
125428
125552
  createdAt: now2,
125429
125553
  updatedAt: now2
125554
+ },
125555
+ anonymousPlayer: {
125556
+ id: DEMO_USER_IDS2.anonymousPlayer,
125557
+ name: DEMO_DISPLAY_NAME_PLACEHOLDER2,
125558
+ username: "anonymous_demo_player",
125559
+ email: "player@anon.demo.playcademy.gg",
125560
+ emailVerified: false,
125561
+ image: null,
125562
+ isAnonymous: true,
125563
+ role: "player",
125564
+ developerStatus: "none",
125565
+ createdAt: now2,
125566
+ updatedAt: now2
125430
125567
  }
125431
125568
  };
125432
125569
  DEMO_USER2 = DEMO_USERS2.player;
@@ -125439,6 +125576,7 @@ var init_demo_tokens2 = __esm7(() => {
125439
125576
  "sandbox-demo-token": DEMO_USERS2.player,
125440
125577
  "sandbox-admin-token": DEMO_USERS2.admin,
125441
125578
  "sandbox-player-token": DEMO_USERS2.player,
125579
+ "sandbox-anonymous-token": DEMO_USERS2.anonymousPlayer,
125442
125580
  "sandbox-developer-token": DEMO_USERS2.developer,
125443
125581
  "sandbox-pending-dev-token": DEMO_USERS2.pendingDeveloper,
125444
125582
  "sandbox-unverified-token": DEMO_USERS2.unverifiedPlayer,
@@ -125447,6 +125585,7 @@ var init_demo_tokens2 = __esm7(() => {
125447
125585
  };
125448
125586
  SANDBOX_TOKENS2 = {
125449
125587
  player: "sandbox-player-token",
125588
+ anonymous: "sandbox-anonymous-token",
125450
125589
  developer: "sandbox-developer-token",
125451
125590
  admin: "sandbox-admin-token",
125452
125591
  pendingDeveloper: "sandbox-pending-dev-token",
@@ -125574,19 +125713,20 @@ var init_constants11 = __esm7(() => {
125574
125713
  init_constants11();
125575
125714
 
125576
125715
  // src/lib/sandbox/token.ts
125577
- var ROLE_TO_USER_ID = {
125716
+ var IDENTITY_TO_USER_ID = {
125578
125717
  player: DEMO_USER_IDS2.player,
125579
125718
  developer: DEMO_USER_IDS2.developer,
125580
- admin: DEMO_USER_IDS2.admin
125719
+ admin: DEMO_USER_IDS2.admin,
125720
+ anonymous: DEMO_USER_IDS2.anonymousPlayer
125581
125721
  };
125582
125722
  function encode(obj) {
125583
125723
  return btoa(JSON.stringify(obj));
125584
125724
  }
125585
- function createSandboxGameToken(gameSlug, role = "player") {
125725
+ function createSandboxGameToken(gameSlug, identity = "player") {
125586
125726
  const header = { alg: "none", typ: "sandbox" };
125587
125727
  const payload = {
125588
125728
  sub: gameSlug,
125589
- uid: ROLE_TO_USER_ID[role],
125729
+ uid: IDENTITY_TO_USER_ID[identity],
125590
125730
  iat: Math.floor(Date.now() / 1000)
125591
125731
  };
125592
125732
  return `${encode(header)}.${encode(payload)}.sandbox`;
@@ -125658,6 +125798,7 @@ var shell_default = `<!doctype html>
125658
125798
  gameId: '{{GAME_ID}}',
125659
125799
  gameToken: '{{GAME_TOKEN}}',
125660
125800
  gameUrl: '{{GAME_URL}}' || undefined,
125801
+ mode: '{{MODE}}',
125661
125802
  timebackJson: '{{TIMEBACK_DATA}}',
125662
125803
  hideBadge: '{{HIDE_BADGE}}' === 'true',
125663
125804
  }
@@ -125680,10 +125821,13 @@ var shell_default = `<!doctype html>
125680
125821
  const log = (...args) => window.PLAYCADEMY_DEBUG && console.log('[DevShell]', ...args)
125681
125822
 
125682
125823
  async function checkSandbox() {
125824
+ // Use the public /health endpoint as a pure liveness probe.
125825
+ // We used to hit /api/users/me here, but that's \`requireNonAnonymous\`
125826
+ // and 403s in demo mode (anonymous cohort) even when the sandbox
125827
+ // is perfectly healthy. /health is auth-bypassed and is all we
125828
+ // actually need for a reachability check.
125683
125829
  try {
125684
- const res = await fetch(\`\${CONFIG.sandboxUrl}/api/users/me\`, {
125685
- headers: { Authorization: \`Bearer \${CONFIG.gameToken}\` },
125686
- })
125830
+ const res = await fetch(\`\${CONFIG.sandboxUrl}/health\`)
125687
125831
  if (!res.ok) throw new Error('Sandbox unavailable')
125688
125832
  badge.classList.remove('offline')
125689
125833
  log('Sandbox connected')
@@ -125702,6 +125846,7 @@ var shell_default = `<!doctype html>
125702
125846
  gameUrl: CONFIG.gameUrl,
125703
125847
  gameId: CONFIG.gameId,
125704
125848
  token: CONFIG.gameToken,
125849
+ mode: CONFIG.mode,
125705
125850
  timeback: timebackData,
125706
125851
  }
125707
125852
 
@@ -125756,15 +125901,17 @@ var shell_default = `<!doctype html>
125756
125901
  `;
125757
125902
 
125758
125903
  // src/server/middleware.ts
125759
- function generateLoaderHTML(sandboxUrl, gameSlug, options, gameUrl) {
125904
+ function generateLoaderHTML(sandboxUrl, gameSlug, gameId, options, gameUrl) {
125760
125905
  const timebackJson = generateTimebackJson(options.timeback);
125761
- const platformRole = getPlatformRoleOverride() ?? "player";
125762
- const gameToken = createSandboxGameToken(gameSlug, platformRole);
125763
- return shell_default.replace(/{{SANDBOX_URL}}/g, sandboxUrl).replace(/{{GAME_ID}}/g, gameSlug).replace(/{{GAME_TOKEN}}/g, gameToken).replace(/{{GAME_URL}}/g, gameUrl || "").replace(/{{TIMEBACK_DATA}}/g, timebackJson).replace(/{{HIDE_BADGE}}/g, String(options.hideBadge));
125906
+ const mode = getCurrentMode();
125907
+ const tokenIdentity = mode === "demo" ? "anonymous" : getPlatformRoleOverride() ?? "player";
125908
+ const gameToken = createSandboxGameToken(gameSlug, tokenIdentity);
125909
+ const handshakeGameId = gameId || gameSlug;
125910
+ return shell_default.replace(/{{SANDBOX_URL}}/g, sandboxUrl).replace(/{{GAME_ID}}/g, handshakeGameId).replace(/{{GAME_TOKEN}}/g, gameToken).replace(/{{GAME_URL}}/g, gameUrl || "").replace(/{{MODE}}/g, isShellMode(mode) ? mode : "platform").replace(/{{TIMEBACK_DATA}}/g, timebackJson).replace(/{{HIDE_BADGE}}/g, String(options.hideBadge));
125764
125911
  }
125765
125912
  function devServerMiddleware(server, sandbox, gameUrl, options) {
125766
125913
  server.middlewares.use("/", (req, res, next) => {
125767
- if (getCurrentMode() !== "platform") {
125914
+ if (!isShellMode(getCurrentMode())) {
125768
125915
  next();
125769
125916
  return;
125770
125917
  }
@@ -125774,7 +125921,7 @@ function devServerMiddleware(server, sandbox, gameUrl, options) {
125774
125921
  next();
125775
125922
  } else {
125776
125923
  res.setHeader("Content-Type", "text/html");
125777
- res.end(generateLoaderHTML(sandbox.baseUrl, sandbox.project?.slug ?? "", options, gameUrl));
125924
+ res.end(generateLoaderHTML(sandbox.baseUrl, sandbox.project?.slug ?? "", sandbox.gameId ?? "", options, gameUrl));
125778
125925
  }
125779
125926
  return;
125780
125927
  }
@@ -125792,9 +125939,9 @@ async function recreateSandbox(options) {
125792
125939
  viteConfig.logger.error(`${prefix2} ${import_picocolors7.red("Cannot recreate sandbox database: no Vite server reference")}`);
125793
125940
  return { success: false, error: "No Vite server reference" };
125794
125941
  }
125795
- if (currentMode !== "platform") {
125796
- viteConfig.logger.warn(`${prefix2} ${import_picocolors7.yellow("can only recreate sandbox database in platform mode (m + enter)")}`);
125797
- return { success: false, error: "Not in platform mode" };
125942
+ if (!isShellMode(currentMode)) {
125943
+ viteConfig.logger.warn(`${prefix2} ${import_picocolors7.yellow("can only recreate sandbox database in platform or demo mode")}`);
125944
+ return { success: false, error: "Not in a shell-backed mode" };
125798
125945
  }
125799
125946
  viteConfig.logger.info(`${prefix2} recreating database...`);
125800
125947
  if (serverState.sandbox) {
@@ -125843,7 +125990,7 @@ function createChangeHandler(server, viteConfig, platformModeOptions, watchedFil
125843
125990
  if (!isWatchedFile) {
125844
125991
  return;
125845
125992
  }
125846
- if (getCurrentMode() !== "platform") {
125993
+ if (!isShellMode(getCurrentMode())) {
125847
125994
  return;
125848
125995
  }
125849
125996
  if (debounceTimer) {
@@ -125949,7 +126096,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
125949
126096
  // package.json
125950
126097
  var package_default2 = {
125951
126098
  name: "@playcademy/vite-plugin",
125952
- version: "0.2.24",
126099
+ version: "0.2.25-beta.1",
125953
126100
  type: "module",
125954
126101
  exports: {
125955
126102
  ".": {
@@ -126191,7 +126338,7 @@ async function configureStandaloneMode(server, viteConfig, options) {
126191
126338
  // src/server/hotkeys/toggle-mode.ts
126192
126339
  async function toggleMode(options) {
126193
126340
  const currentMode = getCurrentMode();
126194
- const newMode = currentMode === "platform" ? "standalone" : "platform";
126341
+ const newMode = getNextMode(currentMode);
126195
126342
  const viteServer = getViteServerRef();
126196
126343
  const prefix2 = createLogPrefix("playcademy");
126197
126344
  if (!viteServer) {
@@ -126214,7 +126361,7 @@ async function toggleMode(options) {
126214
126361
  function toggleModeHotkey(options) {
126215
126362
  return {
126216
126363
  key: "m",
126217
- description: `${import_picocolors12.cyan(import_picocolors12.bold("[playcademy]"))} toggle platform/standalone mode`,
126364
+ description: `${import_picocolors12.cyan(import_picocolors12.bold("[playcademy]"))} cycle platform/demo/standalone mode`,
126218
126365
  action: () => toggleMode(options)
126219
126366
  };
126220
126367
  }
@@ -12,4 +12,14 @@
12
12
  * - signature: "sandbox" (not cryptographically verified)
13
13
  */
14
14
  import type { PlatformRoleOverride } from '../../types';
15
- export declare function createSandboxGameToken(gameSlug: string, role?: PlatformRoleOverride): string;
15
+ /**
16
+ * Identities the dev shell can mint sandbox tokens for.
17
+ *
18
+ * Platform roles (player/developer/admin) are driven by the role-cycle
19
+ * hotkey and only make sense in `platform` mode. `anonymous` resolves to
20
+ * the seeded `DEMO_USERS.anonymousPlayer` and is what we mint in `demo`
21
+ * mode so demo-only APIs (`/users/demo-profile`, anonymous leaderboard
22
+ * reads, etc.) can authenticate against an actual anonymous cohort.
23
+ */
24
+ export type SandboxTokenIdentity = PlatformRoleOverride | 'anonymous';
25
+ export declare function createSandboxGameToken(gameSlug: string, identity?: SandboxTokenIdentity): string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * 'm' hotkey - toggle platform/standalone mode
2
+ * 'm' hotkey - cycle platform/demo/standalone mode
3
3
  */
4
4
  import type { HotkeyOptions } from '../../types';
5
5
  export declare function toggleModeHotkey(options: HotkeyOptions): {
@@ -0,0 +1,4 @@
1
+ import type { PlaycademyMode } from '../types/options';
2
+ export type ShellMode = Exclude<PlaycademyMode, 'standalone'>;
3
+ export declare function isShellMode(mode: PlaycademyMode): mode is ShellMode;
4
+ export declare function getNextMode(mode: PlaycademyMode): PlaycademyMode;
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Platform mode server configuration
3
- * Full Playcademy platform: sandbox + backend + shell wrapper
2
+ * Shell-backed mode server configuration
3
+ * Sandbox + backend + shell wrapper for platform and demo flows
4
4
  */
5
5
  import type { ResolvedConfig, ViteDevServer } from 'vite';
6
6
  import type { PlatformModeOptions } from '../types';
7
7
  /**
8
- * Configure server in platform mode (sandbox + backend + shell)
8
+ * Configure server in a shell-backed mode (sandbox + backend + shell)
9
9
  */
10
10
  export declare function configurePlatformMode(server: ViteDevServer, viteConfig: ResolvedConfig, options: PlatformModeOptions): Promise<void>;
@@ -118,7 +118,7 @@ export interface CliDevServerOptions {
118
118
  gameId?: string;
119
119
  }
120
120
  /**
121
- * Options for platform mode (sandbox + backend + shell)
121
+ * Options for shell-backed modes (sandbox + backend + shell)
122
122
  */
123
123
  export interface PlatformModeOptions {
124
124
  startSandbox: boolean;
@@ -155,7 +155,7 @@ export interface HotkeyOptions {
155
155
  export interface BannerOptions {
156
156
  /** Plugin version */
157
157
  version: string;
158
- /** Game/project name (platform mode only) */
158
+ /** Game/project name (shell-backed modes only) */
159
159
  gameName?: string;
160
160
  /** Sandbox server info */
161
161
  sandbox?: {
@@ -170,7 +170,7 @@ export interface BannerOptions {
170
170
  /** Vite port for proxied URL display */
171
171
  vitePort?: number;
172
172
  };
173
- /** Timeback info (platform mode only) */
173
+ /** Timeback info (shell-backed modes only) */
174
174
  timeback?: {
175
175
  courseCount: number;
176
176
  enrolledCount: number;
@@ -6,11 +6,12 @@
6
6
  *
7
7
  * Controls how the Vite plugin operates during development:
8
8
  * - `'platform'`: Full Playcademy platform experience with sandbox server, backend bundling, and shell wrapper (default)
9
+ * - `'demo'`: Shell-backed development mode that initializes the SDK with `mode: 'demo'`
9
10
  * - `'standalone'`: Backend only, no sandbox or shell
10
11
  *
11
12
  * @default 'platform'
12
13
  */
13
- export type PlaycademyMode = 'platform' | 'standalone';
14
+ export type PlaycademyMode = 'platform' | 'demo' | 'standalone';
14
15
  /**
15
16
  * Configuration options for exporting/building Playcademy games
16
17
  *
@@ -254,7 +255,7 @@ export interface PlaycademyTimebackOptions {
254
255
  * Configuration options for the development shell wrapper
255
256
  *
256
257
  * The shell provides the platform UI during development, including the
257
- * Playcademy badge, game selection, and other platform features.
258
+ * Playcademy badge and iframe wrapper used by `platform` and `demo` modes.
258
259
  */
259
260
  export interface PlaycademyDisplayOptions {
260
261
  /**
@@ -318,9 +319,11 @@ export interface PlaycademyPluginOptions {
318
319
  * Plugin operation mode.
319
320
  *
320
321
  * - `'platform'`: Full development experience with sandbox server and shell (recommended)
322
+ * - `'demo'`: Sandbox + shell, but initializes the SDK with `mode: 'demo'`
321
323
  * - `'standalone'`: Backend bundling only, no platform features
322
324
  *
323
- * Most games should use `'platform'` mode.
325
+ * Most games should use `'platform'` mode; use `'demo'` to exercise
326
+ * anonymous/demo-specific SDK flows during local development.
324
327
  *
325
328
  * @default 'platform'
326
329
  * @example
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "0.2.24",
3
+ "version": "0.2.25-beta.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {