playcademy 0.14.32-alpha.1 → 0.14.33

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/cli.js CHANGED
@@ -562,17 +562,11 @@ function displayApiError(error, indent) {
562
562
  return;
563
563
  }
564
564
  const statusCode = errorInfo.status;
565
- let displayMessage = errorInfo.statusText;
565
+ let displayMessage = errorInfo.message;
566
566
  displayMessage = removeStatusPrefix(displayMessage, statusCode);
567
567
  const { cleanMessage, json: embeddedJson } = extractEmbeddedJson(displayMessage);
568
568
  displayMessage = cleanMessageSuffix(cleanMessage);
569
- let errorCode;
570
- if (error.details && typeof error.details === "object") {
571
- const details = error.details;
572
- if ("code" in details && typeof details.code === "string") {
573
- errorCode = details.code;
574
- }
575
- }
569
+ const errorCode = error.code;
576
570
  let errorHeader = "API Error";
577
571
  if (errorCode) {
578
572
  errorHeader += ` ${redBright(`[${errorCode}]`)}`;
@@ -1050,7 +1044,7 @@ var package_default = {
1050
1044
  sort: "bunx sort-package-json **/package.json",
1051
1045
  test: "bun test",
1052
1046
  "test:unit": "bun scripts/test-unit.ts",
1053
- "test:integration": "bun scripts/test-integration.ts",
1047
+ "test:e2e": "bun scripts/test-e2e.ts",
1054
1048
  "test:watch": "bun test --watch",
1055
1049
  "docker:relay": "bun run scripts/docker-relay.ts",
1056
1050
  "debug:leaderboard-notifications": "bunx sst shell -- bun scripts/debug/leaderboard-notifications.ts",
@@ -1210,6 +1204,104 @@ var CONFIG_FILE_NAMES = [
1210
1204
  "playcademy.config.mjs"
1211
1205
  ];
1212
1206
 
1207
+ // ../constants/src/achievements.ts
1208
+ var ACHIEVEMENT_IDS = {
1209
+ PLAY_ANY_GAME_DAILY: "play_any_game_daily",
1210
+ DAILY_CHEST_OPEN: "daily_chest_open",
1211
+ LEADERBOARD_TOP3_DAILY: "leaderboard_top3_daily",
1212
+ LEADERBOARD_TOP3_WEEKLY: "leaderboard_top3_weekly",
1213
+ FIRST_SCORE_ANY_GAME: "first_score_any_game",
1214
+ PERSONAL_BEST_ANY_GAME: "personal_best_any_game"
1215
+ };
1216
+ var ACHIEVEMENT_DEFINITIONS = [
1217
+ {
1218
+ id: ACHIEVEMENT_IDS.PLAY_ANY_GAME_DAILY,
1219
+ title: "Play any game",
1220
+ description: "Play any arcade game for at least 60 seconds in a single session.",
1221
+ scope: "daily",
1222
+ rewardCredits: 10,
1223
+ limit: 1,
1224
+ completionType: "time_played_session",
1225
+ completionConfig: { minSeconds: 60 },
1226
+ target: { anyArcadeGame: true },
1227
+ active: true
1228
+ },
1229
+ {
1230
+ id: ACHIEVEMENT_IDS.DAILY_CHEST_OPEN,
1231
+ title: "Opened the daily chest",
1232
+ description: "Find the chest on the map and open it to claim your reward.",
1233
+ scope: "daily",
1234
+ rewardCredits: 10,
1235
+ limit: 1,
1236
+ completionType: "interaction",
1237
+ completionConfig: { triggerId: "bunny_chest" },
1238
+ target: { map: "arcade" },
1239
+ active: true
1240
+ },
1241
+ {
1242
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_DAILY,
1243
+ title: "Daily Champion",
1244
+ description: "Finish in the top 3 of any game leaderboard for the day.",
1245
+ scope: "daily",
1246
+ rewardCredits: 25,
1247
+ // Base reward, overridden by rankRewards
1248
+ limit: 1,
1249
+ completionType: "leaderboard_rank",
1250
+ completionConfig: {
1251
+ rankRewards: [50, 30, 15]
1252
+ // [1st place, 2nd place, 3rd place]
1253
+ },
1254
+ target: { anyArcadeGame: true },
1255
+ active: true
1256
+ },
1257
+ {
1258
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_WEEKLY,
1259
+ title: "Weekly Legend",
1260
+ description: "Finish in the top 3 of any game leaderboard for the week.",
1261
+ scope: "weekly",
1262
+ rewardCredits: 50,
1263
+ // Base reward, overridden by rankRewards
1264
+ limit: 1,
1265
+ completionType: "leaderboard_rank",
1266
+ completionConfig: {
1267
+ rankRewards: [100, 60, 30]
1268
+ // [1st place, 2nd place, 3rd place]
1269
+ },
1270
+ target: { anyArcadeGame: true },
1271
+ active: true
1272
+ },
1273
+ {
1274
+ id: ACHIEVEMENT_IDS.FIRST_SCORE_ANY_GAME,
1275
+ title: "First Score",
1276
+ description: "Submit your first score in any arcade game.",
1277
+ scope: "game",
1278
+ // Scoped per game, no time limit
1279
+ rewardCredits: 5,
1280
+ limit: 1,
1281
+ completionType: "first_score",
1282
+ completionConfig: {},
1283
+ target: { anyArcadeGame: true },
1284
+ active: true
1285
+ },
1286
+ {
1287
+ id: ACHIEVEMENT_IDS.PERSONAL_BEST_ANY_GAME,
1288
+ title: "Personal Best",
1289
+ description: "Beat your personal best score in any arcade game.",
1290
+ scope: "daily",
1291
+ rewardCredits: 5,
1292
+ limit: 3,
1293
+ // Can earn 3 times per day
1294
+ completionType: "personal_best",
1295
+ completionConfig: {},
1296
+ target: { anyArcadeGame: true },
1297
+ active: true
1298
+ }
1299
+ ];
1300
+
1301
+ // ../constants/src/typescript.ts
1302
+ var TSC_PACKAGE = "@typescript/native-preview";
1303
+ var USE_NATIVE_TSC = TSC_PACKAGE.includes("native-preview");
1304
+
1213
1305
  // ../constants/src/overworld.ts
1214
1306
  var ITEM_SLUGS = {
1215
1307
  /** Primary platform currency */
@@ -1280,6 +1372,9 @@ function normalizeGitignoreEntry(entry) {
1280
1372
  // src/lib/core/import.ts
1281
1373
  import * as esbuild from "esbuild";
1282
1374
 
1375
+ // src/lib/core/transpile.ts
1376
+ import * as esbuild2 from "esbuild";
1377
+
1283
1378
  // src/lib/secrets/env.ts
1284
1379
  init_file_loader();
1285
1380
  import { existsSync as existsSync4, writeFileSync } from "fs";
@@ -1631,8 +1726,105 @@ function processConfigVariables(config) {
1631
1726
 
1632
1727
  // ../timeback/dist/constants.js
1633
1728
  var __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
1729
+ var ACHIEVEMENT_IDS2;
1730
+ var ACHIEVEMENT_DEFINITIONS2;
1731
+ var init_achievements = __esm2(() => {
1732
+ ACHIEVEMENT_IDS2 = {
1733
+ PLAY_ANY_GAME_DAILY: "play_any_game_daily",
1734
+ DAILY_CHEST_OPEN: "daily_chest_open",
1735
+ LEADERBOARD_TOP3_DAILY: "leaderboard_top3_daily",
1736
+ LEADERBOARD_TOP3_WEEKLY: "leaderboard_top3_weekly",
1737
+ FIRST_SCORE_ANY_GAME: "first_score_any_game",
1738
+ PERSONAL_BEST_ANY_GAME: "personal_best_any_game"
1739
+ };
1740
+ ACHIEVEMENT_DEFINITIONS2 = [
1741
+ {
1742
+ id: ACHIEVEMENT_IDS2.PLAY_ANY_GAME_DAILY,
1743
+ title: "Play any game",
1744
+ description: "Play any arcade game for at least 60 seconds in a single session.",
1745
+ scope: "daily",
1746
+ rewardCredits: 10,
1747
+ limit: 1,
1748
+ completionType: "time_played_session",
1749
+ completionConfig: { minSeconds: 60 },
1750
+ target: { anyArcadeGame: true },
1751
+ active: true
1752
+ },
1753
+ {
1754
+ id: ACHIEVEMENT_IDS2.DAILY_CHEST_OPEN,
1755
+ title: "Opened the daily chest",
1756
+ description: "Find the chest on the map and open it to claim your reward.",
1757
+ scope: "daily",
1758
+ rewardCredits: 10,
1759
+ limit: 1,
1760
+ completionType: "interaction",
1761
+ completionConfig: { triggerId: "bunny_chest" },
1762
+ target: { map: "arcade" },
1763
+ active: true
1764
+ },
1765
+ {
1766
+ id: ACHIEVEMENT_IDS2.LEADERBOARD_TOP3_DAILY,
1767
+ title: "Daily Champion",
1768
+ description: "Finish in the top 3 of any game leaderboard for the day.",
1769
+ scope: "daily",
1770
+ rewardCredits: 25,
1771
+ limit: 1,
1772
+ completionType: "leaderboard_rank",
1773
+ completionConfig: {
1774
+ rankRewards: [50, 30, 15]
1775
+ },
1776
+ target: { anyArcadeGame: true },
1777
+ active: true
1778
+ },
1779
+ {
1780
+ id: ACHIEVEMENT_IDS2.LEADERBOARD_TOP3_WEEKLY,
1781
+ title: "Weekly Legend",
1782
+ description: "Finish in the top 3 of any game leaderboard for the week.",
1783
+ scope: "weekly",
1784
+ rewardCredits: 50,
1785
+ limit: 1,
1786
+ completionType: "leaderboard_rank",
1787
+ completionConfig: {
1788
+ rankRewards: [100, 60, 30]
1789
+ },
1790
+ target: { anyArcadeGame: true },
1791
+ active: true
1792
+ },
1793
+ {
1794
+ id: ACHIEVEMENT_IDS2.FIRST_SCORE_ANY_GAME,
1795
+ title: "First Score",
1796
+ description: "Submit your first score in any arcade game.",
1797
+ scope: "game",
1798
+ rewardCredits: 5,
1799
+ limit: 1,
1800
+ completionType: "first_score",
1801
+ completionConfig: {},
1802
+ target: { anyArcadeGame: true },
1803
+ active: true
1804
+ },
1805
+ {
1806
+ id: ACHIEVEMENT_IDS2.PERSONAL_BEST_ANY_GAME,
1807
+ title: "Personal Best",
1808
+ description: "Beat your personal best score in any arcade game.",
1809
+ scope: "daily",
1810
+ rewardCredits: 5,
1811
+ limit: 3,
1812
+ completionType: "personal_best",
1813
+ completionConfig: {},
1814
+ target: { anyArcadeGame: true },
1815
+ active: true
1816
+ }
1817
+ ];
1818
+ });
1634
1819
  var init_auth = () => {
1635
1820
  };
1821
+ var TSC_PACKAGE2 = "@typescript/native-preview";
1822
+ var USE_NATIVE_TSC2;
1823
+ var init_typescript = __esm2(() => {
1824
+ USE_NATIVE_TSC2 = TSC_PACKAGE2.includes("native-preview");
1825
+ });
1826
+ var init_character = () => {
1827
+ };
1636
1828
  var PLAYCADEMY_BASE_URLS2;
1637
1829
  var init_domains = __esm2(() => {
1638
1830
  PLAYCADEMY_BASE_URLS2 = {
@@ -1669,6 +1861,7 @@ var init_overworld = __esm2(() => {
1669
1861
  FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
1670
1862
  };
1671
1863
  });
1864
+ var TIMEBACK_CALIPER_SENSORS;
1672
1865
  var TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY";
1673
1866
  var TIMEBACK_ORG_NAME = "Playcademy Studios";
1674
1867
  var TIMEBACK_ORG_TYPE = "department";
@@ -1677,6 +1870,11 @@ var TIMEBACK_RESOURCE_DEFAULTS;
1677
1870
  var TIMEBACK_COMPONENT_DEFAULTS;
1678
1871
  var TIMEBACK_COMPONENT_RESOURCE_DEFAULTS;
1679
1872
  var init_timeback = __esm2(() => {
1873
+ TIMEBACK_CALIPER_SENSORS = [
1874
+ "https://samuraimath.com",
1875
+ "https://app.alphamath.school",
1876
+ "https://mathraiders.com"
1877
+ ];
1680
1878
  TIMEBACK_COURSE_DEFAULTS = {
1681
1879
  gradingScheme: "STANDARD",
1682
1880
  level: {
@@ -1717,7 +1915,10 @@ var init_timeback = __esm2(() => {
1717
1915
  var init_workers = () => {
1718
1916
  };
1719
1917
  var init_src = __esm2(() => {
1918
+ init_achievements();
1720
1919
  init_auth();
1920
+ init_typescript();
1921
+ init_character();
1721
1922
  init_domains();
1722
1923
  init_env_vars();
1723
1924
  init_overworld();
@@ -2801,6 +3002,7 @@ var package_default2 = {
2801
3002
  ],
2802
3003
  scripts: {
2803
3004
  build: "bun build.ts",
3005
+ check: "bunx @typescript/native-preview --noEmit",
2804
3006
  dev: "PLAYCADEMY_BASE_URL=http://localhost:5174 bun src/index.ts",
2805
3007
  pub: "bun publish.ts"
2806
3008
  },
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Achievement definitions - single source of truth for all achievements
3
+ *
4
+ * Used by:
5
+ * - apps/cademy seed scripts
6
+ * - packages/sandbox seeding
7
+ * - packages/fns achievement awarding
8
+ *
9
+ * NOTE: Uses string literals instead of AchievementCompletionType enum
10
+ * to avoid circular dependency with @playcademy/data
11
+ */
12
+
13
+ /**
14
+ * Achievement IDs as constants to avoid magic strings
15
+ */
16
+ export const ACHIEVEMENT_IDS = {
17
+ PLAY_ANY_GAME_DAILY: 'play_any_game_daily',
18
+ DAILY_CHEST_OPEN: 'daily_chest_open',
19
+ LEADERBOARD_TOP3_DAILY: 'leaderboard_top3_daily',
20
+ LEADERBOARD_TOP3_WEEKLY: 'leaderboard_top3_weekly',
21
+ FIRST_SCORE_ANY_GAME: 'first_score_any_game',
22
+ PERSONAL_BEST_ANY_GAME: 'personal_best_any_game',
23
+ } as const
24
+
25
+ export type AchievementId = (typeof ACHIEVEMENT_IDS)[keyof typeof ACHIEVEMENT_IDS]
26
+
27
+ /**
28
+ * Achievement definitions for platform seeding
29
+ */
30
+ export const ACHIEVEMENT_DEFINITIONS = [
31
+ {
32
+ id: ACHIEVEMENT_IDS.PLAY_ANY_GAME_DAILY,
33
+ title: 'Play any game',
34
+ description: 'Play any arcade game for at least 60 seconds in a single session.',
35
+ scope: 'daily' as const,
36
+ rewardCredits: 10,
37
+ limit: 1,
38
+ completionType: 'time_played_session' as const,
39
+ completionConfig: { minSeconds: 60 },
40
+ target: { anyArcadeGame: true },
41
+ active: true,
42
+ },
43
+ {
44
+ id: ACHIEVEMENT_IDS.DAILY_CHEST_OPEN,
45
+ title: 'Opened the daily chest',
46
+ description: 'Find the chest on the map and open it to claim your reward.',
47
+ scope: 'daily' as const,
48
+ rewardCredits: 10,
49
+ limit: 1,
50
+ completionType: 'interaction' as const,
51
+ completionConfig: { triggerId: 'bunny_chest' },
52
+ target: { map: 'arcade' },
53
+ active: true,
54
+ },
55
+ {
56
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_DAILY,
57
+ title: 'Daily Champion',
58
+ description: 'Finish in the top 3 of any game leaderboard for the day.',
59
+ scope: 'daily' as const,
60
+ rewardCredits: 25, // Base reward, overridden by rankRewards
61
+ limit: 1,
62
+ completionType: 'leaderboard_rank' as const,
63
+ completionConfig: {
64
+ rankRewards: [50, 30, 15], // [1st place, 2nd place, 3rd place]
65
+ },
66
+ target: { anyArcadeGame: true },
67
+ active: true,
68
+ },
69
+ {
70
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_WEEKLY,
71
+ title: 'Weekly Legend',
72
+ description: 'Finish in the top 3 of any game leaderboard for the week.',
73
+ scope: 'weekly' as const,
74
+ rewardCredits: 50, // Base reward, overridden by rankRewards
75
+ limit: 1,
76
+ completionType: 'leaderboard_rank' as const,
77
+ completionConfig: {
78
+ rankRewards: [100, 60, 30], // [1st place, 2nd place, 3rd place]
79
+ },
80
+ target: { anyArcadeGame: true },
81
+ active: true,
82
+ },
83
+ {
84
+ id: ACHIEVEMENT_IDS.FIRST_SCORE_ANY_GAME,
85
+ title: 'First Score',
86
+ description: 'Submit your first score in any arcade game.',
87
+ scope: 'game' as const, // Scoped per game, no time limit
88
+ rewardCredits: 5,
89
+ limit: 1,
90
+ completionType: 'first_score' as const,
91
+ completionConfig: {},
92
+ target: { anyArcadeGame: true },
93
+ active: true,
94
+ },
95
+ {
96
+ id: ACHIEVEMENT_IDS.PERSONAL_BEST_ANY_GAME,
97
+ title: 'Personal Best',
98
+ description: 'Beat your personal best score in any arcade game.',
99
+ scope: 'daily' as const,
100
+ rewardCredits: 5,
101
+ limit: 3, // Can earn 3 times per day
102
+ completionType: 'personal_best' as const,
103
+ completionConfig: {},
104
+ target: { anyArcadeGame: true },
105
+ active: true,
106
+ },
107
+ ]
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Character Constants
3
+ *
4
+ * Configuration for player character customization.
5
+ */
6
+
7
+ /** Accessory slot configuration with unlock levels */
8
+ export const ACCESSORY_SLOTS = [
9
+ { type: 'head', unlockLevel: 1, displayName: 'Head', maxItems: 1 },
10
+ { type: 'face', unlockLevel: 2, displayName: 'Face', maxItems: 1 },
11
+ { type: 'hands', unlockLevel: 10, displayName: 'Hands', maxItems: 1 },
12
+ { type: 'back', unlockLevel: 15, displayName: 'Back', maxItems: 1 },
13
+ ] as const
14
+
15
+ /** Valid accessory slot types */
16
+ export const VALID_SLOT_TYPES = ['head', 'face', 'hands', 'back'] as const
@@ -6,7 +6,10 @@
6
6
  * and other constants that need to be consistent across multiple packages.
7
7
  */
8
8
 
9
+ export * from './achievements'
9
10
  export * from './auth'
11
+ export * from './typescript'
12
+ export * from './character'
10
13
  export * from './domains'
11
14
  export * from './env-vars'
12
15
  export * from './overworld'
@@ -4,6 +4,15 @@
4
4
  * Constants related to TimeBack Caliper integration.
5
5
  */
6
6
 
7
+ /**
8
+ * TimeBack LTI issuer URLs by environment
9
+ * Used for JWT token verification (iss claim)
10
+ */
11
+ export const TIMEBACK_LTI_ISSUER_URLS = {
12
+ production: 'https://timeback.com',
13
+ staging: 'https://staging.timeback.com',
14
+ } as const
15
+
7
16
  /**
8
17
  * TimeBack Caliper sensor URLs
9
18
  * Allowed sensor URLs for Caliper event validation
@@ -0,0 +1,19 @@
1
+ /**
2
+ * TypeScript configuration constants
3
+ *
4
+ * Toggle between TypeScript compilers for type generation.
5
+ */
6
+
7
+ /**
8
+ * TypeScript package to use for type generation via bunx.
9
+ *
10
+ * Options:
11
+ * - '@typescript/native-preview' - Native TypeScript compiler (faster)
12
+ * - 'typescript' - Standard TypeScript compiler
13
+ */
14
+ export const TSC_PACKAGE = '@typescript/native-preview'
15
+
16
+ /**
17
+ * Whether using the native TypeScript compiler.
18
+ */
19
+ export const USE_NATIVE_TSC = TSC_PACKAGE.includes('native-preview')
package/dist/constants.js CHANGED
@@ -72,7 +72,7 @@ var package_default = {
72
72
  sort: "bunx sort-package-json **/package.json",
73
73
  test: "bun test",
74
74
  "test:unit": "bun scripts/test-unit.ts",
75
- "test:integration": "bun scripts/test-integration.ts",
75
+ "test:e2e": "bun scripts/test-e2e.ts",
76
76
  "test:watch": "bun test --watch",
77
77
  "docker:relay": "bun run scripts/docker-relay.ts",
78
78
  "debug:leaderboard-notifications": "bunx sst shell -- bun scripts/debug/leaderboard-notifications.ts",
@@ -292,6 +292,104 @@ var CONFIG_FILE_NAMES = [
292
292
  "playcademy.config.mjs"
293
293
  ];
294
294
 
295
+ // ../constants/src/achievements.ts
296
+ var ACHIEVEMENT_IDS = {
297
+ PLAY_ANY_GAME_DAILY: "play_any_game_daily",
298
+ DAILY_CHEST_OPEN: "daily_chest_open",
299
+ LEADERBOARD_TOP3_DAILY: "leaderboard_top3_daily",
300
+ LEADERBOARD_TOP3_WEEKLY: "leaderboard_top3_weekly",
301
+ FIRST_SCORE_ANY_GAME: "first_score_any_game",
302
+ PERSONAL_BEST_ANY_GAME: "personal_best_any_game"
303
+ };
304
+ var ACHIEVEMENT_DEFINITIONS = [
305
+ {
306
+ id: ACHIEVEMENT_IDS.PLAY_ANY_GAME_DAILY,
307
+ title: "Play any game",
308
+ description: "Play any arcade game for at least 60 seconds in a single session.",
309
+ scope: "daily",
310
+ rewardCredits: 10,
311
+ limit: 1,
312
+ completionType: "time_played_session",
313
+ completionConfig: { minSeconds: 60 },
314
+ target: { anyArcadeGame: true },
315
+ active: true
316
+ },
317
+ {
318
+ id: ACHIEVEMENT_IDS.DAILY_CHEST_OPEN,
319
+ title: "Opened the daily chest",
320
+ description: "Find the chest on the map and open it to claim your reward.",
321
+ scope: "daily",
322
+ rewardCredits: 10,
323
+ limit: 1,
324
+ completionType: "interaction",
325
+ completionConfig: { triggerId: "bunny_chest" },
326
+ target: { map: "arcade" },
327
+ active: true
328
+ },
329
+ {
330
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_DAILY,
331
+ title: "Daily Champion",
332
+ description: "Finish in the top 3 of any game leaderboard for the day.",
333
+ scope: "daily",
334
+ rewardCredits: 25,
335
+ // Base reward, overridden by rankRewards
336
+ limit: 1,
337
+ completionType: "leaderboard_rank",
338
+ completionConfig: {
339
+ rankRewards: [50, 30, 15]
340
+ // [1st place, 2nd place, 3rd place]
341
+ },
342
+ target: { anyArcadeGame: true },
343
+ active: true
344
+ },
345
+ {
346
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_WEEKLY,
347
+ title: "Weekly Legend",
348
+ description: "Finish in the top 3 of any game leaderboard for the week.",
349
+ scope: "weekly",
350
+ rewardCredits: 50,
351
+ // Base reward, overridden by rankRewards
352
+ limit: 1,
353
+ completionType: "leaderboard_rank",
354
+ completionConfig: {
355
+ rankRewards: [100, 60, 30]
356
+ // [1st place, 2nd place, 3rd place]
357
+ },
358
+ target: { anyArcadeGame: true },
359
+ active: true
360
+ },
361
+ {
362
+ id: ACHIEVEMENT_IDS.FIRST_SCORE_ANY_GAME,
363
+ title: "First Score",
364
+ description: "Submit your first score in any arcade game.",
365
+ scope: "game",
366
+ // Scoped per game, no time limit
367
+ rewardCredits: 5,
368
+ limit: 1,
369
+ completionType: "first_score",
370
+ completionConfig: {},
371
+ target: { anyArcadeGame: true },
372
+ active: true
373
+ },
374
+ {
375
+ id: ACHIEVEMENT_IDS.PERSONAL_BEST_ANY_GAME,
376
+ title: "Personal Best",
377
+ description: "Beat your personal best score in any arcade game.",
378
+ scope: "daily",
379
+ rewardCredits: 5,
380
+ limit: 3,
381
+ // Can earn 3 times per day
382
+ completionType: "personal_best",
383
+ completionConfig: {},
384
+ target: { anyArcadeGame: true },
385
+ active: true
386
+ }
387
+ ];
388
+
389
+ // ../constants/src/typescript.ts
390
+ var TSC_PACKAGE = "@typescript/native-preview";
391
+ var USE_NATIVE_TSC = TSC_PACKAGE.includes("native-preview");
392
+
295
393
  // ../constants/src/domains.ts
296
394
  var PLAYCADEMY_DOMAINS = {
297
395
  /** Primary domain (hub, landing pages) */
package/dist/db.d.ts CHANGED
@@ -30,9 +30,17 @@ import { Miniflare } from 'miniflare';
30
30
  declare function getDevDbPath(): string;
31
31
 
32
32
  /**
33
- * Seed Worker Bundler
34
- *
35
- * Bundles user's seed file into standalone Cloudflare Worker
33
+ * Environment context passed to seed functions
34
+ */
35
+ interface SeedContext {
36
+ env: {
37
+ DB: unknown;
38
+ BUCKET?: unknown;
39
+ secrets?: Record<string, string>;
40
+ };
41
+ }
42
+ /**
43
+ * Result of bundling a seed file into a Cloudflare Worker
36
44
  */
37
45
  interface SeedWorkerBundle {
38
46
  /** Bundled worker code */
@@ -40,13 +48,29 @@ interface SeedWorkerBundle {
40
48
  /** Size in bytes */
41
49
  size: number;
42
50
  }
51
+
43
52
  /**
44
- * Bundle seed file into a Cloudflare Worker
53
+ * Seed Worker Bundler
54
+ *
55
+ * Bundles user's seed file into standalone Cloudflare Worker
56
+ * with enhanced logging capture and structured error reporting.
57
+ */
58
+
59
+ /**
60
+ * Generate the worker entry code that wraps the user's seed function.
61
+ *
62
+ * The generated worker:
63
+ * - Imports the user's seed function from the specified path
64
+ * - Captures console output (log/warn/error/info) with timestamps
65
+ * - Parses D1/SQLite errors into structured details
66
+ * - Returns success/error as JSON with logs and timing
45
67
  *
46
- * Creates a minimal worker that:
47
- * 1. Imports user's seed function
48
- * 2. Wraps it in fetch handler
49
- * 3. Returns success/error as JSON
68
+ * @param seedFilePath - Absolute path to user's seed file
69
+ * @returns Worker entry code as a string
70
+ */
71
+ declare function createSeedWorkerEntry(seedFilePath: string): Promise<string>;
72
+ /**
73
+ * Bundle seed file into a Cloudflare Worker
50
74
  *
51
75
  * @param seedFilePath - Absolute path to seed file
52
76
  * @param projectPath - Project root (for resolving imports)
@@ -75,17 +99,6 @@ declare function resetDatabase(workspace: string, mf: Miniflare, options?: {
75
99
  debug?: boolean;
76
100
  }): Promise<void>;
77
101
 
78
- /**
79
- * Environment context passed to seed functions
80
- */
81
- interface SeedContext {
82
- env: {
83
- DB: unknown;
84
- BUCKET?: unknown;
85
- secrets?: Record<string, string>;
86
- };
87
- }
88
-
89
102
  /**
90
103
  * Database Seed Utilities
91
104
  *
@@ -110,5 +123,40 @@ declare function getBucket(mf: Miniflare): Promise<unknown | null>;
110
123
  */
111
124
  declare function executeSeedFile(seedFilePath: string, mf: Miniflare, envSecrets?: Record<string, string>): Promise<void>;
112
125
 
113
- export { bundleSeedWorker, executeSeedFile, getBucket, getDevDbPath as getPath, importSeedModule, resetDatabase };
126
+ /** Log entry captured from seed worker console output */
127
+ interface SeedLogEntry {
128
+ /** Log level (log, warn, error, info) */
129
+ level: 'log' | 'warn' | 'error' | 'info';
130
+ /** Log message content */
131
+ message: string;
132
+ /** Milliseconds since seed execution started */
133
+ timestamp: number;
134
+ }
135
+
136
+ /**
137
+ * Seed Output Utilities
138
+ *
139
+ * Functions for displaying seed execution results and handling errors.
140
+ */
141
+
142
+ /**
143
+ * Display captured logs from seed execution.
144
+ * Uses a vertical line to indicate output is from another source (the seed file).
145
+ *
146
+ * @param logs - Captured log entries
147
+ * @param status - 'success' for green bar, 'error' for red bar
148
+ */
149
+ declare function displaySeedLogs(logs: SeedLogEntry[], status?: 'success' | 'error'): void;
150
+ /**
151
+ * Handle seed-specific errors with helpful messages.
152
+ *
153
+ * For seed execution errors, displays captured logs with error styling.
154
+ * For infrastructure errors (network, auth, etc.), shows admonitions.
155
+ *
156
+ * Returns true if the error was handled (caller should show simple failure message).
157
+ * Returns false if not handled (caller should show the error message).
158
+ */
159
+ declare function handleSeedError(error: unknown): boolean;
160
+
161
+ export { bundleSeedWorker, createSeedWorkerEntry, displaySeedLogs, executeSeedFile, getBucket, getDevDbPath as getPath, handleSeedError, importSeedModule, resetDatabase };
114
162
  export type { SeedWorkerBundle };