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 +211 -9
- package/dist/constants/src/achievements.ts +107 -0
- package/dist/constants/src/character.ts +16 -0
- package/dist/constants/src/index.ts +3 -0
- package/dist/constants/src/timeback.ts +9 -0
- package/dist/constants/src/typescript.ts +19 -0
- package/dist/constants.js +99 -1
- package/dist/db.d.ts +68 -20
- package/dist/db.js +456 -43
- package/dist/edge-play/src/routes/integrations/timeback/end-activity.ts +1 -1
- package/dist/edge-play/src/types.ts +1 -1
- package/dist/index.d.ts +248 -16
- package/dist/index.js +945 -133
- package/dist/utils.d.ts +198 -8
- package/dist/utils.js +143 -42
- package/dist/version.js +1 -0
- package/package.json +3 -2
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.
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
*
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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 };
|