playcademy 0.27.0 → 0.27.1-beta.10

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/bucket.d.ts CHANGED
@@ -109,6 +109,34 @@ interface IntegrationsConfig {
109
109
  /** Queues (optional) */
110
110
  queues?: Record<string, QueueConfig | boolean>;
111
111
  }
112
+ /**
113
+ * Developer dashboard configuration.
114
+ *
115
+ * The dashboard is a separate app in the game's repo, deployed as a sibling
116
+ * worker with `playcademy dashboard deploy`. Auth and session plumbing are
117
+ * platform-managed; the dashboard app itself is just a static SPA build.
118
+ */
119
+ interface DashboardConfig {
120
+ /** Path to the dashboard app, relative to the config file (default: './dashboard') */
121
+ directory?: string;
122
+ /** Build command run inside the dashboard directory (defaults to the workspace package manager's build script) */
123
+ buildCommand?: string[];
124
+ /** Path to the dashboard build output, relative to the dashboard directory (default: 'dist') */
125
+ buildPath?: string;
126
+ /** Colors for the platform-managed login/invite page (hex); unset uses the built-in palette */
127
+ theme?: DashboardThemeConfig;
128
+ }
129
+ /**
130
+ * Login-page theming. Two seeds — a primary (button, focus, background
131
+ * wash) and a secondary accent — the platform derives the full palette
132
+ * from. Either may be omitted to keep that half of the built-in palette.
133
+ */
134
+ interface DashboardThemeConfig {
135
+ /** Primary color as a hex string, e.g. '#e0245e' */
136
+ primary?: string;
137
+ /** Secondary/accent color as a hex string, e.g. '#ffd166' */
138
+ secondary?: string;
139
+ }
112
140
  /**
113
141
  * Unified Playcademy configuration
114
142
  * Used for playcademy.config.{js,json}
@@ -130,6 +158,8 @@ interface PlaycademyConfig {
130
158
  externalUrl?: string;
131
159
  /** Game platform */
132
160
  platform?: 'web' | 'godot';
161
+ /** Developer dashboard app (optional) — `true` uses defaults */
162
+ dashboard?: DashboardConfig | boolean;
133
163
  /** Integrations (database, custom routes, external services) */
134
164
  integrations?: IntegrationsConfig;
135
165
  }
package/dist/bucket.js CHANGED
@@ -22,62 +22,12 @@ var init_file_loader = __esm({
22
22
  import { existsSync } from "fs";
23
23
  import { join as join8 } from "path";
24
24
 
25
- // ../constants/src/timeback.ts
26
- var TIMEBACK_ROUTES = {
27
- END_ACTIVITY: "/integrations/timeback/end-activity",
28
- GET_XP: "/integrations/timeback/xp",
29
- GET_MASTERY: "/integrations/timeback/mastery",
30
- GET_HIGHEST_GRADE_MASTERED: "/integrations/timeback/highest-grade-mastered",
31
- HEARTBEAT: "/integrations/timeback/heartbeat",
32
- ADVANCE_COURSE: "/integrations/timeback/advance-course",
33
- UNENROLL_COURSE: "/integrations/timeback/unenroll-course"
34
- };
35
- var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
36
- xp: 1,
37
- mastery: 0,
38
- score: 2
39
- };
40
- var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
41
- xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
42
- mastery: 0,
43
- time: 60,
44
- score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
45
- };
46
-
47
- // ../constants/src/cloudflare.ts
48
- var CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
49
-
50
- // ../edge-play/src/constants.ts
25
+ // ../edge-play/src/lib/constants.ts
51
26
  var ASSET_PREFIX = "_assets/";
52
27
  var MANIFEST_KEY = "_manifest.json";
53
28
  var ASSET_ROUTE_PREFIX = "/api/assets/";
54
- var ROUTES = {
55
- /** Route index (lists available routes) */
56
- INDEX: "/api",
57
- /** Health check endpoint */
58
- HEALTH: "/api/health",
59
- /**
60
- * Built-in content-addressable asset serving.
61
- *
62
- * Serves files from the game's BUCKET (R2) binding under their
63
- * content-hashed keys, with immutable caching. Registered in the
64
- * terminal tier so a game's own `server/api/assets/[...path].ts`
65
- * route supersedes it.
66
- */
67
- ASSETS: `${ASSET_ROUTE_PREFIX}*`,
68
- /** TimeBack integration routes */
69
- TIMEBACK: {
70
- END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
71
- GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
72
- GET_MASTERY: `/api${TIMEBACK_ROUTES.GET_MASTERY}`,
73
- GET_HIGHEST_GRADE_MASTERED: `/api${TIMEBACK_ROUTES.GET_HIGHEST_GRADE_MASTERED}`,
74
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
75
- ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`,
76
- UNENROLL_COURSE: `/api${TIMEBACK_ROUTES.UNENROLL_COURSE}`
77
- }
78
- };
79
29
 
80
- // src/lib/bundle/backend.ts
30
+ // src/lib/bundle/game-backend.ts
81
31
  import { join as join6 } from "node:path";
82
32
 
83
33
  // src/constants/api.ts
@@ -85,11 +35,11 @@ import { join as join2 } from "node:path";
85
35
 
86
36
  // src/constants/server.ts
87
37
  import { join } from "node:path";
88
- var SERVER_ROOT_DIRECTORY = "server";
89
- var SERVER_LIB_DIRECTORY = join(SERVER_ROOT_DIRECTORY, "lib");
38
+ var GAME_SERVER_ROOT_DIRECTORY = "server";
39
+ var GAME_SERVER_LIB_DIRECTORY = join(GAME_SERVER_ROOT_DIRECTORY, "lib");
90
40
 
91
41
  // src/constants/api.ts
92
- var DEFAULT_API_ROUTES_DIRECTORY = join2(SERVER_ROOT_DIRECTORY, "api");
42
+ var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
93
43
 
94
44
  // ../better-auth/package.json
95
45
  var package_default = {
@@ -200,6 +150,22 @@ var CLI_DEFAULT_OUTPUTS = {
200
150
  WORKER_BUNDLE: join5(WORKSPACE_NAME, "worker-bundle.js")
201
151
  };
202
152
 
153
+ // ../constants/src/timeback.ts
154
+ var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
155
+ xp: 1,
156
+ mastery: 0,
157
+ score: 2
158
+ };
159
+ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
160
+ xp: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.xp,
161
+ mastery: 0,
162
+ time: 60,
163
+ score: 0.5 / 10 ** TIMEBACK_GAME_METRIC_DECIMAL_PLACES.score
164
+ };
165
+
166
+ // ../constants/src/cloudflare.ts
167
+ var CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
168
+
203
169
  // src/lib/core/client.ts
204
170
  import { PlaycademyClient } from "@playcademy/sdk/internal";
205
171
 
@@ -309,6 +275,17 @@ var TIMEBACK_DISCREPANCY_QUEUE_WINDOWS = [
309
275
  "custom"
310
276
  ];
311
277
  var TIMEBACK_DISCREPANCY_QUEUE_METRICS = ["xp", "mastery", "time", "score"];
278
+ var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES = [
279
+ "choice",
280
+ "inline-choice",
281
+ "text-entry",
282
+ "order",
283
+ "match",
284
+ "hottext"
285
+ ];
286
+ var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPE_SET = new Set(
287
+ PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES
288
+ );
312
289
  var TIMEBACK_DISCREPANCY_QUEUE_WINDOW_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_WINDOWS);
313
290
  var TIMEBACK_DISCREPANCY_QUEUE_METRIC_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_METRICS);
314
291
 
@@ -327,11 +304,13 @@ import {
327
304
  // src/lib/core/transpile.ts
328
305
  import * as esbuild2 from "esbuild";
329
306
 
330
- // src/lib/bundle/backend.ts
331
- function getCustomRoutesDirectory(projectPath, config) {
307
+ // src/lib/bundle/game-backend.ts
308
+ function resolveGameRoutesDir(config) {
332
309
  const customRoutes = config?.integrations?.customRoutes;
333
- const customRoutesDir = typeof customRoutes === "object" && customRoutes.directory || DEFAULT_API_ROUTES_DIRECTORY;
334
- return join6(projectPath, customRoutesDir);
310
+ return typeof customRoutes === "object" && customRoutes.directory || GAME_API_ROUTES_DIRECTORY;
311
+ }
312
+ function getGameRoutesDirectory(projectPath, config) {
313
+ return join6(projectPath, resolveGameRoutesDir(config));
335
314
  }
336
315
 
337
316
  // src/lib/bucket/local.ts
@@ -354,7 +333,7 @@ async function openLocalBucket() {
354
333
  // src/lib/bucket/dev.ts
355
334
  var ASSET_DEV_ROUTE_PREFIX = ASSET_ROUTE_PREFIX;
356
335
  function hasCustomAssetsRoute(projectRoot, config) {
357
- const routesDir = getCustomRoutesDirectory(projectRoot, config);
336
+ const routesDir = getGameRoutesDirectory(projectRoot, config);
358
337
  return existsSync(join8(routesDir, "assets")) || existsSync(join8(routesDir, "assets.ts"));
359
338
  }
360
339
  function openLocalAssetBucket() {