@playcademy/vite-plugin 0.2.1 → 0.2.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +259 -103
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -176083,6 +176083,21 @@ async function mintGameJwt(gameId, userId) {
176083
176083
  const token = await signJwt({ uid: userId }, "game", expirationTimeSeconds, gameId);
176084
176084
  return { token, exp: expirationTimestampMillis };
176085
176085
  }
176086
+ async function mintRealtimeJwt(userId, gameId, username, role) {
176087
+ const payload = {
176088
+ sub: userId
176089
+ };
176090
+ if (gameId) {
176091
+ payload.gameId = gameId;
176092
+ }
176093
+ if (username) {
176094
+ payload.username = username;
176095
+ }
176096
+ if (role) {
176097
+ payload.role = role;
176098
+ }
176099
+ return await signJwt(payload, "realtime", "30m");
176100
+ }
176086
176101
  function formatValidationErrors(error2) {
176087
176102
  const flattened = error2.flatten();
176088
176103
  const fieldErrors = Object.entries(flattened.fieldErrors).map(([field, errors2]) => `${field}: ${errors2?.join(", ") || "Invalid"}`).join("; ");
@@ -186438,6 +186453,55 @@ async function verifyGameAccessBySlug(slug2, user) {
186438
186453
  function getGameWorkerApiKeyName(slug2) {
186439
186454
  return `game-worker-${slug2}`.substring(0, 32);
186440
186455
  }
186456
+ var import_client_s32 = __toESM2(require_dist_cjs81(), 1);
186457
+ function getSecretsConfig() {
186458
+ const bucketName = process.env.GAME_SECRETS_BUCKET;
186459
+ const masterKey = process.env.GAME_SECRETS_MASTER_KEY;
186460
+ if (!bucketName || !masterKey) {
186461
+ throw new Error("Secrets storage not configured");
186462
+ }
186463
+ const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
186464
+ const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
186465
+ const endpoint = process.env.CLOUDFLARE_R2_DEFAULT_ENDPOINT;
186466
+ if (!accessKeyId || !secretAccessKey || !endpoint) {
186467
+ throw new Error("R2 credentials not configured");
186468
+ }
186469
+ return {
186470
+ bucketName,
186471
+ credentials: {
186472
+ accessKeyId,
186473
+ secretAccessKey,
186474
+ endpoint
186475
+ },
186476
+ masterKey
186477
+ };
186478
+ }
186479
+ function getSecretsKey(gameId) {
186480
+ return `${gameId}.json.enc`;
186481
+ }
186482
+ function createR2Client(config2) {
186483
+ return new import_client_s32.S3Client({
186484
+ region: "auto",
186485
+ endpoint: config2.credentials.endpoint,
186486
+ credentials: {
186487
+ accessKeyId: config2.credentials.accessKeyId,
186488
+ secretAccessKey: config2.credentials.secretAccessKey
186489
+ }
186490
+ });
186491
+ }
186492
+ async function deleteSecrets(gameId, config2) {
186493
+ const client = createR2Client(config2);
186494
+ const key = getSecretsKey(gameId);
186495
+ try {
186496
+ await client.send(new import_client_s32.DeleteObjectCommand({
186497
+ Bucket: config2.bucketName,
186498
+ Key: key
186499
+ }));
186500
+ } catch (error2) {
186501
+ log2.error("[SecretsStorage] Failed to delete secrets", { gameId, error: error2 });
186502
+ throw new Error(`Failed to delete secrets for game ${gameId}`);
186503
+ }
186504
+ }
186441
186505
  async function seedAchievements(db) {
186442
186506
  const now2 = new Date;
186443
186507
  for (const def of ACHIEVEMENT_DEFINITIONS) {
@@ -186477,6 +186541,29 @@ async function seedCurrencies(db) {
186477
186541
  });
186478
186542
  }
186479
186543
  }
186544
+ var customLogger;
186545
+ function setLogger(logger3) {
186546
+ customLogger = logger3;
186547
+ }
186548
+ function getLogger() {
186549
+ if (customLogger) {
186550
+ return customLogger;
186551
+ }
186552
+ return {
186553
+ info: (msg) => console.log(msg),
186554
+ warn: (msg) => console.warn(msg),
186555
+ error: (msg) => console.error(msg)
186556
+ };
186557
+ }
186558
+ var logger3 = {
186559
+ info: (msg) => {
186560
+ if (customLogger || !config.embedded) {
186561
+ getLogger().info(msg);
186562
+ }
186563
+ },
186564
+ warn: (msg) => getLogger().warn(msg),
186565
+ error: (msg) => getLogger().error(msg)
186566
+ };
186480
186567
  function generateMockStudentId(userId) {
186481
186568
  return `mock-student-${userId.slice(-8)}`;
186482
186569
  }
@@ -186538,7 +186625,7 @@ async function seedCoreGames(db) {
186538
186625
  try {
186539
186626
  await db.insert(games).values(gameData).onConflictDoNothing();
186540
186627
  } catch (error2) {
186541
- console.error(`Error seeding core game '${gameData.slug}':`, error2);
186628
+ logger3.error(`Error seeding core game '${gameData.slug}': ${error2}`);
186542
186629
  }
186543
186630
  }
186544
186631
  }
@@ -186579,7 +186666,7 @@ async function seedCurrentProjectGame(db, project) {
186579
186666
  }
186580
186667
  return newGame;
186581
186668
  } catch (error2) {
186582
- console.error("❌ Error seeding project game:", error2);
186669
+ logger3.error(`❌ Error seeding project game: ${error2}`);
186583
186670
  throw error2;
186584
186671
  }
186585
186672
  }
@@ -186696,10 +186783,6 @@ async function setupServerDatabase(processedOptions, project) {
186696
186783
  return db;
186697
186784
  }
186698
186785
  var import_json_colorizer = __toESM2(require_dist22(), 1);
186699
- var customLogger;
186700
- function setLogger(logger3) {
186701
- customLogger = logger3;
186702
- }
186703
186786
  function processServerOptions(_port, options) {
186704
186787
  const {
186705
186788
  verbose = false,
@@ -186824,6 +186907,8 @@ manifestRouter.get("/", async (c3) => {
186824
186907
  { method: "GET", url: `${baseUrl}/api/notifications/stats/:userId` },
186825
186908
  { method: "POST", url: `${baseUrl}/api/notifications/deliver` },
186826
186909
  { method: "POST", url: `${baseUrl}/api/timeback/populate-student` },
186910
+ { method: "GET", url: `${baseUrl}/api/timeback/user` },
186911
+ { method: "GET", url: `${baseUrl}/api/timeback/user/:timebackId` },
186827
186912
  { method: "GET", url: `${baseUrl}/api/timeback/xp/today` },
186828
186913
  { method: "PUT", url: `${baseUrl}/api/timeback/xp/today` },
186829
186914
  { method: "GET", url: `${baseUrl}/api/timeback/xp/total` },
@@ -192046,6 +192131,10 @@ async function getMockTimebackData(db, timebackId, gameId) {
192046
192131
  });
192047
192132
  return { id: timebackId, role, enrollments, organizations };
192048
192133
  }
192134
+ async function getMockTimebackUser(db, gameId) {
192135
+ const timebackId = config.timeback.timebackId || "mock-student-00000001";
192136
+ return getMockTimebackData(db, timebackId, gameId);
192137
+ }
192049
192138
  async function buildMockUserResponse(db, user, gameId) {
192050
192139
  const timeback3 = user.timebackId ? await getMockTimebackData(db, user.timebackId, gameId) : undefined;
192051
192140
  if (gameId) {
@@ -192174,55 +192263,6 @@ usersRouter.all("/", async (c3) => {
192174
192263
  const error2 = ApiError.methodNotAllowed("Method not allowed");
192175
192264
  return c3.json(createErrorResponse(error2), 405);
192176
192265
  });
192177
- var import_client_s32 = __toESM2(require_dist_cjs81(), 1);
192178
- function getSecretsConfig() {
192179
- const bucketName = process.env.GAME_SECRETS_BUCKET;
192180
- const masterKey = process.env.GAME_SECRETS_MASTER_KEY;
192181
- if (!bucketName || !masterKey) {
192182
- throw new Error("Secrets storage not configured");
192183
- }
192184
- const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
192185
- const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
192186
- const endpoint = process.env.CLOUDFLARE_R2_DEFAULT_ENDPOINT;
192187
- if (!accessKeyId || !secretAccessKey || !endpoint) {
192188
- throw new Error("R2 credentials not configured");
192189
- }
192190
- return {
192191
- bucketName,
192192
- credentials: {
192193
- accessKeyId,
192194
- secretAccessKey,
192195
- endpoint
192196
- },
192197
- masterKey
192198
- };
192199
- }
192200
- function getSecretsKey(gameId) {
192201
- return `${gameId}.json.enc`;
192202
- }
192203
- function createR2Client(config2) {
192204
- return new import_client_s32.S3Client({
192205
- region: "auto",
192206
- endpoint: config2.credentials.endpoint,
192207
- credentials: {
192208
- accessKeyId: config2.credentials.accessKeyId,
192209
- secretAccessKey: config2.credentials.secretAccessKey
192210
- }
192211
- });
192212
- }
192213
- async function deleteSecrets(gameId, config2) {
192214
- const client = createR2Client(config2);
192215
- const key = getSecretsKey(gameId);
192216
- try {
192217
- await client.send(new import_client_s32.DeleteObjectCommand({
192218
- Bucket: config2.bucketName,
192219
- Key: key
192220
- }));
192221
- } catch (error2) {
192222
- log2.error("[SecretsStorage] Failed to delete secrets", { gameId, error: error2 });
192223
- throw new Error(`Failed to delete secrets for game ${gameId}`);
192224
- }
192225
- }
192226
192266
  function assertError(err2) {
192227
192267
  if (!isError(err2)) {
192228
192268
  throw new Error("Parameter was not an error");
@@ -196725,7 +196765,7 @@ async function deliverNotifications(ctx) {
196725
196765
  if (!user) {
196726
196766
  throw ApiError.unauthorized("Must be logged in to deliver notifications");
196727
196767
  }
196728
- log2.info("[API] Delivering pending notifications", {
196768
+ log2.debug("[API] Delivering pending notifications", {
196729
196769
  userId: user.id
196730
196770
  });
196731
196771
  try {
@@ -196831,6 +196871,56 @@ notificationsRouter.post("/deliver", async (c3) => {
196831
196871
  return c3.json(createUnknownErrorResponse(error2), 500);
196832
196872
  }
196833
196873
  });
196874
+ async function generateRealtimeToken(ctx) {
196875
+ const user = ctx.user;
196876
+ const gameId = ctx.params?.gameId;
196877
+ log2.debug("[API] generating realtime token", {
196878
+ userId: user?.id || "anonymous",
196879
+ gameId: gameId || "none"
196880
+ });
196881
+ if (!user) {
196882
+ throw ApiError.unauthorized("Valid session or bearer token required");
196883
+ }
196884
+ try {
196885
+ if (gameId) {
196886
+ const db = getDatabase();
196887
+ const game = await db.query.games.findFirst({
196888
+ where: eq(games.id, gameId),
196889
+ columns: { id: true }
196890
+ });
196891
+ if (!game) {
196892
+ throw ApiError.notFound("Game not found");
196893
+ }
196894
+ }
196895
+ const displayName = user.username || (user.name ? user.name.split(" ")[0] : undefined) || undefined;
196896
+ const token2 = await mintRealtimeJwt(user.id, gameId, displayName, user.role || undefined);
196897
+ return { token: token2 };
196898
+ } catch (error2) {
196899
+ if (error2 instanceof ApiError)
196900
+ throw error2;
196901
+ log2.error("[API /realtime/token] Failed to generate realtime token:", { error: error2 });
196902
+ throw ApiError.internal("Internal server error", error2);
196903
+ }
196904
+ }
196905
+ var realtimeRouter = new Hono2;
196906
+ realtimeRouter.post("/token", async (c3) => {
196907
+ const ctx = {
196908
+ user: c3.get("user"),
196909
+ params: {},
196910
+ url: new URL(c3.req.url),
196911
+ request: c3.req.raw
196912
+ };
196913
+ try {
196914
+ const result = await generateRealtimeToken(ctx);
196915
+ return c3.json(result);
196916
+ } catch (error2) {
196917
+ if (error2 instanceof ApiError) {
196918
+ return c3.json(createErrorResponse(error2), error2.statusCode);
196919
+ }
196920
+ console.error("Error in realtime/token:", error2);
196921
+ return c3.json(createUnknownErrorResponse(error2), 500);
196922
+ }
196923
+ });
196834
196924
  function isTimebackApiError(error2) {
196835
196925
  return typeof error2 === "object" && error2 !== null && "name" in error2 && error2.name === "TimebackApiError" && "status" in error2 && "details" in error2;
196836
196926
  }
@@ -197360,23 +197450,58 @@ async function getTimeBackXpHistory(ctx) {
197360
197450
  throw ApiError.internal("Failed to get TimeBack XP history", error2);
197361
197451
  }
197362
197452
  }
197363
- async function getStudentEnrollments(ctx) {
197453
+ async function getTimebackUser(ctx) {
197454
+ const user = ctx.user;
197455
+ if (!user) {
197456
+ throw ApiError.unauthorized("Must be logged in to get timeback data");
197457
+ }
197458
+ const db = getDatabase();
197459
+ const userData = await db.query.users.findFirst({
197460
+ where: eq(users.id, user.id)
197461
+ });
197462
+ if (!userData) {
197463
+ throw ApiError.notFound("User not found");
197464
+ }
197465
+ if (!userData.timebackId) {
197466
+ throw ApiError.notFound("User does not have a TimeBack account");
197467
+ }
197468
+ log2.debug("[API] Getting timeback user data", {
197469
+ userId: user.id,
197470
+ timebackId: userData.timebackId,
197471
+ gameId: ctx.gameId
197472
+ });
197473
+ const timeback3 = await fetchUserTimebackData(userData.timebackId, ctx.gameId);
197474
+ log2.info("[API] Retrieved timeback user data", {
197475
+ userId: user.id,
197476
+ timebackId: userData.timebackId,
197477
+ role: timeback3.role,
197478
+ enrollmentCount: timeback3.enrollments.length,
197479
+ organizationCount: timeback3.organizations.length
197480
+ });
197481
+ return timeback3;
197482
+ }
197483
+ async function getTimebackUserById(ctx) {
197364
197484
  const user = ctx.user;
197365
197485
  if (!user) {
197366
- throw ApiError.unauthorized("Must be logged in to get enrollments");
197486
+ throw ApiError.unauthorized("Must be logged in to get timeback data");
197367
197487
  }
197368
197488
  const timebackId = ctx.params.timebackId;
197369
197489
  if (!timebackId) {
197370
197490
  throw ApiError.badRequest("Missing timebackId parameter");
197371
197491
  }
197372
- log2.debug("[API] Getting student enrollments", { userId: user.id, timebackId });
197373
- const enrollments = await fetchEnrollmentsFromEduBridge(timebackId);
197374
- log2.info("[API] Retrieved student enrollments", {
197375
- userId: user.id,
197492
+ log2.debug("[API] Getting timeback user by ID", {
197493
+ requesterId: user.id,
197494
+ timebackId
197495
+ });
197496
+ const timeback3 = await fetchUserTimebackData(timebackId);
197497
+ log2.info("[API] Retrieved timeback user by ID", {
197498
+ requesterId: user.id,
197376
197499
  timebackId,
197377
- enrollmentCount: enrollments.length
197500
+ role: timeback3.role,
197501
+ enrollmentCount: timeback3.enrollments.length,
197502
+ organizationCount: timeback3.organizations.length
197378
197503
  });
197379
- return { enrollments };
197504
+ return timeback3;
197380
197505
  }
197381
197506
  var timebackRouter = new Hono2;
197382
197507
  timebackRouter.post("/populate-student", async (c3) => {
@@ -197568,32 +197693,62 @@ timebackRouter.post("/end-activity", async (c3) => {
197568
197693
  return c3.json(createUnknownErrorResponse(error2), 500);
197569
197694
  }
197570
197695
  });
197571
- timebackRouter.get("/enrollments/:timebackId", async (c3) => {
197696
+ timebackRouter.get("/user", async (c3) => {
197697
+ const user2 = c3.get("user");
197698
+ const gameId = c3.get("gameId");
197699
+ if (!user2) {
197700
+ const error2 = ApiError.unauthorized("Must be logged in to get timeback data");
197701
+ return c3.json(createErrorResponse(error2), error2.statusCode);
197702
+ }
197703
+ try {
197704
+ if (shouldMockTimeback()) {
197705
+ const db = c3.get("db");
197706
+ const timeback3 = await getMockTimebackUser(db, gameId);
197707
+ return c3.json(timeback3);
197708
+ }
197709
+ const ctx = {
197710
+ user: user2,
197711
+ params: {},
197712
+ url: new URL(c3.req.url),
197713
+ request: c3.req.raw,
197714
+ gameId
197715
+ };
197716
+ const result = await getTimebackUser(ctx);
197717
+ return c3.json(result);
197718
+ } catch (error2) {
197719
+ if (error2 instanceof ApiError) {
197720
+ return c3.json(createErrorResponse(error2), error2.statusCode);
197721
+ }
197722
+ console.error("Error in getTimebackUser:", error2);
197723
+ return c3.json(createUnknownErrorResponse(error2), 500);
197724
+ }
197725
+ });
197726
+ timebackRouter.get("/user/:timebackId", async (c3) => {
197572
197727
  const timebackId = c3.req.param("timebackId");
197573
- const user = c3.get("user");
197574
- if (!user) {
197575
- const error2 = ApiError.unauthorized("Must be logged in to get enrollments");
197728
+ const user2 = c3.get("user");
197729
+ if (!user2) {
197730
+ const error2 = ApiError.unauthorized("Must be logged in to get timeback data");
197576
197731
  return c3.json(createErrorResponse(error2), error2.statusCode);
197577
197732
  }
197578
197733
  try {
197579
197734
  if (shouldMockTimeback()) {
197580
197735
  const db = c3.get("db");
197581
- const enrollments2 = await getMockEnrollments(db);
197582
- return c3.json({ enrollments: enrollments2 });
197736
+ const timeback3 = await getMockTimebackUser(db);
197737
+ return c3.json(timeback3);
197583
197738
  }
197584
197739
  const ctx = {
197585
- user,
197740
+ user: user2,
197586
197741
  params: { timebackId },
197587
197742
  url: new URL(c3.req.url),
197588
197743
  request: c3.req.raw
197589
197744
  };
197590
- const result = await getStudentEnrollments(ctx);
197745
+ const result = await getTimebackUserById(ctx);
197591
197746
  return c3.json(result);
197592
197747
  } catch (error2) {
197593
197748
  if (error2 instanceof ApiError) {
197594
197749
  return c3.json(createErrorResponse(error2), error2.statusCode);
197595
197750
  }
197596
- console.error("Error in getStudentEnrollments:", error2);
197751
+ console.error("Error in getTimebackUserById:", error2);
197597
197752
  return c3.json(createUnknownErrorResponse(error2), 500);
197598
197753
  }
197599
197754
  });
@@ -197628,29 +197783,29 @@ async function provisionUserFromLti(claims) {
197628
197783
  where: and(eq(accounts.accountId, ltiTimebackId), eq(accounts.providerId, providerId))
197629
197784
  });
197630
197785
  if (existingAccount) {
197631
- const user2 = await db.query.users.findFirst({
197786
+ const user3 = await db.query.users.findFirst({
197632
197787
  where: eq(users.id, existingAccount.userId)
197633
197788
  });
197634
- if (user2) {
197789
+ if (user3) {
197635
197790
  log2.info("[lti-timeback] Found existing user by LTI account linkage", {
197636
- userId: user2.id,
197791
+ userId: user3.id,
197637
197792
  ltiTimebackId
197638
197793
  });
197639
- return user2;
197794
+ return user3;
197640
197795
  }
197641
197796
  }
197642
- const user = await db.query.users.findFirst({
197797
+ const user2 = await db.query.users.findFirst({
197643
197798
  where: eq(users.email, email)
197644
197799
  });
197645
- if (user) {
197800
+ if (user2) {
197646
197801
  await db.transaction(async (tx) => {
197647
197802
  const existingLtiAccount = await tx.query.accounts.findFirst({
197648
- where: and(eq(accounts.userId, user.id), eq(accounts.providerId, providerId))
197803
+ where: and(eq(accounts.userId, user2.id), eq(accounts.providerId, providerId))
197649
197804
  });
197650
197805
  if (!existingLtiAccount) {
197651
197806
  await tx.insert(accounts).values({
197652
197807
  id: crypto7.randomUUID(),
197653
- userId: user.id,
197808
+ userId: user2.id,
197654
197809
  accountId: ltiTimebackId,
197655
197810
  providerId,
197656
197811
  accessToken: null,
@@ -197661,14 +197816,14 @@ async function provisionUserFromLti(claims) {
197661
197816
  updatedAt: new Date
197662
197817
  });
197663
197818
  log2.info("[lti-timeback] Linked existing user to LTI provider", {
197664
- userId: user.id,
197665
- email: user.email,
197819
+ userId: user2.id,
197820
+ email: user2.email,
197666
197821
  ltiTimebackId,
197667
197822
  note: "User may have different TimeBack ID from OAuth flow"
197668
197823
  });
197669
197824
  }
197670
197825
  });
197671
- return user;
197826
+ return user2;
197672
197827
  }
197673
197828
  const newUserId = crypto7.randomUUID();
197674
197829
  const createdUser = await db.transaction(async (tx) => {
@@ -197711,10 +197866,10 @@ async function processLtiLaunch(idToken) {
197711
197866
  try {
197712
197867
  const claims = await verifyLtiToken(idToken);
197713
197868
  validateLtiClaims(claims);
197714
- const user = await provisionUserFromLti(claims);
197869
+ const user2 = await provisionUserFromLti(claims);
197715
197870
  const targetUri = claims["https://purl.imsglobal.org/spec/lti/claim/target_link_uri"];
197716
197871
  return {
197717
- user,
197872
+ user: user2,
197718
197873
  targetUri,
197719
197874
  claims
197720
197875
  };
@@ -197734,45 +197889,45 @@ async function processTimeBackLtiLaunch(ctx) {
197734
197889
  }
197735
197890
  const currentHost = ctx.url.hostname;
197736
197891
  const launchResult = await processLtiLaunch(idToken);
197737
- const { user, targetUri, claims } = launchResult;
197892
+ const { user: user2, targetUri, claims } = launchResult;
197738
197893
  log2.info("[lti-timeback] User roles", {
197739
- userId: user.id,
197894
+ userId: user2.id,
197740
197895
  isLearner: LtiRoleChecks.isLearner(claims),
197741
197896
  isInstructor: LtiRoleChecks.isInstructor(claims),
197742
197897
  isAdministrator: LtiRoleChecks.isAdministrator(claims),
197743
197898
  allRoles: claims["https://purl.imsglobal.org/spec/lti/claim/roles"]
197744
197899
  });
197745
- const sessionToken = await createLtiSession(user.id);
197900
+ const sessionToken = await createLtiSession(user2.id);
197746
197901
  const redirectPath = extractRedirectPath(targetUri, currentHost);
197747
197902
  log2.info("[lti-timeback] LTI launch successful", {
197748
- userId: user.id,
197903
+ userId: user2.id,
197749
197904
  redirectPath
197750
197905
  });
197751
197906
  return {
197752
- user,
197907
+ user: user2,
197753
197908
  redirectPath,
197754
197909
  sessionToken
197755
197910
  };
197756
197911
  }
197757
197912
  async function getTimeBackLtiStatus(ctx) {
197758
- const user = ctx.user;
197759
- if (!user) {
197913
+ const user2 = ctx.user;
197914
+ if (!user2) {
197760
197915
  throw ApiError.unauthorized("Must be logged in");
197761
197916
  }
197762
197917
  const db = getDatabase();
197763
197918
  const ltiAccount = await db.query.accounts.findFirst({
197764
- where: and(eq(accounts.userId, user.id), eq(accounts.providerId, AUTH_PROVIDER_IDS.TIMEBACK_LTI))
197919
+ where: and(eq(accounts.userId, user2.id), eq(accounts.providerId, AUTH_PROVIDER_IDS.TIMEBACK_LTI))
197765
197920
  });
197766
197921
  const oauthAccount = await db.query.accounts.findFirst({
197767
- where: and(eq(accounts.userId, user.id), eq(accounts.providerId, AUTH_PROVIDER_IDS.TIMEBACK))
197922
+ where: and(eq(accounts.userId, user2.id), eq(accounts.providerId, AUTH_PROVIDER_IDS.TIMEBACK))
197768
197923
  });
197769
197924
  return {
197770
197925
  hasLtiAccount: !!ltiAccount,
197771
197926
  ltiTimebackId: ltiAccount?.accountId || undefined,
197772
197927
  hasOAuthAccount: !!oauthAccount,
197773
- oauthTimebackId: oauthAccount?.accountId || user.timebackId || undefined,
197774
- email: user.email,
197775
- userId: user.id
197928
+ oauthTimebackId: oauthAccount?.accountId || user2.timebackId || undefined,
197929
+ email: user2.email,
197930
+ userId: user2.id
197776
197931
  };
197777
197932
  }
197778
197933
  var ltiRouter = new Hono2;
@@ -197809,20 +197964,20 @@ ltiRouter.all("/launch", async (c3) => {
197809
197964
  }
197810
197965
  });
197811
197966
  ltiRouter.get("/status", async (c3) => {
197812
- let user = undefined;
197967
+ let user2 = undefined;
197813
197968
  const authHeader = c3.req.header("Authorization");
197814
197969
  if (authHeader?.startsWith("Bearer ")) {
197815
197970
  const token2 = authHeader.substring(7);
197816
197971
  const demoUser = DEMO_TOKENS[token2];
197817
197972
  if (demoUser) {
197818
197973
  const db = c3.get("db");
197819
- user = await db.query.users.findFirst({
197974
+ user2 = await db.query.users.findFirst({
197820
197975
  where: eq(users.id, demoUser.id)
197821
197976
  });
197822
197977
  }
197823
197978
  }
197824
197979
  const ctx = {
197825
- user,
197980
+ user: user2,
197826
197981
  params: {},
197827
197982
  url: new URL(c3.req.url),
197828
197983
  request: c3.req.raw
@@ -197856,6 +198011,7 @@ function registerRoutes(app) {
197856
198011
  app.route("/api/sprites", spriteRouter);
197857
198012
  app.route("/api/achievements", achievementsRouter);
197858
198013
  app.route("/api/notifications", notificationsRouter);
198014
+ app.route("/api/realtime", realtimeRouter);
197859
198015
  app.route("/api/dev", devRouter);
197860
198016
  app.route("/api/timeback", timebackRouter);
197861
198017
  app.route("/api/lti", ltiRouter);
@@ -198752,7 +198908,7 @@ var import_picocolors11 = __toESM(require_picocolors(), 1);
198752
198908
  // package.json
198753
198909
  var package_default2 = {
198754
198910
  name: "@playcademy/vite-plugin",
198755
- version: "0.2.1",
198911
+ version: "0.2.2-alpha.1",
198756
198912
  type: "module",
198757
198913
  exports: {
198758
198914
  ".": {
@@ -198771,7 +198927,6 @@ var package_default2 = {
198771
198927
  pub: "bun publish.ts"
198772
198928
  },
198773
198929
  dependencies: {
198774
- "@playcademy/utils": "workspace:*",
198775
198930
  archiver: "^7.0.1",
198776
198931
  picocolors: "^1.1.1",
198777
198932
  playcademy: "workspace:*"
@@ -198779,6 +198934,7 @@ var package_default2 = {
198779
198934
  devDependencies: {
198780
198935
  "@inquirer/prompts": "^7.8.6",
198781
198936
  "@playcademy/sandbox": "workspace:*",
198937
+ "@playcademy/utils": "workspace:*",
198782
198938
  "@types/archiver": "^6.0.3",
198783
198939
  "@types/bun": "latest"
198784
198940
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "0.2.1",
3
+ "version": "0.2.2-alpha.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -19,14 +19,14 @@
19
19
  "pub": "bun publish.ts"
20
20
  },
21
21
  "dependencies": {
22
- "@playcademy/utils": "0.0.1",
23
22
  "archiver": "^7.0.1",
24
23
  "picocolors": "^1.1.1",
25
- "playcademy": "0.14.27"
24
+ "playcademy": "0.14.28"
26
25
  },
27
26
  "devDependencies": {
28
27
  "@inquirer/prompts": "^7.8.6",
29
28
  "@playcademy/sandbox": "0.3.6",
29
+ "@playcademy/utils": "0.0.1",
30
30
  "@types/archiver": "^6.0.3",
31
31
  "@types/bun": "latest"
32
32
  },