playcademy 0.14.13 → 0.14.14-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.
- package/dist/constants/src/academics.ts +28 -0
- package/dist/constants/src/domains.ts +22 -5
- package/dist/constants/src/index.ts +1 -0
- package/dist/constants.d.ts +54 -20
- package/dist/constants.js +59 -19
- package/dist/db.js +68 -18
- package/dist/edge-play/src/entry/middleware.ts +60 -0
- package/dist/edge-play/src/entry/session.ts +34 -0
- package/dist/edge-play/src/entry.ts +6 -0
- package/dist/index.d.ts +220 -128
- package/dist/index.js +1198 -461
- package/dist/templates/api/sample-database.ts.template +18 -39
- package/dist/templates/api/sample-kv.ts.template +53 -46
- package/dist/templates/auth/auth.ts.template +1 -1
- package/dist/templates/config/timeback-config.js.template +2 -2
- package/dist/templates/database/db-schema-example.ts.template +29 -0
- package/dist/templates/database/db-schema-index.ts.template +1 -2
- package/dist/templates/database/db-seed.ts.template +12 -20
- package/dist/templates/database/db-types.ts.template +2 -10
- package/dist/templates/database/drizzle-config.ts.template +2 -2
- package/dist/utils.d.ts +9 -2
- package/dist/utils.js +211 -71
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/templates/database/db-schema-scores.ts.template +0 -43
- package/dist/templates/database/db-schema-users.ts.template +0 -23
package/dist/utils.js
CHANGED
|
@@ -1708,7 +1708,19 @@ init_file_loader();
|
|
|
1708
1708
|
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
1709
1709
|
|
|
1710
1710
|
// src/constants/api.ts
|
|
1711
|
-
|
|
1711
|
+
import { join as join2 } from "node:path";
|
|
1712
|
+
|
|
1713
|
+
// src/constants/server.ts
|
|
1714
|
+
import { join } from "node:path";
|
|
1715
|
+
var SERVER_ROOT_DIRECTORY = "server";
|
|
1716
|
+
var SERVER_LIB_DIRECTORY = join(SERVER_ROOT_DIRECTORY, "lib");
|
|
1717
|
+
var PUBLIC_DIRECTORY = "public";
|
|
1718
|
+
var ACADEMICS_PUBLIC_DIRECTORY = join(PUBLIC_DIRECTORY, "academics");
|
|
1719
|
+
var PROBLEM_SETS_DIRECTORY = join(ACADEMICS_PUBLIC_DIRECTORY, "problem-sets");
|
|
1720
|
+
var PROBLEM_SETS_URL_PATH = "academics/problem-sets";
|
|
1721
|
+
|
|
1722
|
+
// src/constants/api.ts
|
|
1723
|
+
var DEFAULT_API_ROUTES_DIRECTORY = join2(SERVER_ROOT_DIRECTORY, "api");
|
|
1712
1724
|
|
|
1713
1725
|
// ../../package.json
|
|
1714
1726
|
var package_default = {
|
|
@@ -1757,9 +1769,11 @@ var package_default = {
|
|
|
1757
1769
|
"sync-engine-assets": "bun scripts/sync-engine-assets.ts",
|
|
1758
1770
|
"sync-vite-templates": "bun scripts/sync-vite-templates.ts",
|
|
1759
1771
|
"sync-godot-template": "bun scripts/sync-godot-template.ts",
|
|
1772
|
+
"sync-problem-sets": "sst shell -- bun scripts/sync-problem-sets.ts",
|
|
1760
1773
|
"sync:all": "bun scripts/sync-all.ts",
|
|
1761
1774
|
cf: "sst shell -- bun scripts/setup-cloudflare-dispatch.ts",
|
|
1762
1775
|
"list-s3-bucket": "sst shell -- bun scripts/list-s3-bucket.ts",
|
|
1776
|
+
"invalidate-cdn": "sst shell -- bun scripts/invalidate-cdn.ts",
|
|
1763
1777
|
doctor: "bunx sst shell -- bun scripts/doctor.ts",
|
|
1764
1778
|
format: "bun run --filter '*' format",
|
|
1765
1779
|
lint: "bun run --filter '*' lint",
|
|
@@ -1849,24 +1863,50 @@ var CLOUDFLARE_COMPATIBILITY_DATE = "2024-01-01";
|
|
|
1849
1863
|
var CLOUDFLARE_BINDINGS = {
|
|
1850
1864
|
/** R2 bucket binding name */
|
|
1851
1865
|
BUCKET: "BUCKET",
|
|
1866
|
+
/** Problem sets bucket binding name */
|
|
1867
|
+
BUCKET_PROBLEM_SETS: "PROBLEM_SETS",
|
|
1852
1868
|
/** KV namespace binding name */
|
|
1853
1869
|
KV: "KV",
|
|
1854
1870
|
/** D1 database binding name */
|
|
1855
1871
|
DB: "DB"
|
|
1856
1872
|
};
|
|
1857
1873
|
|
|
1874
|
+
// src/constants/database.ts
|
|
1875
|
+
import { join as join3 } from "path";
|
|
1876
|
+
var DEFAULT_DATABASE_DIRECTORY = join3("server", "db");
|
|
1877
|
+
var DRIZZLE_CONFIG_FILES = ["drizzle.config.ts", "drizzle.config.js"];
|
|
1878
|
+
|
|
1879
|
+
// src/constants/godot.ts
|
|
1880
|
+
import { join as join4 } from "node:path";
|
|
1881
|
+
var GODOT_BUILD_DIRECTORIES = {
|
|
1882
|
+
/** Root build directory (cleared before each export) */
|
|
1883
|
+
ROOT: "build",
|
|
1884
|
+
/** Web export subdirectory */
|
|
1885
|
+
WEB: join4("build", "web")
|
|
1886
|
+
};
|
|
1887
|
+
var GODOT_BUILD_OUTPUTS = {
|
|
1888
|
+
/** Exported web build entry point */
|
|
1889
|
+
INDEX_HTML: join4("build", "web", "index.html"),
|
|
1890
|
+
/** Packaged zip file (created by Godot export) */
|
|
1891
|
+
ZIP: join4("build", "web_playcademy.zip")
|
|
1892
|
+
};
|
|
1893
|
+
|
|
1858
1894
|
// src/constants/paths.ts
|
|
1859
|
-
import { join } from "path";
|
|
1895
|
+
import { join as join5 } from "path";
|
|
1860
1896
|
var WORKSPACE_NAME = ".playcademy";
|
|
1861
1897
|
var CLI_DIRECTORIES = {
|
|
1862
1898
|
/** Root directory for CLI artifacts in workspace */
|
|
1863
1899
|
WORKSPACE: WORKSPACE_NAME,
|
|
1864
1900
|
/** Database directory within workspace */
|
|
1865
|
-
DATABASE:
|
|
1901
|
+
DATABASE: join5(WORKSPACE_NAME, "db"),
|
|
1866
1902
|
/** KV storage directory within workspace */
|
|
1867
|
-
KV:
|
|
1903
|
+
KV: join5(WORKSPACE_NAME, "kv"),
|
|
1868
1904
|
/** Bucket storage directory within workspace */
|
|
1869
|
-
BUCKET:
|
|
1905
|
+
BUCKET: join5(WORKSPACE_NAME, "bucket")
|
|
1906
|
+
};
|
|
1907
|
+
var CLI_DEFAULT_OUTPUTS = {
|
|
1908
|
+
/** Default worker bundle output for debug command */
|
|
1909
|
+
WORKER_BUNDLE: join5(WORKSPACE_NAME, "worker-bundle.js")
|
|
1870
1910
|
};
|
|
1871
1911
|
|
|
1872
1912
|
// src/constants/ports.ts
|
|
@@ -1884,6 +1924,15 @@ var CONFIG_FILE_NAMES = [
|
|
|
1884
1924
|
"playcademy.config.mjs"
|
|
1885
1925
|
];
|
|
1886
1926
|
|
|
1927
|
+
// ../constants/src/domains.ts
|
|
1928
|
+
var APEX_DOMAIN = "playcademy.net";
|
|
1929
|
+
var CDN_DOMAINS = {
|
|
1930
|
+
/** Production CDN: https://cdn.playcademy.net */
|
|
1931
|
+
production: `https://cdn.${APEX_DOMAIN}`,
|
|
1932
|
+
/** Development CDN: https://cdn.dev.playcademy.net */
|
|
1933
|
+
staging: `https://cdn.dev.${APEX_DOMAIN}`
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1887
1936
|
// ../constants/src/overworld.ts
|
|
1888
1937
|
var ITEM_SLUGS = {
|
|
1889
1938
|
/** Primary platform currency */
|
|
@@ -2987,15 +3036,16 @@ function processConfigVariables(config) {
|
|
|
2987
3036
|
}
|
|
2988
3037
|
|
|
2989
3038
|
// src/lib/dev/server.ts
|
|
3039
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as rmSync2 } from "fs";
|
|
2990
3040
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
2991
|
-
import { join as
|
|
3041
|
+
import { join as join16 } from "path";
|
|
2992
3042
|
import { Log, LogLevel, Miniflare } from "miniflare";
|
|
2993
3043
|
|
|
2994
3044
|
// ../utils/src/port.ts
|
|
2995
3045
|
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2996
3046
|
import { createServer } from "node:net";
|
|
2997
3047
|
import { homedir } from "node:os";
|
|
2998
|
-
import { join as
|
|
3048
|
+
import { join as join6 } from "node:path";
|
|
2999
3049
|
async function isPortAvailableOnHost(port, host) {
|
|
3000
3050
|
return new Promise((resolve4) => {
|
|
3001
3051
|
const server = createServer();
|
|
@@ -3034,11 +3084,11 @@ async function findAvailablePort(startPort = 4321) {
|
|
|
3034
3084
|
}
|
|
3035
3085
|
function getRegistryPath() {
|
|
3036
3086
|
const home = homedir();
|
|
3037
|
-
const dir =
|
|
3087
|
+
const dir = join6(home, ".playcademy");
|
|
3038
3088
|
if (!existsSync2(dir)) {
|
|
3039
3089
|
mkdirSync(dir, { recursive: true });
|
|
3040
3090
|
}
|
|
3041
|
-
return
|
|
3091
|
+
return join6(dir, ".proc");
|
|
3042
3092
|
}
|
|
3043
3093
|
function readRegistry() {
|
|
3044
3094
|
const registryPath = getRegistryPath();
|
|
@@ -3084,7 +3134,7 @@ import { PlaycademyClient } from "@playcademy/sdk";
|
|
|
3084
3134
|
// ../utils/src/package-manager.ts
|
|
3085
3135
|
import { execSync } from "child_process";
|
|
3086
3136
|
import { existsSync as existsSync3 } from "fs";
|
|
3087
|
-
import { join as
|
|
3137
|
+
import { join as join7 } from "path";
|
|
3088
3138
|
function isCommandAvailable(command) {
|
|
3089
3139
|
try {
|
|
3090
3140
|
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
@@ -3094,16 +3144,16 @@ function isCommandAvailable(command) {
|
|
|
3094
3144
|
}
|
|
3095
3145
|
}
|
|
3096
3146
|
function detectPackageManager(cwd = process.cwd()) {
|
|
3097
|
-
if (existsSync3(
|
|
3147
|
+
if (existsSync3(join7(cwd, "bun.lock")) || existsSync3(join7(cwd, "bun.lockb"))) {
|
|
3098
3148
|
return "bun";
|
|
3099
3149
|
}
|
|
3100
|
-
if (existsSync3(
|
|
3150
|
+
if (existsSync3(join7(cwd, "pnpm-lock.yaml"))) {
|
|
3101
3151
|
return "pnpm";
|
|
3102
3152
|
}
|
|
3103
|
-
if (existsSync3(
|
|
3153
|
+
if (existsSync3(join7(cwd, "yarn.lock"))) {
|
|
3104
3154
|
return "yarn";
|
|
3105
3155
|
}
|
|
3106
|
-
if (existsSync3(
|
|
3156
|
+
if (existsSync3(join7(cwd, "package-lock.json"))) {
|
|
3107
3157
|
return "npm";
|
|
3108
3158
|
}
|
|
3109
3159
|
return detectByCommandAvailability();
|
|
@@ -3180,7 +3230,7 @@ init_package_json();
|
|
|
3180
3230
|
// src/lib/secrets/env.ts
|
|
3181
3231
|
init_file_loader();
|
|
3182
3232
|
import { existsSync as existsSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
3183
|
-
import { join as
|
|
3233
|
+
import { join as join8 } from "path";
|
|
3184
3234
|
function parseEnvFile(contents) {
|
|
3185
3235
|
const secrets = {};
|
|
3186
3236
|
for (const line of contents.split("\n")) {
|
|
@@ -3216,10 +3266,10 @@ async function readEnvFile(workspace) {
|
|
|
3216
3266
|
return secrets;
|
|
3217
3267
|
}
|
|
3218
3268
|
function getLoadedEnvFiles(workspace) {
|
|
3219
|
-
return ENV_FILES.filter((filename) => existsSync4(
|
|
3269
|
+
return ENV_FILES.filter((filename) => existsSync4(join8(workspace, filename)));
|
|
3220
3270
|
}
|
|
3221
3271
|
function hasEnvFile(workspace) {
|
|
3222
|
-
return ENV_FILES.some((filename) => existsSync4(
|
|
3272
|
+
return ENV_FILES.some((filename) => existsSync4(join8(workspace, filename)));
|
|
3223
3273
|
}
|
|
3224
3274
|
|
|
3225
3275
|
// src/lib/templates/loader.ts
|
|
@@ -3246,12 +3296,12 @@ function loadTemplateString(filename) {
|
|
|
3246
3296
|
// src/lib/core/import.ts
|
|
3247
3297
|
import { mkdtempSync, rmSync } from "fs";
|
|
3248
3298
|
import { tmpdir } from "os";
|
|
3249
|
-
import { join as
|
|
3299
|
+
import { join as join9 } from "path";
|
|
3250
3300
|
import { pathToFileURL } from "url";
|
|
3251
3301
|
import * as esbuild from "esbuild";
|
|
3252
3302
|
async function importTypescriptFile(filePath, bundleOptions) {
|
|
3253
|
-
const tempDir = mkdtempSync(
|
|
3254
|
-
const outFile =
|
|
3303
|
+
const tempDir = mkdtempSync(join9(tmpdir(), "playcademy-import-"));
|
|
3304
|
+
const outFile = join9(tempDir, "bundle.mjs");
|
|
3255
3305
|
try {
|
|
3256
3306
|
await esbuild.build({
|
|
3257
3307
|
entryPoints: [filePath],
|
|
@@ -3280,10 +3330,10 @@ async function importTypescriptDefault(filePath, bundleOptions) {
|
|
|
3280
3330
|
|
|
3281
3331
|
// src/lib/deploy/bundle.ts
|
|
3282
3332
|
import { existsSync as existsSync6 } from "fs";
|
|
3283
|
-
import { join as
|
|
3333
|
+
import { join as join11 } from "path";
|
|
3284
3334
|
|
|
3285
3335
|
// ../edge-play/src/entry.ts
|
|
3286
|
-
var entry_default = "/**\n * Game Backend Entry Point\n *\n * This file is the main entry point for deployed game backends.\n * It creates a Hono app and registers all enabled integration routes.\n *\n * Bundled with esbuild and deployed to Cloudflare Workers (or AWS Lambda).\n * Config is injected at build time via esbuild's `define` option.\n *\n * DO NOT REMOVE any code wrapped by \u26A0\uFE0F BUILD_MARKER: <marker> \u26A0\uFE0F\n */\n\nimport { Hono } from 'hono'\n\nimport {\n registerApiNotFoundHandler,\n registerAssetFallback,\n registerCors,\n registerEnvSetup,\n registerPlaycademyUser,\n registerSdkInit,\n} from './entry/middleware'\nimport { setupProcessGlobal } from './entry/setup'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { RuntimeConfig } from './entry/types'\nimport type { HonoEnv } from './types'\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTE_IMPORTS \u26A0\uFE0F\n\n/**\n * Config injected at build time by esbuild\n *\n * The `declare const` tells TypeScript \"this exists at runtime, trust me.\"\n * During bundling, esbuild's `define` option does literal text replacement:\n *\n * Example bundling:\n * Source: if (PLAYCADEMY_CONFIG.integrations.timeback) { ... }\n * Define: { 'PLAYCADEMY_CONFIG': JSON.stringify({ integrations: { timeback: {...} } }) }\n * Output: if ({\"integrations\":{\"timeback\":{...}}}.integrations.timeback) { ... }\n *\n * This enables tree-shaking: if timeback is not configured, those code paths are removed.\n * The bundled Worker only includes the routes that are actually enabled.\n */\ndeclare const PLAYCADEMY_CONFIG: RuntimeConfig\n\n// Setup process global polyfill for SDK compatibility\nsetupProcessGlobal()\n\n// Create Hono app\nconst app = new Hono<HonoEnv>()\n\n// Register middleware\nregisterCors(app)\nregisterEnvSetup(app, PLAYCADEMY_CONFIG)\nregisterSdkInit(app, PLAYCADEMY_CONFIG)\nregisterPlaycademyUser(app)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: SESSION_MIDDLEWARE \u26A0\uFE0F\n\n// Register built-in integration routes based on enabled integrations\n// This function conditionally imports and registers routes like:\n// - GET /api (always included)\n// - GET /api/health (always included)\n// - POST /api/integrations/timeback/end-activity (if timeback enabled)\n//\n// Uses dynamic imports for tree-shaking: if an integration is not enabled,\n// its route code is completely removed from the bundle.\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTES \u26A0\uFE0F\n\n// Register API 404 handler\n// Returns JSON error for unmatched /api/* routes\n// Must be registered after all API routes\nregisterApiNotFoundHandler(app)\n\n// Register static asset fallback handler\n// Serves frontend assets from Workers Assets binding\n// MUST be registered last as it uses a wildcard GET route (app.get('*', ...))\n//\n// In production: Serves frontend assets from Workers Assets binding\n// In local dev: Returns 404 (Vite serves the frontend separately)\nregisterAssetFallback(app)\n\nexport default app\n";
|
|
3336
|
+
var entry_default = "/**\n * Game Backend Entry Point\n *\n * This file is the main entry point for deployed game backends.\n * It creates a Hono app and registers all enabled integration routes.\n *\n * Bundled with esbuild and deployed to Cloudflare Workers (or AWS Lambda).\n * Config is injected at build time via esbuild's `define` option.\n *\n * DO NOT REMOVE any code wrapped by \u26A0\uFE0F BUILD_MARKER: <marker> \u26A0\uFE0F\n */\n\nimport { Hono } from 'hono'\n\nimport {\n registerApiNotFoundHandler,\n registerAssetFallback,\n registerCors,\n registerEnvSetup,\n registerLocalProblemSets,\n registerPlaycademyUser,\n registerSdkInit,\n} from './entry/middleware'\nimport { setupProcessGlobal } from './entry/setup'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { RuntimeConfig } from './entry/types'\nimport type { HonoEnv } from './types'\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTE_IMPORTS \u26A0\uFE0F\n\n/**\n * Config injected at build time by esbuild\n *\n * The `declare const` tells TypeScript \"this exists at runtime, trust me.\"\n * During bundling, esbuild's `define` option does literal text replacement:\n *\n * Example bundling:\n * Source: if (PLAYCADEMY_CONFIG.integrations.timeback) { ... }\n * Define: { 'PLAYCADEMY_CONFIG': JSON.stringify({ integrations: { timeback: {...} } }) }\n * Output: if ({\"integrations\":{\"timeback\":{...}}}.integrations.timeback) { ... }\n *\n * This enables tree-shaking: if timeback is not configured, those code paths are removed.\n * The bundled Worker only includes the routes that are actually enabled.\n */\ndeclare const PLAYCADEMY_CONFIG: RuntimeConfig\n\n// Setup process global polyfill for SDK compatibility\nsetupProcessGlobal()\n\n// Create Hono app\nconst app = new Hono<HonoEnv>()\n\n// Register middleware\nregisterCors(app)\nregisterEnvSetup(app, PLAYCADEMY_CONFIG)\nregisterSdkInit(app, PLAYCADEMY_CONFIG)\nregisterPlaycademyUser(app)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: SESSION_MIDDLEWARE \u26A0\uFE0F\n\n// Register built-in integration routes based on enabled integrations\n// This function conditionally imports and registers routes like:\n// - GET /api (always included)\n// - GET /api/health (always included)\n// - POST /api/integrations/timeback/end-activity (if timeback enabled)\n//\n// Uses dynamic imports for tree-shaking: if an integration is not enabled,\n// its route code is completely removed from the bundle.\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTES \u26A0\uFE0F\n\n// Register local problem sets (dev only)\n// Serves .playcademy/problems/* from environment bindings in local development\n// No-op in production (bindings not injected)\nregisterLocalProblemSets(app)\n\n// Register API 404 handler\n// Returns JSON error for unmatched /api/* routes\n// Must be registered after all API routes\nregisterApiNotFoundHandler(app)\n\n// Register static asset fallback handler\n// Serves frontend assets from Workers Assets binding\n// MUST be registered last as it uses a wildcard GET route (app.get('*', ...))\n//\n// In production: Serves frontend assets from Workers Assets binding\n// In local dev: Returns 404 (Vite serves the frontend separately)\nregisterAssetFallback(app)\n\nexport default app\n";
|
|
3287
3337
|
|
|
3288
3338
|
// ../utils/src/path.ts
|
|
3289
3339
|
import fs from "node:fs";
|
|
@@ -3553,7 +3603,7 @@ function textLoaderPlugin() {
|
|
|
3553
3603
|
init_file_loader();
|
|
3554
3604
|
import { mkdir, writeFile } from "fs/promises";
|
|
3555
3605
|
import { tmpdir as tmpdir2 } from "os";
|
|
3556
|
-
import { join as
|
|
3606
|
+
import { join as join10, relative } from "path";
|
|
3557
3607
|
|
|
3558
3608
|
// src/lib/deploy/hash.ts
|
|
3559
3609
|
import { createHash } from "crypto";
|
|
@@ -3572,7 +3622,7 @@ async function discoverRoutes(apiDir) {
|
|
|
3572
3622
|
const routes = await Promise.all(
|
|
3573
3623
|
files.map(async (file) => {
|
|
3574
3624
|
const routePath = filePathToRoutePath(file);
|
|
3575
|
-
const absolutePath =
|
|
3625
|
+
const absolutePath = join10(apiDir, file);
|
|
3576
3626
|
const relativePath = relative(getWorkspace(), absolutePath);
|
|
3577
3627
|
const methods = await detectExportedMethods(absolutePath);
|
|
3578
3628
|
return {
|
|
@@ -3633,10 +3683,10 @@ async function transpileRoute(filePath) {
|
|
|
3633
3683
|
if (!result.outputFiles?.[0]) {
|
|
3634
3684
|
throw new Error("Transpilation failed: no output");
|
|
3635
3685
|
}
|
|
3636
|
-
const tempDir =
|
|
3686
|
+
const tempDir = join10(tmpdir2(), "playcademy-dev");
|
|
3637
3687
|
await mkdir(tempDir, { recursive: true });
|
|
3638
3688
|
const hash = hashContent(filePath).slice(0, 12);
|
|
3639
|
-
const jsPath =
|
|
3689
|
+
const jsPath = join10(tempDir, `${hash}.mjs`);
|
|
3640
3690
|
await writeFile(jsPath, result.outputFiles[0].text);
|
|
3641
3691
|
return jsPath;
|
|
3642
3692
|
}
|
|
@@ -3647,7 +3697,7 @@ async function discoverCustomRoutes(config) {
|
|
|
3647
3697
|
const workspace = getWorkspace();
|
|
3648
3698
|
const customRoutesConfig = config.integrations?.customRoutes;
|
|
3649
3699
|
const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
|
|
3650
|
-
const customRoutes = await discoverRoutes(
|
|
3700
|
+
const customRoutes = await discoverRoutes(join11(workspace, customRoutesDir));
|
|
3651
3701
|
const customRouteData = customRoutes.map((r) => ({
|
|
3652
3702
|
path: r.path,
|
|
3653
3703
|
file: r.file,
|
|
@@ -3659,15 +3709,15 @@ async function discoverCustomRoutes(config) {
|
|
|
3659
3709
|
function resolveEmbeddedSourcePaths() {
|
|
3660
3710
|
const workspace = getWorkspace();
|
|
3661
3711
|
const distDir = new URL(".", import.meta.url).pathname;
|
|
3662
|
-
const embeddedEdgeSrc =
|
|
3712
|
+
const embeddedEdgeSrc = join11(distDir, "edge-play", "src");
|
|
3663
3713
|
const isBuiltPackage = existsSync6(embeddedEdgeSrc);
|
|
3664
3714
|
const monorepoRoot = getMonorepoRoot();
|
|
3665
|
-
const monorepoEdgeSrc =
|
|
3715
|
+
const monorepoEdgeSrc = join11(monorepoRoot, "packages/edge-play/src");
|
|
3666
3716
|
const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
|
|
3667
|
-
const cliPackageRoot = isBuiltPackage ?
|
|
3668
|
-
const cliNodeModules = isBuiltPackage ?
|
|
3669
|
-
const workspaceNodeModules =
|
|
3670
|
-
const constantsEntry = isBuiltPackage ?
|
|
3717
|
+
const cliPackageRoot = isBuiltPackage ? join11(distDir, "../../..") : join11(monorepoRoot, "packages/cli");
|
|
3718
|
+
const cliNodeModules = isBuiltPackage ? join11(cliPackageRoot, "node_modules") : monorepoRoot;
|
|
3719
|
+
const workspaceNodeModules = join11(workspace, "node_modules");
|
|
3720
|
+
const constantsEntry = isBuiltPackage ? join11(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join11(monorepoRoot, "packages", "constants", "src", "index.ts");
|
|
3671
3721
|
return {
|
|
3672
3722
|
isBuiltPackage,
|
|
3673
3723
|
edgePlaySrc,
|
|
@@ -3732,22 +3782,22 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
|
|
|
3732
3782
|
// │ Example: import * as route from '@game-api/hello.ts' │
|
|
3733
3783
|
// │ Resolves to: /user-project/server/api/hello.ts │
|
|
3734
3784
|
// └─────────────────────────────────────────────────────────────────┘
|
|
3735
|
-
"@game-api":
|
|
3785
|
+
"@game-api": join11(workspace, customRoutesDir),
|
|
3736
3786
|
// ┌─ User's server lib directory ───────────────────────────────────┐
|
|
3737
3787
|
// │ @game-server is a virtual module for server utilities/config │
|
|
3738
3788
|
// │ Example: import { getAuth } from '@game-server/lib/auth' │
|
|
3739
3789
|
// │ Resolves to: /user-project/server/lib/auth.ts │
|
|
3740
3790
|
// └─────────────────────────────────────────────────────────────────┘
|
|
3741
|
-
"@game-server":
|
|
3791
|
+
"@game-server": join11(workspace, "server"),
|
|
3742
3792
|
// ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
|
|
3743
3793
|
// │ Workers don't have fs, path, os, etc. Redirect to polyfills │
|
|
3744
3794
|
// │ that throw helpful errors if user code tries to use them. │
|
|
3745
3795
|
// └─────────────────────────────────────────────────────────────────┘
|
|
3746
|
-
fs:
|
|
3747
|
-
"fs/promises":
|
|
3748
|
-
path:
|
|
3749
|
-
os:
|
|
3750
|
-
process:
|
|
3796
|
+
fs: join11(edgePlaySrc, "polyfills.js"),
|
|
3797
|
+
"fs/promises": join11(edgePlaySrc, "polyfills.js"),
|
|
3798
|
+
path: join11(edgePlaySrc, "polyfills.js"),
|
|
3799
|
+
os: join11(edgePlaySrc, "polyfills.js"),
|
|
3800
|
+
process: join11(edgePlaySrc, "polyfills.js")
|
|
3751
3801
|
},
|
|
3752
3802
|
// ──── Build Plugins ────
|
|
3753
3803
|
plugins: [textLoaderPlugin()],
|
|
@@ -3821,9 +3871,12 @@ function generateEntryCode(customRoutes, customRoutesDir, hasAuth) {
|
|
|
3821
3871
|
}
|
|
3822
3872
|
|
|
3823
3873
|
// src/lib/init/prompts.ts
|
|
3824
|
-
import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
3874
|
+
import { checkbox, confirm, input, select, Separator as Separator3 } from "@inquirer/prompts";
|
|
3825
3875
|
import { bold as bold4, cyan as cyan2 } from "colorette";
|
|
3826
3876
|
|
|
3877
|
+
// src/lib/timeback/problem-sets.ts
|
|
3878
|
+
import { Separator as Separator2 } from "@inquirer/prompts";
|
|
3879
|
+
|
|
3827
3880
|
// src/lib/init/auth.ts
|
|
3828
3881
|
var authTemplate = loadTemplateString("auth/auth.ts");
|
|
3829
3882
|
var authCatchAllTemplate = loadTemplateString("auth/auth-catch-all.ts");
|
|
@@ -3832,12 +3885,12 @@ var protectedRouteTemplate = loadTemplateString("api/sample-protected.ts");
|
|
|
3832
3885
|
|
|
3833
3886
|
// src/lib/init/database.ts
|
|
3834
3887
|
import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3835
|
-
import { join as
|
|
3888
|
+
import { join as join12 } from "path";
|
|
3836
3889
|
|
|
3837
3890
|
// package.json
|
|
3838
3891
|
var package_default2 = {
|
|
3839
3892
|
name: "playcademy",
|
|
3840
|
-
version: "0.14.
|
|
3893
|
+
version: "0.14.14",
|
|
3841
3894
|
type: "module",
|
|
3842
3895
|
exports: {
|
|
3843
3896
|
".": {
|
|
@@ -3916,8 +3969,7 @@ var version = package_default2.version;
|
|
|
3916
3969
|
|
|
3917
3970
|
// src/lib/init/database.ts
|
|
3918
3971
|
var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
|
|
3919
|
-
var
|
|
3920
|
-
var dbSchemaScoresTemplate = loadTemplateString("database/db-schema-scores.ts");
|
|
3972
|
+
var dbSchemaExampleTemplate = loadTemplateString("database/db-schema-example.ts");
|
|
3921
3973
|
var dbSchemaIndexTemplate = loadTemplateString("database/db-schema-index.ts");
|
|
3922
3974
|
var dbIndexTemplate = loadTemplateString("database/db-index.ts");
|
|
3923
3975
|
var dbTypesTemplate = loadTemplateString("database/db-types.ts");
|
|
@@ -3925,9 +3977,7 @@ var dbSeedTemplate = loadTemplateString("database/db-seed.ts");
|
|
|
3925
3977
|
var packageTemplate = loadTemplateString("database/package.json");
|
|
3926
3978
|
function hasDatabaseSetup() {
|
|
3927
3979
|
const workspace = getWorkspace();
|
|
3928
|
-
|
|
3929
|
-
const drizzleConfigJsPath = join8(workspace, "drizzle.config.js");
|
|
3930
|
-
return existsSync7(drizzleConfigPath) || existsSync7(drizzleConfigJsPath);
|
|
3980
|
+
return DRIZZLE_CONFIG_FILES.some((filename) => existsSync7(join12(workspace, filename)));
|
|
3931
3981
|
}
|
|
3932
3982
|
|
|
3933
3983
|
// src/lib/init/scaffold.ts
|
|
@@ -3951,16 +4001,16 @@ function hasBucketSetup(config) {
|
|
|
3951
4001
|
init_file_loader();
|
|
3952
4002
|
import { execSync as execSync2 } from "child_process";
|
|
3953
4003
|
import { existsSync as existsSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
3954
|
-
import { dirname as dirname4, join as
|
|
4004
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
3955
4005
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3956
4006
|
|
|
3957
4007
|
// src/lib/deploy/backend.ts
|
|
3958
4008
|
import { existsSync as existsSync8 } from "node:fs";
|
|
3959
|
-
import { join as
|
|
4009
|
+
import { join as join13 } from "node:path";
|
|
3960
4010
|
function getCustomRoutesDirectory(projectPath, config) {
|
|
3961
4011
|
const customRoutes = config?.integrations?.customRoutes;
|
|
3962
4012
|
const customRoutesDir = typeof customRoutes === "object" && customRoutes.directory || DEFAULT_API_ROUTES_DIRECTORY;
|
|
3963
|
-
return
|
|
4013
|
+
return join13(projectPath, customRoutesDir);
|
|
3964
4014
|
}
|
|
3965
4015
|
function hasLocalCustomRoutes(projectPath, config) {
|
|
3966
4016
|
const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
|
|
@@ -3970,7 +4020,7 @@ function hasLocalCustomRoutes(projectPath, config) {
|
|
|
3970
4020
|
// src/lib/init/tsconfig.ts
|
|
3971
4021
|
init_file_loader();
|
|
3972
4022
|
import { existsSync as existsSync9, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3973
|
-
import { join as
|
|
4023
|
+
import { join as join14 } from "path";
|
|
3974
4024
|
function hasPlaycademyEnv(config) {
|
|
3975
4025
|
return config.include?.includes("playcademy-env.d.ts") ?? false;
|
|
3976
4026
|
}
|
|
@@ -4027,7 +4077,7 @@ function addToIncludeArrayPreservingComments(content) {
|
|
|
4027
4077
|
}
|
|
4028
4078
|
async function ensureTsconfigIncludes(workspace) {
|
|
4029
4079
|
for (const filename of TSCONFIG_FILES) {
|
|
4030
|
-
const configPath =
|
|
4080
|
+
const configPath = join14(workspace, filename);
|
|
4031
4081
|
if (!existsSync9(configPath)) {
|
|
4032
4082
|
continue;
|
|
4033
4083
|
}
|
|
@@ -4077,8 +4127,8 @@ function hasAnyBackend(features) {
|
|
|
4077
4127
|
return Object.values(features).some(Boolean);
|
|
4078
4128
|
}
|
|
4079
4129
|
async function setupPlaycademyDependencies(workspace) {
|
|
4080
|
-
const playcademyDir =
|
|
4081
|
-
const playcademyPkgPath =
|
|
4130
|
+
const playcademyDir = join15(workspace, CLI_DIRECTORIES.WORKSPACE);
|
|
4131
|
+
const playcademyPkgPath = join15(playcademyDir, "package.json");
|
|
4082
4132
|
const __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
4083
4133
|
const cliPkg = await loadPackageJson({ cwd: __dirname, searchUp: true, required: true });
|
|
4084
4134
|
const workersTypesVersion = cliPkg?.devDependencies?.["@cloudflare/workers-types"] || "latest";
|
|
@@ -4163,14 +4213,14 @@ async function ensurePlaycademyTypes(options = {}) {
|
|
|
4163
4213
|
const bindingsStr = generateBindingsTypeString(features);
|
|
4164
4214
|
const secretsStr = await generateSecretsTypeString(workspace, verbose);
|
|
4165
4215
|
const hasAuth = !!config.integrations?.auth;
|
|
4166
|
-
const hasAuthFile = existsSync10(
|
|
4216
|
+
const hasAuthFile = existsSync10(join15(workspace, "server/lib/auth.ts"));
|
|
4167
4217
|
const authVariablesString = generateAuthVariablesString(hasAuth, hasAuthFile);
|
|
4168
4218
|
let envContent = playcademyEnvTemplate.replace("{{BINDINGS}}", bindingsStr);
|
|
4169
4219
|
envContent = envContent.replace("{{SECRETS}}", secretsStr);
|
|
4170
4220
|
envContent = envContent.replace("{{AUTH_IMPORT}}", authVariablesString.authImport);
|
|
4171
4221
|
envContent = envContent.replace("{{VARIABLES}}", authVariablesString.variables);
|
|
4172
4222
|
envContent = envContent.replace("{{CONTEXT_VARS}}", authVariablesString.contextVars);
|
|
4173
|
-
const envPath =
|
|
4223
|
+
const envPath = join15(workspace, "playcademy-env.d.ts");
|
|
4174
4224
|
writeFileSync5(envPath, envContent);
|
|
4175
4225
|
if (verbose) {
|
|
4176
4226
|
logger.success(`Generated <playcademy-env.d.ts>`);
|
|
@@ -4231,8 +4281,9 @@ async function startDevServer(options) {
|
|
|
4231
4281
|
const dbDir = hasDatabase ? await ensureDatabaseDirectory() : void 0;
|
|
4232
4282
|
const hasKV = hasKVSetup(config);
|
|
4233
4283
|
const kvDir = hasKV ? await ensureKvDirectory() : void 0;
|
|
4284
|
+
const bucketDir = await ensureBucketDirectory();
|
|
4234
4285
|
const hasBucket = hasBucketSetup(config);
|
|
4235
|
-
|
|
4286
|
+
clearProblemSetsBucket(bucketDir);
|
|
4236
4287
|
const workspace = getWorkspace();
|
|
4237
4288
|
const envSecrets = await readEnvFile(workspace);
|
|
4238
4289
|
const log2 = logger2 ? new FilteredLog(LogLevel.INFO, customLogger) : new Log(LogLevel.NONE);
|
|
@@ -4261,7 +4312,10 @@ async function startDevServer(options) {
|
|
|
4261
4312
|
d1Persist: dbDir,
|
|
4262
4313
|
kvNamespaces: hasKV ? [CLOUDFLARE_BINDINGS.KV] : [],
|
|
4263
4314
|
kvPersist: kvDir,
|
|
4264
|
-
r2Buckets:
|
|
4315
|
+
r2Buckets: [
|
|
4316
|
+
...hasBucket ? [CLOUDFLARE_BINDINGS.BUCKET] : [],
|
|
4317
|
+
CLOUDFLARE_BINDINGS.BUCKET_PROBLEM_SETS
|
|
4318
|
+
],
|
|
4265
4319
|
r2Persist: bucketDir,
|
|
4266
4320
|
compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
|
|
4267
4321
|
/**
|
|
@@ -4276,14 +4330,13 @@ async function startDevServer(options) {
|
|
|
4276
4330
|
*/
|
|
4277
4331
|
compatibilityFlags: ["nodejs_compat"]
|
|
4278
4332
|
});
|
|
4279
|
-
if (hasDatabase)
|
|
4280
|
-
|
|
4281
|
-
}
|
|
4333
|
+
if (hasDatabase) await initializeDatabase(mf);
|
|
4334
|
+
await initializeProblemSets(mf);
|
|
4282
4335
|
await writeBackendServerInfo(port);
|
|
4283
4336
|
return { server: mf, port };
|
|
4284
4337
|
}
|
|
4285
4338
|
async function ensureDatabaseDirectory() {
|
|
4286
|
-
const dbDir =
|
|
4339
|
+
const dbDir = join16(getWorkspace(), CLI_DIRECTORIES.DATABASE);
|
|
4287
4340
|
try {
|
|
4288
4341
|
await mkdir2(dbDir, { recursive: true });
|
|
4289
4342
|
} catch (error) {
|
|
@@ -4292,7 +4345,7 @@ async function ensureDatabaseDirectory() {
|
|
|
4292
4345
|
return dbDir;
|
|
4293
4346
|
}
|
|
4294
4347
|
async function ensureKvDirectory() {
|
|
4295
|
-
const kvDir =
|
|
4348
|
+
const kvDir = join16(getWorkspace(), CLI_DIRECTORIES.KV);
|
|
4296
4349
|
try {
|
|
4297
4350
|
await mkdir2(kvDir, { recursive: true });
|
|
4298
4351
|
} catch (error) {
|
|
@@ -4301,7 +4354,7 @@ async function ensureKvDirectory() {
|
|
|
4301
4354
|
return kvDir;
|
|
4302
4355
|
}
|
|
4303
4356
|
async function ensureBucketDirectory() {
|
|
4304
|
-
const bucketDir =
|
|
4357
|
+
const bucketDir = join16(getWorkspace(), CLI_DIRECTORIES.BUCKET);
|
|
4305
4358
|
try {
|
|
4306
4359
|
await mkdir2(bucketDir, { recursive: true });
|
|
4307
4360
|
} catch (error) {
|
|
@@ -4309,10 +4362,40 @@ async function ensureBucketDirectory() {
|
|
|
4309
4362
|
}
|
|
4310
4363
|
return bucketDir;
|
|
4311
4364
|
}
|
|
4365
|
+
function clearProblemSetsBucket(bucketDir) {
|
|
4366
|
+
const problemSetsBucketDir = join16(bucketDir, "PROBLEM_SETS");
|
|
4367
|
+
if (existsSync11(problemSetsBucketDir)) {
|
|
4368
|
+
try {
|
|
4369
|
+
rmSync2(problemSetsBucketDir, { recursive: true, force: true });
|
|
4370
|
+
} catch {
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4312
4374
|
async function initializeDatabase(mf) {
|
|
4313
4375
|
const d1 = await mf.getD1Database(CLOUDFLARE_BINDINGS.DB);
|
|
4314
4376
|
await d1.exec("SELECT 1");
|
|
4315
4377
|
}
|
|
4378
|
+
async function initializeProblemSets(mf) {
|
|
4379
|
+
const problemSetsDir = join16(getWorkspace(), PROBLEM_SETS_DIRECTORY);
|
|
4380
|
+
if (!existsSync11(problemSetsDir)) {
|
|
4381
|
+
return;
|
|
4382
|
+
}
|
|
4383
|
+
try {
|
|
4384
|
+
const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET_PROBLEM_SETS);
|
|
4385
|
+
for (const object of (await bucket.list()).objects) {
|
|
4386
|
+
await bucket.delete(object.key);
|
|
4387
|
+
}
|
|
4388
|
+
const files = readdirSync2(problemSetsDir);
|
|
4389
|
+
for (const file of files) {
|
|
4390
|
+
if (file.endsWith(".qti.json")) {
|
|
4391
|
+
const filePath = join16(problemSetsDir, file);
|
|
4392
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
4393
|
+
await bucket.put(join16(PROBLEM_SETS_URL_PATH, file), content);
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
} catch {
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4316
4399
|
async function writeBackendServerInfo(port) {
|
|
4317
4400
|
writeServerInfo("backend", {
|
|
4318
4401
|
pid: process.pid,
|
|
@@ -4324,7 +4407,8 @@ async function writeBackendServerInfo(port) {
|
|
|
4324
4407
|
}
|
|
4325
4408
|
|
|
4326
4409
|
// src/lib/dev/reload.ts
|
|
4327
|
-
import {
|
|
4410
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4411
|
+
import { basename, join as join17, relative as relative2 } from "path";
|
|
4328
4412
|
import chokidar from "chokidar";
|
|
4329
4413
|
import { bold as bold5, cyan as cyan3, dim as dim4, green as green2 } from "colorette";
|
|
4330
4414
|
function formatTime() {
|
|
@@ -4341,9 +4425,9 @@ function startHotReload(onReload, options = {}) {
|
|
|
4341
4425
|
const customRoutesConfig = options.config?.integrations?.customRoutes;
|
|
4342
4426
|
const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
|
|
4343
4427
|
const watchPaths = [
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4428
|
+
join17(workspace, customRoutesDir),
|
|
4429
|
+
join17(workspace, "playcademy.config.js"),
|
|
4430
|
+
join17(workspace, "playcademy.config.json")
|
|
4347
4431
|
];
|
|
4348
4432
|
const watcher = chokidar.watch(watchPaths, {
|
|
4349
4433
|
persistent: true,
|
|
@@ -4358,10 +4442,10 @@ function startHotReload(onReload, options = {}) {
|
|
|
4358
4442
|
const relativePath = relative2(workspace, changedPath);
|
|
4359
4443
|
const timestamp = dim4(formatTime());
|
|
4360
4444
|
const brand = bold5(cyan3("[playcademy]"));
|
|
4361
|
-
const event = eventType === "changed" ? green2("
|
|
4445
|
+
const event = eventType === "changed" ? green2("update") : green2(eventType || "update");
|
|
4362
4446
|
console.log(`${timestamp} ${brand} ${event} ${dim4(relativePath)}`);
|
|
4363
4447
|
} else {
|
|
4364
|
-
logger.success("
|
|
4448
|
+
logger.success("Updated");
|
|
4365
4449
|
}
|
|
4366
4450
|
});
|
|
4367
4451
|
const logError = options.onError || ((error) => {
|
|
@@ -4379,6 +4463,62 @@ function startHotReload(onReload, options = {}) {
|
|
|
4379
4463
|
watcher.on("change", createReloadHandler("changed"));
|
|
4380
4464
|
watcher.on("add", createReloadHandler("added"));
|
|
4381
4465
|
watcher.on("unlink", createReloadHandler("removed"));
|
|
4466
|
+
let problemSetsWatcher;
|
|
4467
|
+
if (options.miniflareRef) {
|
|
4468
|
+
problemSetsWatcher = watchProblemSets(options.miniflareRef, options.onProblemSetUpdate);
|
|
4469
|
+
}
|
|
4470
|
+
return {
|
|
4471
|
+
close: () => {
|
|
4472
|
+
watcher.close();
|
|
4473
|
+
problemSetsWatcher?.close();
|
|
4474
|
+
}
|
|
4475
|
+
};
|
|
4476
|
+
}
|
|
4477
|
+
function watchProblemSets(miniflareRef, onUpdate) {
|
|
4478
|
+
const workspace = getWorkspace();
|
|
4479
|
+
const problemSetsPath = join17(workspace, PROBLEM_SETS_DIRECTORY);
|
|
4480
|
+
const watcher = chokidar.watch(problemSetsPath, {
|
|
4481
|
+
persistent: true,
|
|
4482
|
+
ignoreInitial: true,
|
|
4483
|
+
awaitWriteFinish: {
|
|
4484
|
+
stabilityThreshold: 100,
|
|
4485
|
+
pollInterval: 100
|
|
4486
|
+
}
|
|
4487
|
+
});
|
|
4488
|
+
const logUpdate = (filePath, eventType) => {
|
|
4489
|
+
const relativePath = relative2(workspace, filePath);
|
|
4490
|
+
if (onUpdate) {
|
|
4491
|
+
onUpdate(relativePath, eventType);
|
|
4492
|
+
return;
|
|
4493
|
+
}
|
|
4494
|
+
const timestamp = dim4(formatTime());
|
|
4495
|
+
const brand = bold5(cyan3("[playcademy]"));
|
|
4496
|
+
const event = green2(eventType);
|
|
4497
|
+
console.log(`${timestamp} ${brand} ${event} ${dim4(relativePath)}`);
|
|
4498
|
+
};
|
|
4499
|
+
const updateProblemSet = async (filePath, operation) => {
|
|
4500
|
+
if (!filePath.endsWith(".qti.json")) return;
|
|
4501
|
+
const mf = miniflareRef.current;
|
|
4502
|
+
if (!mf) return;
|
|
4503
|
+
try {
|
|
4504
|
+
const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET_PROBLEM_SETS);
|
|
4505
|
+
const filename = basename(filePath);
|
|
4506
|
+
const key = `${PROBLEM_SETS_URL_PATH}/${filename}`;
|
|
4507
|
+
if (operation === "remove") {
|
|
4508
|
+
await bucket.delete(key);
|
|
4509
|
+
} else {
|
|
4510
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
4511
|
+
await bucket.put(key, content);
|
|
4512
|
+
}
|
|
4513
|
+
logUpdate(filePath, operation);
|
|
4514
|
+
} catch (error) {
|
|
4515
|
+
if (error instanceof Error && error.message.includes("disposed")) return;
|
|
4516
|
+
logger.error(`Failed to ${operation} problem set: ${getErrorMessage(error)}`);
|
|
4517
|
+
}
|
|
4518
|
+
};
|
|
4519
|
+
watcher.on("change", (filePath) => updateProblemSet(filePath, "update"));
|
|
4520
|
+
watcher.on("add", (filePath) => updateProblemSet(filePath, "add"));
|
|
4521
|
+
watcher.on("unlink", (filePath) => updateProblemSet(filePath, "remove"));
|
|
4382
4522
|
return watcher;
|
|
4383
4523
|
}
|
|
4384
4524
|
export {
|
package/dist/version.js
CHANGED