@playcademy/vite-plugin 1.1.2-beta.1 → 1.1.2-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +120 -8
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -23746,7 +23746,7 @@ import path from "node:path";
23746
23746
  // package.json
23747
23747
  var package_default = {
23748
23748
  name: "@playcademy/vite-plugin",
23749
- version: "1.1.2-beta.1",
23749
+ version: "1.1.2-beta.2",
23750
23750
  type: "module",
23751
23751
  exports: {
23752
23752
  ".": {
@@ -24476,6 +24476,7 @@ var init_game = __esm(() => {
24476
24476
  COLLABORATOR: "collaborator"
24477
24477
  };
24478
24478
  });
24479
+ var PLAYCADEMY_BROWSER_TIME_ZONE_HEADER = "x-playcademy-browser-time-zone";
24479
24480
  var CORE_GAME_UUIDS;
24480
24481
  var init_platform = __esm(() => {
24481
24482
  CORE_GAME_UUIDS = {
@@ -25344,7 +25345,7 @@ var package_default2;
25344
25345
  var init_package = __esm(() => {
25345
25346
  package_default2 = {
25346
25347
  name: "@playcademy/sandbox",
25347
- version: "0.6.0",
25348
+ version: "0.6.1-beta.1",
25348
25349
  description: "Local development server for Playcademy game development",
25349
25350
  type: "module",
25350
25351
  exports: {
@@ -35335,6 +35336,11 @@ var init_table3 = __esm(() => {
35335
35336
  email: text("email").notNull().unique(),
35336
35337
  isAnonymous: boolean("is_anonymous").notNull().default(false),
35337
35338
  timebackId: text("timeback_id").unique(),
35339
+ learningTimeZone: text("learning_time_zone"),
35340
+ learningTimeZoneUpdatedAt: timestamp("learning_time_zone_updated_at", {
35341
+ mode: "date",
35342
+ withTimezone: true
35343
+ }),
35338
35344
  emailVerified: boolean("email_verified").notNull().default(false),
35339
35345
  image: text("image"),
35340
35346
  role: userRoleEnum("role").notNull().default("player"),
@@ -100786,6 +100792,7 @@ class AnalyticsXp {
100786
100792
  this.core = core3;
100787
100793
  }
100788
100794
  async get(studentId, options) {
100795
+ const timezone2 = options?.timezone ?? PLATFORM_TIMEZONE;
100789
100796
  const enrollments = await this.core.api.edubridge.enrollments.list({ userId: studentId });
100790
100797
  const filteredEnrollments = options?.courseIds?.length ? enrollments.filter((e) => options.courseIds.includes(e.course.id)) : enrollments;
100791
100798
  if (filteredEnrollments.length === 0) {
@@ -100799,7 +100806,7 @@ class AnalyticsXp {
100799
100806
  try {
100800
100807
  const analytics = await this.core.api.edubridge.analytics.getEnrollmentFacts({
100801
100808
  enrollmentId: enrollment.id,
100802
- timezone: PLATFORM_TIMEZONE
100809
+ timezone: timezone2
100803
100810
  });
100804
100811
  return { enrollment, analytics };
100805
100812
  } catch (error88) {
@@ -100811,7 +100818,7 @@ class AnalyticsXp {
100811
100818
  return { enrollment, analytics: null };
100812
100819
  }
100813
100820
  }));
100814
- const today = formatDateYMDInTimezone(PLATFORM_TIMEZONE);
100821
+ const today = formatDateYMDInTimezone(timezone2);
100815
100822
  let totalXp = 0;
100816
100823
  let todayXp = 0;
100817
100824
  const courses = [];
@@ -106772,13 +106779,24 @@ var init_timeback_service = __esm(async () => {
106772
106779
  };
106773
106780
  }
106774
106781
  }
106782
+ const timezone2 = await this.getLearningTimeZoneForStudent(timebackId);
106775
106783
  const result = await client.analytics.getXp(timebackId, {
106776
106784
  courseIds: courseIds.length > 0 ? courseIds : undefined,
106785
+ timezone: timezone2,
106777
106786
  include: options?.include
106778
106787
  });
106779
106788
  return result;
106780
106789
  });
106781
106790
  }
106791
+ async getLearningTimeZoneForStudent(timebackId) {
106792
+ const user = await this.deps.db.query.users.findFirst({
106793
+ where: eq(users.timebackId, timebackId),
106794
+ columns: {
106795
+ learningTimeZone: true
106796
+ }
106797
+ });
106798
+ return user?.learningTimeZone ?? PLATFORM_TIMEZONE;
106799
+ }
106782
106800
  async getStudentMastery(timebackId, user, options) {
106783
106801
  return this.withClientTelemetry(async () => {
106784
106802
  const client = this.requireClient();
@@ -107540,13 +107558,26 @@ var init_session_service = __esm(() => {
107540
107558
  init_spans();
107541
107559
  init_errors();
107542
107560
  });
107561
+ function normalizeIanaTimeZone(value) {
107562
+ const timeZone = value?.trim();
107563
+ if (!timeZone || timeZone.length > MAX_IANA_TIME_ZONE_LENGTH) {
107564
+ return;
107565
+ }
107566
+ try {
107567
+ new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date);
107568
+ return timeZone;
107569
+ } catch {
107570
+ return;
107571
+ }
107572
+ }
107573
+ var MAX_IANA_TIME_ZONE_LENGTH = 100;
107543
107574
 
107544
107575
  class UserService {
107545
107576
  deps;
107546
107577
  constructor(deps) {
107547
107578
  this.deps = deps;
107548
107579
  }
107549
- async getMe(user, gameId) {
107580
+ async getMe(user, gameId, observedTimeZone) {
107550
107581
  const db2 = this.deps.db;
107551
107582
  const userData = await db2.query.users.findFirst({
107552
107583
  where: eq(users.id, user.id)
@@ -107559,6 +107590,7 @@ class UserService {
107559
107590
  "app.user.has_timeback_account": Boolean(userData.timebackId),
107560
107591
  "app.user.timeback_enriched": false
107561
107592
  });
107593
+ const localDay = await this.resolveLocalDay(userData, observedTimeZone);
107562
107594
  const timeback2 = userData.timebackId ? await this.fetchTimebackData(userData.timebackId, gameId) : undefined;
107563
107595
  setAttribute("app.user.timeback_enriched", Boolean(timeback2));
107564
107596
  if (gameId) {
@@ -107568,6 +107600,7 @@ class UserService {
107568
107600
  role: userData.role,
107569
107601
  username: userData.username,
107570
107602
  email: userData.email,
107603
+ localDay,
107571
107604
  timeback: timeback2
107572
107605
  };
107573
107606
  }
@@ -107587,6 +107620,7 @@ class UserService {
107587
107620
  createdAt: userData.createdAt,
107588
107621
  updatedAt: userData.updatedAt,
107589
107622
  hasTimebackAccount: Boolean(timebackAccount),
107623
+ localDay,
107590
107624
  timeback: timeback2
107591
107625
  };
107592
107626
  }
@@ -107624,6 +107658,44 @@ class UserService {
107624
107658
  isDefault: updatedUser.name === DEMO_DISPLAY_NAME_PLACEHOLDER
107625
107659
  };
107626
107660
  }
107661
+ async resolveLocalDay(userData, observedTimeZone) {
107662
+ const normalizedTimeZone = normalizeIanaTimeZone(observedTimeZone);
107663
+ if (normalizedTimeZone) {
107664
+ const observedAt = new Date;
107665
+ const shouldPersist = userData.learningTimeZone !== normalizedTimeZone || !userData.learningTimeZoneUpdatedAt || observedAt.getTime() - userData.learningTimeZoneUpdatedAt.getTime() >= LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS;
107666
+ if (shouldPersist) {
107667
+ await this.deps.db.update(users).set({
107668
+ learningTimeZone: normalizedTimeZone,
107669
+ learningTimeZoneUpdatedAt: observedAt
107670
+ }).where(eq(users.id, userData.id));
107671
+ }
107672
+ setAttributes({
107673
+ "app.user.learning_time_zone_observed": true,
107674
+ "app.user.learning_time_zone_persisted": shouldPersist
107675
+ });
107676
+ return {
107677
+ timeZone: normalizedTimeZone,
107678
+ source: "browser_last_seen",
107679
+ observedAt: (shouldPersist ? observedAt : userData.learningTimeZoneUpdatedAt)?.toISOString() ?? null
107680
+ };
107681
+ }
107682
+ setAttributes({
107683
+ "app.user.learning_time_zone_observed": false,
107684
+ "app.user.learning_time_zone_persisted": false
107685
+ });
107686
+ if (userData.learningTimeZone) {
107687
+ return {
107688
+ timeZone: userData.learningTimeZone,
107689
+ source: "browser_last_seen",
107690
+ observedAt: userData.learningTimeZoneUpdatedAt?.toISOString() ?? null
107691
+ };
107692
+ }
107693
+ return {
107694
+ timeZone: PLATFORM_TIMEZONE,
107695
+ source: "platform_default",
107696
+ observedAt: null
107697
+ };
107698
+ }
107627
107699
  async fetchTimebackData(timebackId, gameId) {
107628
107700
  const [{ role, organizations: allOrganizations }, allEnrollments] = await Promise.all([
107629
107701
  withSpan("timeback.fetch_profile", () => this.fetchStudentProfile(timebackId)),
@@ -107705,6 +107777,7 @@ class UserService {
107705
107777
  return organizations.filter((o) => enrollmentOrgIds.has(o.id));
107706
107778
  }
107707
107779
  }
107780
+ var LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS;
107708
107781
  var init_user_service = __esm(() => {
107709
107782
  init_drizzle_orm();
107710
107783
  init_src();
@@ -107713,6 +107786,7 @@ var init_user_service = __esm(() => {
107713
107786
  init_spans();
107714
107787
  init_errors();
107715
107788
  init_timeback_util();
107789
+ LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS = 86400000;
107716
107790
  });
107717
107791
 
107718
107792
  class VerifyService {
@@ -168001,9 +168075,10 @@ var getDemoProfile;
168001
168075
  var updateDemoProfile;
168002
168076
  var users2;
168003
168077
  var init_user_controller = __esm(() => {
168078
+ init_src();
168004
168079
  init_schemas_index();
168005
168080
  init_utils11();
168006
- getMe = requireNonAnonymous(async (ctx) => ctx.services.user.getMe(ctx.user, ctx.gameId));
168081
+ getMe = requireNonAnonymous(async (ctx) => ctx.services.user.getMe(ctx.user, ctx.gameId, ctx.request.headers.get(PLAYCADEMY_BROWSER_TIME_ZONE_HEADER)));
168007
168082
  getDemoProfile = requireAnonymous(async (ctx) => ctx.services.user.getDemoProfile(ctx.user.id));
168008
168083
  updateDemoProfile = requireAnonymous(async (ctx) => {
168009
168084
  const body2 = await parseRequestBody(ctx.request, DemoProfileSchema);
@@ -168148,8 +168223,39 @@ function getMockHighestGradeMastered(enrollments, subject) {
168148
168223
  const subjectGrades = enrollments.filter((enrollment) => enrollment.subject === subject).map((enrollment) => enrollment.grade);
168149
168224
  return subjectGrades.length > 0 ? Math.max(...subjectGrades) : null;
168150
168225
  }
168151
- async function buildMockUserResponse(db2, user, gameId) {
168226
+ async function buildLocalDayContext(db2, user, observedTimeZone) {
168227
+ const normalizedTimeZone = normalizeIanaTimeZone(observedTimeZone);
168228
+ if (normalizedTimeZone) {
168229
+ const observedAt = new Date;
168230
+ const shouldPersist = user.learningTimeZone !== normalizedTimeZone || !user.learningTimeZoneUpdatedAt || observedAt.getTime() - user.learningTimeZoneUpdatedAt.getTime() >= LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS2;
168231
+ if (shouldPersist) {
168232
+ await db2.update(users).set({
168233
+ learningTimeZone: normalizedTimeZone,
168234
+ learningTimeZoneUpdatedAt: observedAt
168235
+ }).where(eq(users.id, user.id));
168236
+ }
168237
+ return {
168238
+ timeZone: normalizedTimeZone,
168239
+ source: "browser_last_seen",
168240
+ observedAt: (shouldPersist ? observedAt : user.learningTimeZoneUpdatedAt)?.toISOString() ?? null
168241
+ };
168242
+ }
168243
+ if (user.learningTimeZone) {
168244
+ return {
168245
+ timeZone: user.learningTimeZone,
168246
+ source: "browser_last_seen",
168247
+ observedAt: user.learningTimeZoneUpdatedAt?.toISOString() ?? null
168248
+ };
168249
+ }
168250
+ return {
168251
+ timeZone: PLATFORM_TIMEZONE,
168252
+ source: "platform_default",
168253
+ observedAt: null
168254
+ };
168255
+ }
168256
+ async function buildMockUserResponse(db2, user, gameId, observedTimeZone) {
168152
168257
  const timeback3 = user.timebackId ? await getMockTimebackData(db2, user.timebackId, gameId) : undefined;
168258
+ const localDay = await buildLocalDayContext(db2, user, observedTimeZone);
168153
168259
  if (gameId) {
168154
168260
  return {
168155
168261
  id: user.id,
@@ -168157,6 +168263,7 @@ async function buildMockUserResponse(db2, user, gameId) {
168157
168263
  role: user.role,
168158
168264
  username: user.username,
168159
168265
  email: user.email,
168266
+ localDay,
168160
168267
  timeback: timeback3
168161
168268
  };
168162
168269
  }
@@ -168175,15 +168282,19 @@ async function buildMockUserResponse(db2, user, gameId) {
168175
168282
  createdAt: user.createdAt,
168176
168283
  updatedAt: user.updatedAt,
168177
168284
  hasTimebackAccount: Boolean(timebackAccount),
168285
+ localDay,
168178
168286
  timeback: timeback3
168179
168287
  };
168180
168288
  }
168289
+ var LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS2;
168181
168290
  var init_timeback6 = __esm(() => {
168182
168291
  init_drizzle_orm();
168183
168292
  init_utils11();
168293
+ init_src();
168184
168294
  init_tables_index();
168185
168295
  init_src2();
168186
168296
  init_config();
168297
+ LEARNING_TIME_ZONE_REFRESH_INTERVAL_MS2 = 86400000;
168187
168298
  });
168188
168299
  var usersRouter;
168189
168300
  var init_users = __esm(async () => {
@@ -168191,6 +168302,7 @@ var init_users = __esm(async () => {
168191
168302
  init_dist7();
168192
168303
  init_controllers();
168193
168304
  init_errors();
168305
+ init_src();
168194
168306
  init_tables_index();
168195
168307
  init_error_handler();
168196
168308
  init_timeback6();
@@ -168212,7 +168324,7 @@ var init_users = __esm(async () => {
168212
168324
  const error89 = ApiError.notFound("User not found");
168213
168325
  return c2.json(createErrorResponse(error89), error89.status);
168214
168326
  }
168215
- const response = await buildMockUserResponse(db2, userData, gameId);
168327
+ const response = await buildMockUserResponse(db2, userData, gameId, c2.req.header(PLAYCADEMY_BROWSER_TIME_ZONE_HEADER));
168216
168328
  return c2.json(response);
168217
168329
  }
168218
168330
  return handle2(users2.getMe)(c2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "1.1.2-beta.1",
3
+ "version": "1.1.2-beta.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -19,14 +19,14 @@
19
19
  "dependencies": {
20
20
  "archiver": "^7.0.1",
21
21
  "picocolors": "^1.1.1",
22
- "playcademy": "0.26.1-beta.1"
22
+ "playcademy": "0.26.1-beta.2"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@electric-sql/pglite": "^0.3.16",
26
26
  "@inquirer/prompts": "^7.8.6",
27
27
  "@playcademy/constants": "0.0.1",
28
- "@playcademy/sandbox": "0.6.0",
29
- "@playcademy/sdk": "0.14.0",
28
+ "@playcademy/sandbox": "0.6.1-beta.1",
29
+ "@playcademy/sdk": "0.14.1-beta.1",
30
30
  "@playcademy/types": "0.0.1",
31
31
  "@playcademy/utils": "0.0.1",
32
32
  "@types/archiver": "^6.0.3",