playcademy 0.12.7 → 0.12.9

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/db.js CHANGED
@@ -629,6 +629,9 @@ import { dirname, resolve } from "path";
629
629
  import { fileURLToPath } from "url";
630
630
  var currentDir = dirname(fileURLToPath(import.meta.url));
631
631
 
632
+ // src/lib/core/import.ts
633
+ import * as esbuild from "esbuild";
634
+
632
635
  // src/lib/db/reset.ts
633
636
  function resetDatabase() {
634
637
  const dbPath = getDevDbPath();
package/dist/index.js CHANGED
@@ -3610,6 +3610,46 @@ var init_errors = __esm({
3610
3610
  }
3611
3611
  });
3612
3612
 
3613
+ // src/lib/core/import.ts
3614
+ import { mkdtempSync, rmSync } from "fs";
3615
+ import { tmpdir } from "os";
3616
+ import { join as join3 } from "path";
3617
+ import { pathToFileURL } from "url";
3618
+ import * as esbuild from "esbuild";
3619
+ async function importTypescriptFile(filePath, bundleOptions) {
3620
+ const tempDir = mkdtempSync(join3(tmpdir(), "playcademy-import-"));
3621
+ const outFile = join3(tempDir, "bundle.mjs");
3622
+ try {
3623
+ await esbuild.build({
3624
+ entryPoints: [filePath],
3625
+ outfile: outFile,
3626
+ bundle: true,
3627
+ platform: "node",
3628
+ format: "esm",
3629
+ target: "node20",
3630
+ sourcemap: false,
3631
+ minify: false,
3632
+ ...bundleOptions
3633
+ });
3634
+ const module = await import(pathToFileURL(outFile).href);
3635
+ return module;
3636
+ } finally {
3637
+ rmSync(tempDir, { recursive: true, force: true });
3638
+ }
3639
+ }
3640
+ async function importTypescriptDefault(filePath, bundleOptions) {
3641
+ const module = await importTypescriptFile(filePath, bundleOptions);
3642
+ if (module && typeof module === "object" && "default" in module) {
3643
+ return module.default;
3644
+ }
3645
+ return module;
3646
+ }
3647
+ var init_import = __esm({
3648
+ "src/lib/core/import.ts"() {
3649
+ "use strict";
3650
+ }
3651
+ });
3652
+
3613
3653
  // src/lib/core/index.ts
3614
3654
  var core_exports = {};
3615
3655
  __export(core_exports, {
@@ -3628,6 +3668,8 @@ __export(core_exports, {
3628
3668
  getWebBaseUrl: () => getWebBaseUrl,
3629
3669
  getWorkspace: () => getWorkspace,
3630
3670
  hasPackageJson: () => hasPackageJson,
3671
+ importTypescriptDefault: () => importTypescriptDefault,
3672
+ importTypescriptFile: () => importTypescriptFile,
3631
3673
  logAndExit: () => logAndExit,
3632
3674
  logger: () => logger,
3633
3675
  normalizeEnvironment: () => normalizeEnvironment,
@@ -3642,6 +3684,7 @@ var init_core = __esm({
3642
3684
  init_context();
3643
3685
  init_errors();
3644
3686
  init_game();
3687
+ init_import();
3645
3688
  init_logger();
3646
3689
  }
3647
3690
  });
@@ -4476,7 +4519,7 @@ init_core();
4476
4519
  // src/lib/db/path.ts
4477
4520
  init_constants2();
4478
4521
  import { copyFileSync, existsSync as existsSync5, mkdirSync, readdirSync as readdirSync2, unlinkSync } from "fs";
4479
- import { join as join3 } from "path";
4522
+ import { join as join4 } from "path";
4480
4523
  import Database from "better-sqlite3";
4481
4524
  var DB_DIRECTORY = CLI_DIRECTORIES.DATABASE;
4482
4525
  var INITIAL_DB_NAME = CLI_FILES.INITIAL_DATABASE;
@@ -4490,11 +4533,11 @@ var createEmptyDatabase = (path2) => {
4490
4533
  db.close();
4491
4534
  };
4492
4535
  var findMiniflareDatabase = (dbDir) => {
4493
- const miniflareDir = join3(dbDir, "miniflare-D1DatabaseObject");
4536
+ const miniflareDir = join4(dbDir, "miniflare-D1DatabaseObject");
4494
4537
  if (!existsSync5(miniflareDir)) return null;
4495
4538
  const sqliteFiles = readdirSync2(miniflareDir).filter((file) => file.endsWith(".sqlite"));
4496
4539
  if (sqliteFiles.length === 0) return null;
4497
- return join3(miniflareDir, sqliteFiles[0]);
4540
+ return join4(miniflareDir, sqliteFiles[0]);
4498
4541
  };
4499
4542
  var migrateInitialDbToTarget = (initialPath, targetPath) => {
4500
4543
  if (!existsSync5(initialPath)) return;
@@ -4502,7 +4545,7 @@ var migrateInitialDbToTarget = (initialPath, targetPath) => {
4502
4545
  unlinkSync(initialPath);
4503
4546
  };
4504
4547
  function getDevDbPath() {
4505
- const initialDbPath = join3(DB_DIRECTORY, INITIAL_DB_NAME);
4548
+ const initialDbPath = join4(DB_DIRECTORY, INITIAL_DB_NAME);
4506
4549
  ensureDirectoryExists(DB_DIRECTORY);
4507
4550
  const miniflareDbPath = findMiniflareDatabase(DB_DIRECTORY);
4508
4551
  if (miniflareDbPath) {
@@ -4562,7 +4605,7 @@ init_src();
4562
4605
  init_constants2();
4563
4606
  init_core();
4564
4607
  import { existsSync as existsSync10 } from "node:fs";
4565
- import { join as join9 } from "node:path";
4608
+ import { join as join10 } from "node:path";
4566
4609
 
4567
4610
  // src/lib/init/database.ts
4568
4611
  init_log();
@@ -4572,7 +4615,7 @@ init_core();
4572
4615
  init_logger();
4573
4616
  init_loader2();
4574
4617
  import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4575
- import { join as join4 } from "path";
4618
+ import { join as join5 } from "path";
4576
4619
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
4577
4620
  var dbSchemaUsersTemplate = loadTemplateString("database/db-schema-users.ts");
4578
4621
  var dbSchemaScoresTemplate = loadTemplateString("database/db-schema-scores.ts");
@@ -4586,7 +4629,7 @@ function normalizeGitignoreEntry(entry) {
4586
4629
  return entry.replace(/^\/+/, "").replace(/\/+$/, "");
4587
4630
  }
4588
4631
  function ensureGitignoreEntries(workspace) {
4589
- const gitignorePath = join4(workspace, ".gitignore");
4632
+ const gitignorePath = join5(workspace, ".gitignore");
4590
4633
  if (!existsSync6(gitignorePath)) {
4591
4634
  writeFileSync2(gitignorePath, rootGitignoreTemplate);
4592
4635
  return;
@@ -4616,31 +4659,31 @@ async function scaffoldDatabaseSetup(options) {
4616
4659
  await runStep(
4617
4660
  "Configuring database...",
4618
4661
  async () => {
4619
- const dbDir = join4(workspace, "db");
4620
- const schemaDir = join4(dbDir, "schema");
4662
+ const dbDir = join5(workspace, "db");
4663
+ const schemaDir = join5(dbDir, "schema");
4621
4664
  if (!existsSync6(dbDir)) {
4622
4665
  mkdirSync2(dbDir, { recursive: true });
4623
4666
  }
4624
4667
  if (!existsSync6(schemaDir)) {
4625
4668
  mkdirSync2(schemaDir, { recursive: true });
4626
4669
  }
4627
- const usersSchemaPath = join4(schemaDir, "users.ts");
4670
+ const usersSchemaPath = join5(schemaDir, "users.ts");
4628
4671
  writeFileSync2(usersSchemaPath, dbSchemaUsersTemplate);
4629
- const scoresSchemaPath = join4(schemaDir, "scores.ts");
4672
+ const scoresSchemaPath = join5(schemaDir, "scores.ts");
4630
4673
  writeFileSync2(scoresSchemaPath, dbSchemaScoresTemplate);
4631
- const schemaIndexPath = join4(schemaDir, "index.ts");
4674
+ const schemaIndexPath = join5(schemaDir, "index.ts");
4632
4675
  writeFileSync2(schemaIndexPath, dbSchemaIndexTemplate);
4633
- const dbIndexPath = join4(dbDir, "index.ts");
4676
+ const dbIndexPath = join5(dbDir, "index.ts");
4634
4677
  writeFileSync2(dbIndexPath, dbIndexTemplate);
4635
- const dbTypesPath = join4(dbDir, "types.ts");
4678
+ const dbTypesPath = join5(dbDir, "types.ts");
4636
4679
  writeFileSync2(dbTypesPath, dbTypesTemplate);
4637
- const drizzleConfigPath = join4(workspace, "drizzle.config.ts");
4680
+ const drizzleConfigPath = join5(workspace, "drizzle.config.ts");
4638
4681
  writeFileSync2(drizzleConfigPath, drizzleConfigTemplate);
4639
- const playcademyDir = join4(workspace, CLI_DIRECTORIES.WORKSPACE);
4682
+ const playcademyDir = join5(workspace, CLI_DIRECTORIES.WORKSPACE);
4640
4683
  if (!existsSync6(playcademyDir)) {
4641
4684
  mkdirSync2(playcademyDir, { recursive: true });
4642
4685
  }
4643
- const playcademyGitignorePath = join4(playcademyDir, ".gitignore");
4686
+ const playcademyGitignorePath = join5(playcademyDir, ".gitignore");
4644
4687
  writeFileSync2(playcademyGitignorePath, playcademyGitignoreTemplate);
4645
4688
  ensureGitignoreEntries(workspace);
4646
4689
  packagesUpdated = await setupPackageJson(workspace, options.gameName);
@@ -4650,7 +4693,7 @@ async function scaffoldDatabaseSetup(options) {
4650
4693
  return packagesUpdated;
4651
4694
  }
4652
4695
  async function setupPackageJson(workspace, gameName) {
4653
- const pkgPath = join4(workspace, "package.json");
4696
+ const pkgPath = join5(workspace, "package.json");
4654
4697
  const dbDeps = {
4655
4698
  "drizzle-orm": "^0.42.0",
4656
4699
  "better-sqlite3": "^12.0.0"
@@ -4690,8 +4733,8 @@ async function setupPackageJson(workspace, gameName) {
4690
4733
  }
4691
4734
  function hasDatabaseSetup() {
4692
4735
  const workspace = getWorkspace();
4693
- const drizzleConfigPath = join4(workspace, "drizzle.config.ts");
4694
- const drizzleConfigJsPath = join4(workspace, "drizzle.config.js");
4736
+ const drizzleConfigPath = join5(workspace, "drizzle.config.ts");
4737
+ const drizzleConfigJsPath = join5(workspace, "drizzle.config.js");
4695
4738
  return existsSync6(drizzleConfigPath) || existsSync6(drizzleConfigJsPath);
4696
4739
  }
4697
4740
 
@@ -4825,7 +4868,7 @@ var integrationChangeDetectors = {
4825
4868
 
4826
4869
  // src/lib/deploy/bundle.ts
4827
4870
  import { existsSync as existsSync7 } from "fs";
4828
- import { join as join6 } from "path";
4871
+ import { join as join7 } from "path";
4829
4872
 
4830
4873
  // ../edge-play/src/entry.ts
4831
4874
  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\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\n\nimport { PlaycademyClient } from '@playcademy/sdk/server'\n\nimport { ENV_VARS } from './constants'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { PlaycademyConfig } from '@playcademy/sdk/server'\nimport type { HonoEnv } from './types'\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: PlaycademyConfig & {\n customRoutes?: Array<{ path: string; file: string }>\n}\n\n// XXX: Polyfill process global for SDK compatibility\n// SDK code may reference process.env without importing it\n// @ts-expect-error - Adding global for Worker environment\nglobalThis.process = {\n env: {}, // Populated per-request from Worker env bindings\n cwd: () => '/',\n}\n\nconst app = new Hono<HonoEnv>()\n\n// TODO: Harden CORS in production - restrict to trusted origins:\n// - Game's assetBundleBase (for hosted games)\n// - Game's externalUrl (for external games)\n// - Platform frontend domains (hub.playcademy.com, hub.dev.playcademy.net)\n// This would require passing game metadata through env bindings during deployment\napp.use(\n '*',\n cors({\n origin: '*', // Permissive for now\n allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n }),\n)\n\nlet sdkPromise: Promise<PlaycademyClient> | null = null\n\napp.use('*', async (c, next) => {\n // Populate process.env from Worker bindings for SDK compatibility\n globalThis.process.env = {\n [ENV_VARS.PLAYCADEMY_API_KEY]: c.env.PLAYCADEMY_API_KEY,\n [ENV_VARS.GAME_ID]: c.env.GAME_ID,\n [ENV_VARS.PLAYCADEMY_BASE_URL]: c.env.PLAYCADEMY_BASE_URL,\n }\n\n // Set config for all routes\n c.set('config', PLAYCADEMY_CONFIG)\n c.set('customRoutes', PLAYCADEMY_CONFIG.customRoutes || [])\n\n await next()\n})\n\n// Initialize SDK lazily on first request\napp.use('*', async (c, next) => {\n if (!sdkPromise) {\n sdkPromise = PlaycademyClient.init({\n apiKey: c.env[ENV_VARS.PLAYCADEMY_API_KEY],\n gameId: c.env[ENV_VARS.GAME_ID],\n baseUrl: c.env[ENV_VARS.PLAYCADEMY_BASE_URL],\n config: PLAYCADEMY_CONFIG,\n })\n }\n\n c.set('sdk', await sdkPromise)\n await next()\n})\n\n/**\n * Register built-in integration routes based on enabled integrations\n *\n * This function conditionally imports and registers routes like:\n * - POST /api/integrations/timeback/end-activity (if timeback enabled)\n * - GET /api/health (always included)\n *\n * Uses dynamic imports for tree-shaking: if an integration is not enabled,\n * its route code is completely removed from the bundle.\n */\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\nexport default app\n";
@@ -5087,8 +5130,8 @@ init_core();
5087
5130
  init_file_loader();
5088
5131
  init_core();
5089
5132
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
5090
- import { tmpdir } from "os";
5091
- import { join as join5, relative } from "path";
5133
+ import { tmpdir as tmpdir2 } from "os";
5134
+ import { join as join6, relative } from "path";
5092
5135
 
5093
5136
  // src/lib/deploy/hash.ts
5094
5137
  init_file_loader();
@@ -5136,7 +5179,7 @@ async function discoverRoutes(apiDir) {
5136
5179
  const routes = await Promise.all(
5137
5180
  files.map(async (file) => {
5138
5181
  const routePath = filePathToRoutePath(file);
5139
- const absolutePath = join5(apiDir, file);
5182
+ const absolutePath = join6(apiDir, file);
5140
5183
  const relativePath = relative(getWorkspace(), absolutePath);
5141
5184
  const methods = await detectExportedMethods(absolutePath);
5142
5185
  return {
@@ -5196,10 +5239,10 @@ async function transpileRoute(filePath) {
5196
5239
  if (!result.outputFiles?.[0]) {
5197
5240
  throw new Error("Transpilation failed: no output");
5198
5241
  }
5199
- const tempDir = join5(tmpdir(), "playcademy-dev");
5242
+ const tempDir = join6(tmpdir2(), "playcademy-dev");
5200
5243
  await mkdir2(tempDir, { recursive: true });
5201
5244
  const hash = hashContent(filePath).slice(0, 12);
5202
- const jsPath = join5(tempDir, `${hash}.mjs`);
5245
+ const jsPath = join6(tempDir, `${hash}.mjs`);
5203
5246
  await writeFile2(jsPath, result.outputFiles[0].text);
5204
5247
  return jsPath;
5205
5248
  }
@@ -5227,7 +5270,7 @@ async function discoverCustomRoutes(config) {
5227
5270
  const workspace = getWorkspace();
5228
5271
  const customRoutesConfig = config.integrations?.customRoutes;
5229
5272
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
5230
- const customRoutes = await discoverRoutes(join6(workspace, customRoutesDir));
5273
+ const customRoutes = await discoverRoutes(join7(workspace, customRoutesDir));
5231
5274
  const customRouteData = customRoutes.map((r) => ({
5232
5275
  path: r.path,
5233
5276
  file: r.file,
@@ -5239,15 +5282,15 @@ async function discoverCustomRoutes(config) {
5239
5282
  function resolveEmbeddedSourcePaths() {
5240
5283
  const workspace = getWorkspace();
5241
5284
  const distDir = new URL(".", import.meta.url).pathname;
5242
- const embeddedEdgeSrc = join6(distDir, "edge-play", "src");
5285
+ const embeddedEdgeSrc = join7(distDir, "edge-play", "src");
5243
5286
  const isBuiltPackage = existsSync7(embeddedEdgeSrc);
5244
5287
  const monorepoRoot = getMonorepoRoot();
5245
- const monorepoEdgeSrc = join6(monorepoRoot, "packages/edge-play/src");
5288
+ const monorepoEdgeSrc = join7(monorepoRoot, "packages/edge-play/src");
5246
5289
  const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
5247
- const cliPackageRoot = isBuiltPackage ? join6(distDir, "../../..") : join6(monorepoRoot, "packages/cli");
5248
- const cliNodeModules = isBuiltPackage ? join6(cliPackageRoot, "node_modules") : monorepoRoot;
5249
- const workspaceNodeModules = join6(workspace, "node_modules");
5250
- const constantsEntry = isBuiltPackage ? join6(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join6(monorepoRoot, "packages", "constants", "src", "index.ts");
5290
+ const cliPackageRoot = isBuiltPackage ? join7(distDir, "../../..") : join7(monorepoRoot, "packages/cli");
5291
+ const cliNodeModules = isBuiltPackage ? join7(cliPackageRoot, "node_modules") : monorepoRoot;
5292
+ const workspaceNodeModules = join7(workspace, "node_modules");
5293
+ const constantsEntry = isBuiltPackage ? join7(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join7(monorepoRoot, "packages", "constants", "src", "index.ts");
5251
5294
  return {
5252
5295
  isBuiltPackage,
5253
5296
  edgePlaySrc,
@@ -5307,16 +5350,16 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
5307
5350
  // │ Example: import * as route from '@game-api/hello.ts' │
5308
5351
  // │ Resolves to: /user-project/server/api/hello.ts │
5309
5352
  // └─────────────────────────────────────────────────────────────────┘
5310
- "@game-api": join6(workspace, customRoutesDir),
5353
+ "@game-api": join7(workspace, customRoutesDir),
5311
5354
  // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
5312
5355
  // │ Workers don't have fs, path, os, etc. Redirect to polyfills │
5313
5356
  // │ that throw helpful errors if user code tries to use them. │
5314
5357
  // └─────────────────────────────────────────────────────────────────┘
5315
- fs: join6(edgePlaySrc, "polyfills.js"),
5316
- "fs/promises": join6(edgePlaySrc, "polyfills.js"),
5317
- path: join6(edgePlaySrc, "polyfills.js"),
5318
- os: join6(edgePlaySrc, "polyfills.js"),
5319
- process: join6(edgePlaySrc, "polyfills.js")
5358
+ fs: join7(edgePlaySrc, "polyfills.js"),
5359
+ "fs/promises": join7(edgePlaySrc, "polyfills.js"),
5360
+ path: join7(edgePlaySrc, "polyfills.js"),
5361
+ os: join7(edgePlaySrc, "polyfills.js"),
5362
+ process: join7(edgePlaySrc, "polyfills.js")
5320
5363
  },
5321
5364
  // ──── Build Plugins ────
5322
5365
  plugins: [textLoaderPlugin()],
@@ -5380,12 +5423,9 @@ function generateEntryCode(customRoutes, customRoutesDir) {
5380
5423
 
5381
5424
  // src/lib/deploy/schema.ts
5382
5425
  init_core();
5383
- import { existsSync as existsSync9, mkdtempSync, rmSync } from "fs";
5426
+ import { existsSync as existsSync9 } from "fs";
5384
5427
  import { createRequire } from "module";
5385
- import { tmpdir as tmpdir2 } from "os";
5386
- import { join as join8 } from "path";
5387
- import { pathToFileURL } from "url";
5388
- import * as esbuild from "esbuild";
5428
+ import { join as join9 } from "path";
5389
5429
 
5390
5430
  // src/lib/init/prompts.ts
5391
5431
  init_constants3();
@@ -5398,7 +5438,7 @@ init_src();
5398
5438
  init_core();
5399
5439
  init_loader2();
5400
5440
  import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5401
- import { join as join7, resolve as resolve6 } from "path";
5441
+ import { join as join8, resolve as resolve6 } from "path";
5402
5442
  var sampleRouteTemplate = loadTemplateString("api/sample-route.ts");
5403
5443
  var sampleRouteWithDbTemplate = loadTemplateString("api/sample-route-with-db.ts");
5404
5444
  async function scaffoldApiDirectory(apiDirectory, sampleRouteTemplate2) {
@@ -5408,7 +5448,7 @@ async function scaffoldApiDirectory(apiDirectory, sampleRouteTemplate2) {
5408
5448
  async () => {
5409
5449
  if (!existsSync8(apiPath)) {
5410
5450
  mkdirSync3(apiPath, { recursive: true });
5411
- writeFileSync3(join7(apiPath, "hello.ts"), sampleRouteTemplate2, "utf-8");
5451
+ writeFileSync3(join8(apiPath, "hello.ts"), sampleRouteTemplate2, "utf-8");
5412
5452
  }
5413
5453
  },
5414
5454
  "API directory scaffolded"
@@ -5640,7 +5680,7 @@ async function getSchemaInfo(previousSchemaSnapshot) {
5640
5680
  if (!hasDatabaseSetup()) {
5641
5681
  return null;
5642
5682
  }
5643
- const schemaPath = join8(workspace, "db/schema/index.ts");
5683
+ const schemaPath = join9(workspace, "db/schema/index.ts");
5644
5684
  if (!existsSync9(schemaPath)) {
5645
5685
  return null;
5646
5686
  }
@@ -5648,21 +5688,7 @@ async function getSchemaInfo(previousSchemaSnapshot) {
5648
5688
  const require2 = createRequire(import.meta.url);
5649
5689
  const drizzleKitApi = require2("drizzle-kit/api");
5650
5690
  const { generateSQLiteDrizzleJson, generateSQLiteMigration } = drizzleKitApi;
5651
- const tempDir = mkdtempSync(join8(tmpdir2(), "playcademy-schema-"));
5652
- const outFile = join8(tempDir, "schema.mjs");
5653
- await esbuild.build({
5654
- entryPoints: [schemaPath],
5655
- outfile: outFile,
5656
- bundle: true,
5657
- platform: "node",
5658
- format: "esm",
5659
- target: "node20",
5660
- sourcemap: false,
5661
- minify: false
5662
- });
5663
- const schemaModule = await import(pathToFileURL(outFile).href);
5664
- rmSync(tempDir, { recursive: true, force: true });
5665
- const currentSchema = schemaModule && typeof schemaModule === "object" && "default" in schemaModule ? schemaModule.default : schemaModule;
5691
+ const currentSchema = await importTypescriptDefault(schemaPath);
5666
5692
  const nextJson = await generateSQLiteDrizzleJson(currentSchema);
5667
5693
  const prevJson = previousSchemaSnapshot ? previousSchemaSnapshot : await generateSQLiteDrizzleJson({});
5668
5694
  const migrationStatements = await generateSQLiteMigration(prevJson, nextJson);
@@ -5704,7 +5730,7 @@ var CUSTOM_ROUTES_EXTENSIONS_WITH_DOT = [".ts", ".js", ".mjs", ".cjs"];
5704
5730
  function getCustomRoutesDirectory(projectPath, config) {
5705
5731
  const customRoutes = config?.integrations?.customRoutes;
5706
5732
  const customRoutesDir = typeof customRoutes === "object" && customRoutes.directory || DEFAULT_API_ROUTES_DIRECTORY;
5707
- return join9(projectPath, customRoutesDir);
5733
+ return join10(projectPath, customRoutesDir);
5708
5734
  }
5709
5735
  function hasLocalCustomRoutes(projectPath, config) {
5710
5736
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
@@ -5716,7 +5742,7 @@ async function getCustomRoutesHash(projectPath, config) {
5716
5742
  }
5717
5743
  async function getCustomRoutesSize(projectPath, config) {
5718
5744
  const { stat: stat3, readdir } = await import("node:fs/promises");
5719
- const { join: join19 } = await import("node:path");
5745
+ const { join: join20 } = await import("node:path");
5720
5746
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
5721
5747
  if (!existsSync10(customRoutesDir)) {
5722
5748
  return null;
@@ -5725,7 +5751,7 @@ async function getCustomRoutesSize(projectPath, config) {
5725
5751
  async function calculateDirSize(dir) {
5726
5752
  const entries = await readdir(dir, { withFileTypes: true });
5727
5753
  for (const entry of entries) {
5728
- const fullPath = join19(dir, entry.name);
5754
+ const fullPath = join20(dir, entry.name);
5729
5755
  if (entry.isDirectory()) {
5730
5756
  await calculateDirSize(fullPath);
5731
5757
  } else if (entry.isFile()) {
@@ -6095,10 +6121,10 @@ import { dim as dim4 } from "colorette";
6095
6121
  init_file_loader();
6096
6122
  init_constants2();
6097
6123
  init_core();
6098
- import { join as join10, relative as relative2 } from "path";
6124
+ import { join as join11, relative as relative2 } from "path";
6099
6125
  function findSingleBuildZip() {
6100
6126
  const workspace = getWorkspace();
6101
- const playcademyDir = join10(workspace, CLI_DIRECTORIES.WORKSPACE);
6127
+ const playcademyDir = join11(workspace, CLI_DIRECTORIES.WORKSPACE);
6102
6128
  const zipFiles = findFilesByExtension(playcademyDir, "zip");
6103
6129
  if (zipFiles.length === 1) {
6104
6130
  return zipFiles[0] ? relative2(workspace, zipFiles[0]) : null;
@@ -6934,12 +6960,12 @@ init_constants2();
6934
6960
  import { existsSync as existsSync12 } from "node:fs";
6935
6961
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
6936
6962
  import { homedir as homedir2 } from "node:os";
6937
- import { join as join11 } from "node:path";
6963
+ import { join as join12 } from "node:path";
6938
6964
  function getGamesStorePath() {
6939
- return join11(homedir2(), CLI_USER_DIRECTORIES.CONFIG, CLI_FILES.GAMES_STORE);
6965
+ return join12(homedir2(), CLI_USER_DIRECTORIES.CONFIG, CLI_FILES.GAMES_STORE);
6940
6966
  }
6941
6967
  async function ensureConfigDir() {
6942
- const configDir = join11(homedir2(), CLI_USER_DIRECTORIES.CONFIG);
6968
+ const configDir = join12(homedir2(), CLI_USER_DIRECTORIES.CONFIG);
6943
6969
  await mkdir3(configDir, { recursive: true });
6944
6970
  }
6945
6971
  async function loadGameStore() {
@@ -7430,12 +7456,12 @@ async function saveDeploymentState(game, backendDeployment, context2) {
7430
7456
  init_constants2();
7431
7457
  init_core();
7432
7458
  import { existsSync as existsSync14 } from "fs";
7433
- import { join as join12 } from "path";
7459
+ import { join as join13 } from "path";
7434
7460
  function hasCustomRoutes(config) {
7435
7461
  const workspace = getWorkspace();
7436
7462
  const customRoutesConfig = config?.integrations?.customRoutes;
7437
7463
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
7438
- return existsSync14(join12(workspace, customRoutesDir));
7464
+ return existsSync14(join13(workspace, customRoutesDir));
7439
7465
  }
7440
7466
  function needsBackend(config) {
7441
7467
  return !!config?.integrations || hasCustomRoutes(config);
@@ -7486,13 +7512,13 @@ init_constants2();
7486
7512
  init_core();
7487
7513
  import { existsSync as existsSync15, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
7488
7514
  import { mkdir as mkdir4, unlink, writeFile as writeFile4 } from "fs/promises";
7489
- import { join as join13 } from "path";
7515
+ import { join as join14 } from "path";
7490
7516
  function getDevServerPidPath() {
7491
- return join13(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
7517
+ return join14(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
7492
7518
  }
7493
7519
  async function createDevServerPidFile() {
7494
7520
  const pidPath = getDevServerPidFile();
7495
- const pidDir = join13(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
7521
+ const pidDir = join14(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
7496
7522
  await mkdir4(pidDir, { recursive: true });
7497
7523
  await writeFile4(pidPath, process.pid.toString());
7498
7524
  }
@@ -7532,7 +7558,7 @@ function getDevServerPidFile() {
7532
7558
  // src/lib/dev/reload.ts
7533
7559
  init_constants2();
7534
7560
  init_core();
7535
- import { join as join14, relative as relative3 } from "path";
7561
+ import { join as join15, relative as relative3 } from "path";
7536
7562
  import chokidar from "chokidar";
7537
7563
  import { bold as bold4, cyan as cyan3, dim as dim6, green as green3 } from "colorette";
7538
7564
  function formatTime() {
@@ -7549,9 +7575,9 @@ function startHotReload(onReload, options = {}) {
7549
7575
  const customRoutesConfig = options.config?.integrations?.customRoutes;
7550
7576
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
7551
7577
  const watchPaths = [
7552
- join14(workspace, customRoutesDir),
7553
- join14(workspace, "playcademy.config.js"),
7554
- join14(workspace, "playcademy.config.json")
7578
+ join15(workspace, customRoutesDir),
7579
+ join15(workspace, "playcademy.config.js"),
7580
+ join15(workspace, "playcademy.config.json")
7555
7581
  ];
7556
7582
  const watcher = chokidar.watch(watchPaths, {
7557
7583
  persistent: true,
@@ -7595,7 +7621,7 @@ init_src2();
7595
7621
  init_constants2();
7596
7622
  init_core();
7597
7623
  import { mkdir as mkdir5 } from "fs/promises";
7598
- import { join as join15 } from "path";
7624
+ import { join as join16 } from "path";
7599
7625
  import { Miniflare } from "miniflare";
7600
7626
  async function startDevServer(options) {
7601
7627
  const {
@@ -7609,7 +7635,7 @@ async function startDevServer(options) {
7609
7635
  sourcemap: false,
7610
7636
  minify: false
7611
7637
  });
7612
- const dbDir = join15(getWorkspace(), CLI_DIRECTORIES.DATABASE);
7638
+ const dbDir = join16(getWorkspace(), CLI_DIRECTORIES.DATABASE);
7613
7639
  try {
7614
7640
  await mkdir5(dbDir, { recursive: true });
7615
7641
  } catch (error) {
@@ -8652,8 +8678,8 @@ async function runDbDiff() {
8652
8678
  const deployedGameInfo = await getDeployedGame(workspace);
8653
8679
  const previousSnapshot = deployedGameInfo?.schemaSnapshot;
8654
8680
  const schemaInfo = await getSchemaInfo(previousSnapshot);
8681
+ logger.newLine();
8655
8682
  if (!schemaInfo) {
8656
- logger.newLine();
8657
8683
  logger.success("No schema changes detected");
8658
8684
  logger.newLine();
8659
8685
  logger.remark("Nothing to do");
@@ -8757,13 +8783,13 @@ init_src();
8757
8783
  init_constants2();
8758
8784
  import { spawn } from "child_process";
8759
8785
  import { existsSync as existsSync16, rmSync as rmSync2 } from "fs";
8760
- import { join as join16 } from "path";
8786
+ import { join as join17 } from "path";
8761
8787
  import { confirm as confirm6 } from "@inquirer/prompts";
8762
8788
  import { Miniflare as Miniflare2 } from "miniflare";
8763
8789
  async function runDbReset() {
8764
8790
  try {
8765
8791
  const workspace = getWorkspace();
8766
- const dbDir = join16(workspace, CLI_DIRECTORIES.DATABASE);
8792
+ const dbDir = join17(workspace, CLI_DIRECTORIES.DATABASE);
8767
8793
  if (!existsSync16(dbDir)) {
8768
8794
  logger.warn("No database found to reset");
8769
8795
  logger.newLine();
@@ -8868,7 +8894,7 @@ async function runDbReset() {
8868
8894
  init_package_manager();
8869
8895
  import { execSync as execSync4 } from "child_process";
8870
8896
  import { existsSync as existsSync17 } from "fs";
8871
- import { join as join17 } from "path";
8897
+ import { join as join18 } from "path";
8872
8898
  async function runDbSeed(options) {
8873
8899
  const workspace = getWorkspace();
8874
8900
  try {
@@ -8877,7 +8903,7 @@ async function runDbSeed(options) {
8877
8903
  logger.newLine();
8878
8904
  }
8879
8905
  if (options.file) {
8880
- const seedPath = join17(workspace, options.file);
8906
+ const seedPath = join18(workspace, options.file);
8881
8907
  if (!existsSync17(seedPath)) {
8882
8908
  logger.error(`Seed file not found: ${options.file}`);
8883
8909
  logger.newLine();
@@ -9454,7 +9480,7 @@ import { Command as Command26 } from "commander";
9454
9480
  init_src();
9455
9481
  init_constants2();
9456
9482
  import { writeFileSync as writeFileSync8 } from "fs";
9457
- import { join as join18 } from "path";
9483
+ import { join as join19 } from "path";
9458
9484
  import { Command as Command25 } from "commander";
9459
9485
  var bundleCommand = new Command25("bundle").description("Bundle and inspect the game backend worker code (for debugging)").option("-o, --output <path>", "Output file path", CLI_DEFAULT_OUTPUTS.WORKER_BUNDLE).option("--minify", "Minify the output").option("--sourcemap", "Include source maps").action(async (options) => {
9460
9486
  try {
@@ -9484,7 +9510,7 @@ var bundleCommand = new Command25("bundle").description("Bundle and inspect the
9484
9510
  }),
9485
9511
  (result) => `Bundled ${formatSize(result.code.length)}`
9486
9512
  );
9487
- const outputPath = join18(workspace, options.output);
9513
+ const outputPath = join19(workspace, options.output);
9488
9514
  writeFileSync8(outputPath, bundle.code, "utf-8");
9489
9515
  logger.success(`Bundle saved to ${options.output}`);
9490
9516
  logger.newLine();
@@ -9629,6 +9655,8 @@ export {
9629
9655
  hashContent,
9630
9656
  hashDirectory,
9631
9657
  hashFile,
9658
+ importTypescriptDefault,
9659
+ importTypescriptFile,
9632
9660
  integrationChangeDetectors,
9633
9661
  isDevServerRunning,
9634
9662
  listProfiles,
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { OrganizationConfig, CourseConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
2
2
  import { Miniflare } from 'miniflare';
3
3
  import * as chokidar from 'chokidar';
4
+ import * as esbuild from 'esbuild';
4
5
 
5
6
  /**
6
7
  * @fileoverview Server SDK Type Definitions
@@ -125,4 +126,44 @@ interface HotReloadOptions {
125
126
  */
126
127
  declare function startHotReload(onReload: () => Promise<void>, options?: HotReloadOptions): chokidar.FSWatcher;
127
128
 
128
- export { findConfigPath as findPlaycademyConfigPath, loadConfig as loadPlaycademyConfig, startDevServer as startPlaycademyDevServer, startHotReload as startPlaycademyHotReload, validateConfig as validatePlaycademyConfig };
129
+ /**
130
+ * Import utilities for Node-safe TypeScript file loading
131
+ */
132
+
133
+ /**
134
+ * Import a TypeScript file in a Node-safe way
135
+ *
136
+ * This utility compiles TypeScript files to ESM using esbuild and imports them dynamically.
137
+ * This is necessary because Node.js doesn't support importing .ts files directly.
138
+ *
139
+ * @param filePath - Absolute path to the TypeScript file to import
140
+ * @param bundleOptions - Optional esbuild options to customize the build
141
+ * @returns The imported module
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const schema = await importTypescriptFile('/path/to/schema.ts')
146
+ * const exported = schema.default || schema
147
+ * ```
148
+ */
149
+ declare function importTypescriptFile(filePath: string, bundleOptions?: Partial<esbuild.BuildOptions>): Promise<unknown>;
150
+ /**
151
+ * Import a TypeScript file and extract its default export
152
+ *
153
+ * Convenience wrapper around importTypescriptFile that automatically
154
+ * extracts the default export if available, otherwise returns the module itself.
155
+ *
156
+ * @param filePath - Absolute path to the TypeScript file to import
157
+ * @param bundleOptions - Optional esbuild options to customize the build
158
+ * @returns The default export or the entire module
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const schema = await importTypescriptDefault('/path/to/schema.ts')
163
+ * // If schema.ts has `export default`, returns that
164
+ * // Otherwise returns the entire module object
165
+ * ```
166
+ */
167
+ declare function importTypescriptDefault(filePath: string, bundleOptions?: Partial<esbuild.BuildOptions>): Promise<unknown>;
168
+
169
+ export { findConfigPath as findPlaycademyConfigPath, importTypescriptDefault, importTypescriptFile, loadConfig as loadPlaycademyConfig, startDevServer as startPlaycademyDevServer, startHotReload as startPlaycademyHotReload, validateConfig as validatePlaycademyConfig };
package/dist/utils.js CHANGED
@@ -564,7 +564,7 @@ function processConfigVariables(config) {
564
564
 
565
565
  // src/lib/dev/server.ts
566
566
  import { mkdir as mkdir3 } from "fs/promises";
567
- import { join as join4 } from "path";
567
+ import { join as join5 } from "path";
568
568
  import { Miniflare } from "miniflare";
569
569
 
570
570
  // src/lib/core/client.ts
@@ -816,9 +816,44 @@ import { dirname as dirname3, resolve as resolve3 } from "path";
816
816
  import { fileURLToPath } from "url";
817
817
  var currentDir = dirname3(fileURLToPath(import.meta.url));
818
818
 
819
+ // src/lib/core/import.ts
820
+ import { mkdtempSync, rmSync } from "fs";
821
+ import { tmpdir } from "os";
822
+ import { join } from "path";
823
+ import { pathToFileURL } from "url";
824
+ import * as esbuild from "esbuild";
825
+ async function importTypescriptFile(filePath, bundleOptions) {
826
+ const tempDir = mkdtempSync(join(tmpdir(), "playcademy-import-"));
827
+ const outFile = join(tempDir, "bundle.mjs");
828
+ try {
829
+ await esbuild.build({
830
+ entryPoints: [filePath],
831
+ outfile: outFile,
832
+ bundle: true,
833
+ platform: "node",
834
+ format: "esm",
835
+ target: "node20",
836
+ sourcemap: false,
837
+ minify: false,
838
+ ...bundleOptions
839
+ });
840
+ const module = await import(pathToFileURL(outFile).href);
841
+ return module;
842
+ } finally {
843
+ rmSync(tempDir, { recursive: true, force: true });
844
+ }
845
+ }
846
+ async function importTypescriptDefault(filePath, bundleOptions) {
847
+ const module = await importTypescriptFile(filePath, bundleOptions);
848
+ if (module && typeof module === "object" && "default" in module) {
849
+ return module.default;
850
+ }
851
+ return module;
852
+ }
853
+
819
854
  // src/lib/deploy/bundle.ts
820
855
  import { existsSync as existsSync2 } from "fs";
821
- import { join as join2 } from "path";
856
+ import { join as join3 } from "path";
822
857
 
823
858
  // ../edge-play/src/entry.ts
824
859
  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\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\n\nimport { PlaycademyClient } from '@playcademy/sdk/server'\n\nimport { ENV_VARS } from './constants'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { PlaycademyConfig } from '@playcademy/sdk/server'\nimport type { HonoEnv } from './types'\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: PlaycademyConfig & {\n customRoutes?: Array<{ path: string; file: string }>\n}\n\n// XXX: Polyfill process global for SDK compatibility\n// SDK code may reference process.env without importing it\n// @ts-expect-error - Adding global for Worker environment\nglobalThis.process = {\n env: {}, // Populated per-request from Worker env bindings\n cwd: () => '/',\n}\n\nconst app = new Hono<HonoEnv>()\n\n// TODO: Harden CORS in production - restrict to trusted origins:\n// - Game's assetBundleBase (for hosted games)\n// - Game's externalUrl (for external games)\n// - Platform frontend domains (hub.playcademy.com, hub.dev.playcademy.net)\n// This would require passing game metadata through env bindings during deployment\napp.use(\n '*',\n cors({\n origin: '*', // Permissive for now\n allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n }),\n)\n\nlet sdkPromise: Promise<PlaycademyClient> | null = null\n\napp.use('*', async (c, next) => {\n // Populate process.env from Worker bindings for SDK compatibility\n globalThis.process.env = {\n [ENV_VARS.PLAYCADEMY_API_KEY]: c.env.PLAYCADEMY_API_KEY,\n [ENV_VARS.GAME_ID]: c.env.GAME_ID,\n [ENV_VARS.PLAYCADEMY_BASE_URL]: c.env.PLAYCADEMY_BASE_URL,\n }\n\n // Set config for all routes\n c.set('config', PLAYCADEMY_CONFIG)\n c.set('customRoutes', PLAYCADEMY_CONFIG.customRoutes || [])\n\n await next()\n})\n\n// Initialize SDK lazily on first request\napp.use('*', async (c, next) => {\n if (!sdkPromise) {\n sdkPromise = PlaycademyClient.init({\n apiKey: c.env[ENV_VARS.PLAYCADEMY_API_KEY],\n gameId: c.env[ENV_VARS.GAME_ID],\n baseUrl: c.env[ENV_VARS.PLAYCADEMY_BASE_URL],\n config: PLAYCADEMY_CONFIG,\n })\n }\n\n c.set('sdk', await sdkPromise)\n await next()\n})\n\n/**\n * Register built-in integration routes based on enabled integrations\n *\n * This function conditionally imports and registers routes like:\n * - POST /api/integrations/timeback/end-activity (if timeback enabled)\n * - GET /api/health (always included)\n *\n * Uses dynamic imports for tree-shaking: if an integration is not enabled,\n * its route code is completely removed from the bundle.\n */\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\nexport default app\n";
@@ -1041,8 +1076,8 @@ function getMonorepoRoot() {
1041
1076
  function textLoaderPlugin() {
1042
1077
  return {
1043
1078
  name: "text-loader",
1044
- setup(build) {
1045
- build.onLoad({ filter: /edge-play\/src\/entry\.ts$/ }, async (args) => {
1079
+ setup(build2) {
1080
+ build2.onLoad({ filter: /edge-play\/src\/entry\.ts$/ }, async (args) => {
1046
1081
  const fs2 = await import("fs/promises");
1047
1082
  const text = await fs2.readFile(args.path, "utf8");
1048
1083
  return {
@@ -1050,7 +1085,7 @@ function textLoaderPlugin() {
1050
1085
  loader: "js"
1051
1086
  };
1052
1087
  });
1053
- build.onLoad({ filter: /edge-play\/src\/routes\/root\.html$/ }, async (args) => {
1088
+ build2.onLoad({ filter: /edge-play\/src\/routes\/root\.html$/ }, async (args) => {
1054
1089
  const fs2 = await import("fs/promises");
1055
1090
  const text = await fs2.readFile(args.path, "utf8");
1056
1091
  return {
@@ -1058,7 +1093,7 @@ function textLoaderPlugin() {
1058
1093
  loader: "js"
1059
1094
  };
1060
1095
  });
1061
- build.onLoad({ filter: /templates\/sample-route\.ts$/ }, async (args) => {
1096
+ build2.onLoad({ filter: /templates\/sample-route\.ts$/ }, async (args) => {
1062
1097
  const fs2 = await import("fs/promises");
1063
1098
  const text = await fs2.readFile(args.path, "utf8");
1064
1099
  return {
@@ -1073,8 +1108,8 @@ function textLoaderPlugin() {
1073
1108
  // src/lib/dev/routes.ts
1074
1109
  init_file_loader();
1075
1110
  import { mkdir, writeFile } from "fs/promises";
1076
- import { tmpdir } from "os";
1077
- import { join, relative } from "path";
1111
+ import { tmpdir as tmpdir2 } from "os";
1112
+ import { join as join2, relative } from "path";
1078
1113
 
1079
1114
  // src/lib/deploy/hash.ts
1080
1115
  import { createHash } from "crypto";
@@ -1093,7 +1128,7 @@ async function discoverRoutes(apiDir) {
1093
1128
  const routes = await Promise.all(
1094
1129
  files.map(async (file) => {
1095
1130
  const routePath = filePathToRoutePath(file);
1096
- const absolutePath = join(apiDir, file);
1131
+ const absolutePath = join2(apiDir, file);
1097
1132
  const relativePath = relative(getWorkspace(), absolutePath);
1098
1133
  const methods = await detectExportedMethods(absolutePath);
1099
1134
  return {
@@ -1138,8 +1173,8 @@ async function transpileRoute(filePath) {
1138
1173
  if (isBun() || !filePath.endsWith(".ts")) {
1139
1174
  return filePath;
1140
1175
  }
1141
- const esbuild = await import("esbuild");
1142
- const result = await esbuild.build({
1176
+ const esbuild2 = await import("esbuild");
1177
+ const result = await esbuild2.build({
1143
1178
  entryPoints: [filePath],
1144
1179
  write: false,
1145
1180
  format: "esm",
@@ -1153,10 +1188,10 @@ async function transpileRoute(filePath) {
1153
1188
  if (!result.outputFiles?.[0]) {
1154
1189
  throw new Error("Transpilation failed: no output");
1155
1190
  }
1156
- const tempDir = join(tmpdir(), "playcademy-dev");
1191
+ const tempDir = join2(tmpdir2(), "playcademy-dev");
1157
1192
  await mkdir(tempDir, { recursive: true });
1158
1193
  const hash = hashContent(filePath).slice(0, 12);
1159
- const jsPath = join(tempDir, `${hash}.mjs`);
1194
+ const jsPath = join2(tempDir, `${hash}.mjs`);
1160
1195
  await writeFile(jsPath, result.outputFiles[0].text);
1161
1196
  return jsPath;
1162
1197
  }
@@ -1167,7 +1202,7 @@ async function discoverCustomRoutes(config) {
1167
1202
  const workspace = getWorkspace();
1168
1203
  const customRoutesConfig = config.integrations?.customRoutes;
1169
1204
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
1170
- const customRoutes = await discoverRoutes(join2(workspace, customRoutesDir));
1205
+ const customRoutes = await discoverRoutes(join3(workspace, customRoutesDir));
1171
1206
  const customRouteData = customRoutes.map((r) => ({
1172
1207
  path: r.path,
1173
1208
  file: r.file,
@@ -1179,15 +1214,15 @@ async function discoverCustomRoutes(config) {
1179
1214
  function resolveEmbeddedSourcePaths() {
1180
1215
  const workspace = getWorkspace();
1181
1216
  const distDir = new URL(".", import.meta.url).pathname;
1182
- const embeddedEdgeSrc = join2(distDir, "edge-play", "src");
1217
+ const embeddedEdgeSrc = join3(distDir, "edge-play", "src");
1183
1218
  const isBuiltPackage = existsSync2(embeddedEdgeSrc);
1184
1219
  const monorepoRoot = getMonorepoRoot();
1185
- const monorepoEdgeSrc = join2(monorepoRoot, "packages/edge-play/src");
1220
+ const monorepoEdgeSrc = join3(monorepoRoot, "packages/edge-play/src");
1186
1221
  const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
1187
- const cliPackageRoot = isBuiltPackage ? join2(distDir, "../../..") : join2(monorepoRoot, "packages/cli");
1188
- const cliNodeModules = isBuiltPackage ? join2(cliPackageRoot, "node_modules") : monorepoRoot;
1189
- const workspaceNodeModules = join2(workspace, "node_modules");
1190
- const constantsEntry = isBuiltPackage ? join2(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join2(monorepoRoot, "packages", "constants", "src", "index.ts");
1222
+ const cliPackageRoot = isBuiltPackage ? join3(distDir, "../../..") : join3(monorepoRoot, "packages/cli");
1223
+ const cliNodeModules = isBuiltPackage ? join3(cliPackageRoot, "node_modules") : monorepoRoot;
1224
+ const workspaceNodeModules = join3(workspace, "node_modules");
1225
+ const constantsEntry = isBuiltPackage ? join3(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join3(monorepoRoot, "packages", "constants", "src", "index.ts");
1191
1226
  return {
1192
1227
  isBuiltPackage,
1193
1228
  edgePlaySrc,
@@ -1247,16 +1282,16 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
1247
1282
  // │ Example: import * as route from '@game-api/hello.ts' │
1248
1283
  // │ Resolves to: /user-project/server/api/hello.ts │
1249
1284
  // └─────────────────────────────────────────────────────────────────┘
1250
- "@game-api": join2(workspace, customRoutesDir),
1285
+ "@game-api": join3(workspace, customRoutesDir),
1251
1286
  // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
1252
1287
  // │ Workers don't have fs, path, os, etc. Redirect to polyfills │
1253
1288
  // │ that throw helpful errors if user code tries to use them. │
1254
1289
  // └─────────────────────────────────────────────────────────────────┘
1255
- fs: join2(edgePlaySrc, "polyfills.js"),
1256
- "fs/promises": join2(edgePlaySrc, "polyfills.js"),
1257
- path: join2(edgePlaySrc, "polyfills.js"),
1258
- os: join2(edgePlaySrc, "polyfills.js"),
1259
- process: join2(edgePlaySrc, "polyfills.js")
1290
+ fs: join3(edgePlaySrc, "polyfills.js"),
1291
+ "fs/promises": join3(edgePlaySrc, "polyfills.js"),
1292
+ path: join3(edgePlaySrc, "polyfills.js"),
1293
+ os: join3(edgePlaySrc, "polyfills.js"),
1294
+ process: join3(edgePlaySrc, "polyfills.js")
1260
1295
  },
1261
1296
  // ──── Build Plugins ────
1262
1297
  plugins: [textLoaderPlugin()],
@@ -1267,7 +1302,7 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
1267
1302
  };
1268
1303
  }
1269
1304
  async function bundleBackend(config, options = {}) {
1270
- const esbuild = await import("esbuild");
1305
+ const esbuild2 = await import("esbuild");
1271
1306
  const { customRouteData, customRoutesDir } = await discoverCustomRoutes(config);
1272
1307
  const bundleConfig = {
1273
1308
  ...config,
@@ -1282,7 +1317,7 @@ async function bundleBackend(config, options = {}) {
1282
1317
  customRoutesDir,
1283
1318
  options
1284
1319
  );
1285
- const result = await esbuild.build(buildConfig);
1320
+ const result = await esbuild2.build(buildConfig);
1286
1321
  if (!result.outputFiles?.[0]) {
1287
1322
  throw new Error("Backend bundling failed: no output");
1288
1323
  }
@@ -1320,13 +1355,13 @@ function generateEntryCode(customRoutes, customRoutesDir) {
1320
1355
 
1321
1356
  // src/lib/dev/pid.ts
1322
1357
  import { mkdir as mkdir2, unlink, writeFile as writeFile2 } from "fs/promises";
1323
- import { join as join3 } from "path";
1358
+ import { join as join4 } from "path";
1324
1359
  function getDevServerPidPath() {
1325
- return join3(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
1360
+ return join4(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
1326
1361
  }
1327
1362
  async function createDevServerPidFile() {
1328
1363
  const pidPath = getDevServerPidFile();
1329
- const pidDir = join3(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
1364
+ const pidDir = join4(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
1330
1365
  await mkdir2(pidDir, { recursive: true });
1331
1366
  await writeFile2(pidPath, process.pid.toString());
1332
1367
  }
@@ -1347,7 +1382,7 @@ async function startDevServer(options) {
1347
1382
  sourcemap: false,
1348
1383
  minify: false
1349
1384
  });
1350
- const dbDir = join4(getWorkspace(), CLI_DIRECTORIES.DATABASE);
1385
+ const dbDir = join5(getWorkspace(), CLI_DIRECTORIES.DATABASE);
1351
1386
  try {
1352
1387
  await mkdir3(dbDir, { recursive: true });
1353
1388
  } catch (error) {
@@ -1378,7 +1413,7 @@ async function startDevServer(options) {
1378
1413
  }
1379
1414
 
1380
1415
  // src/lib/dev/reload.ts
1381
- import { join as join5, relative as relative2 } from "path";
1416
+ import { join as join6, relative as relative2 } from "path";
1382
1417
  import chokidar from "chokidar";
1383
1418
  import { bold as bold3, cyan as cyan2, dim as dim3, green as green2 } from "colorette";
1384
1419
  function formatTime() {
@@ -1395,9 +1430,9 @@ function startHotReload(onReload, options = {}) {
1395
1430
  const customRoutesConfig = options.config?.integrations?.customRoutes;
1396
1431
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
1397
1432
  const watchPaths = [
1398
- join5(workspace, customRoutesDir),
1399
- join5(workspace, "playcademy.config.js"),
1400
- join5(workspace, "playcademy.config.json")
1433
+ join6(workspace, customRoutesDir),
1434
+ join6(workspace, "playcademy.config.js"),
1435
+ join6(workspace, "playcademy.config.json")
1401
1436
  ];
1402
1437
  const watcher = chokidar.watch(watchPaths, {
1403
1438
  persistent: true,
@@ -1437,6 +1472,8 @@ function startHotReload(onReload, options = {}) {
1437
1472
  }
1438
1473
  export {
1439
1474
  findConfigPath as findPlaycademyConfigPath,
1475
+ importTypescriptDefault,
1476
+ importTypescriptFile,
1440
1477
  loadConfig as loadPlaycademyConfig,
1441
1478
  startDevServer as startPlaycademyDevServer,
1442
1479
  startHotReload as startPlaycademyHotReload,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playcademy",
3
- "version": "0.12.7",
3
+ "version": "0.12.9",
4
4
  "type": "module",
5
5
  "module": "./dist/index.js",
6
6
  "main": "./dist/index.js",