playcademy 0.18.0 → 0.18.2

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.
Files changed (43) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +24 -12
  3. package/dist/index.js +465 -255
  4. package/dist/utils.js +558 -351
  5. package/dist/version.js +1 -1
  6. package/package.json +1 -1
  7. package/dist/constants/src/achievements.ts +0 -107
  8. package/dist/constants/src/auth.ts +0 -13
  9. package/dist/constants/src/character.ts +0 -16
  10. package/dist/constants/src/domains.ts +0 -50
  11. package/dist/constants/src/env-vars.ts +0 -20
  12. package/dist/constants/src/index.ts +0 -18
  13. package/dist/constants/src/overworld.ts +0 -330
  14. package/dist/constants/src/system.ts +0 -10
  15. package/dist/constants/src/timeback.ts +0 -118
  16. package/dist/constants/src/typescript.ts +0 -21
  17. package/dist/constants/src/workers.ts +0 -36
  18. package/dist/edge-play/src/constants.ts +0 -27
  19. package/dist/edge-play/src/entry/middleware.ts +0 -247
  20. package/dist/edge-play/src/entry/queue.test.ts +0 -279
  21. package/dist/edge-play/src/entry/queue.ts +0 -107
  22. package/dist/edge-play/src/entry/session.ts +0 -45
  23. package/dist/edge-play/src/entry/setup.ts +0 -78
  24. package/dist/edge-play/src/entry/types.ts +0 -30
  25. package/dist/edge-play/src/entry.ts +0 -94
  26. package/dist/edge-play/src/html.d.ts +0 -5
  27. package/dist/edge-play/src/index.ts +0 -4
  28. package/dist/edge-play/src/lib/errors.ts +0 -51
  29. package/dist/edge-play/src/lib/index.ts +0 -3
  30. package/dist/edge-play/src/lib/self-dispatch.test.ts +0 -244
  31. package/dist/edge-play/src/lib/self-dispatch.ts +0 -41
  32. package/dist/edge-play/src/lib/validation.test.ts +0 -190
  33. package/dist/edge-play/src/lib/validation.ts +0 -64
  34. package/dist/edge-play/src/polyfills.js +0 -54
  35. package/dist/edge-play/src/register-routes.ts +0 -59
  36. package/dist/edge-play/src/routes/health.ts +0 -104
  37. package/dist/edge-play/src/routes/index.ts +0 -66
  38. package/dist/edge-play/src/routes/integrations/timeback/end-activity.ts +0 -181
  39. package/dist/edge-play/src/routes/integrations/timeback/get-xp.ts +0 -159
  40. package/dist/edge-play/src/routes/root.html +0 -253
  41. package/dist/edge-play/src/routes/root.ts +0 -22
  42. package/dist/edge-play/src/stub-entry.ts +0 -161
  43. package/dist/edge-play/src/types.ts +0 -124
package/dist/index.js CHANGED
@@ -2176,13 +2176,10 @@ function buildQueueImportStatements(queueHandlers) {
2176
2176
  if (queueHandlers.length === 0) {
2177
2177
  return "// No queue handlers";
2178
2178
  }
2179
- return [
2180
- "import { createQueueRouter, registerQueueIngressRoute } from './entry/queue'",
2181
- ...queueHandlers.map((handler, index2) => {
2182
- const normalizedFile = handler.file.replace(/^server\//, "").replace(/\\/g, "/");
2183
- return `import queueHandler${index2} from '@game-server/${normalizedFile}'`;
2184
- })
2185
- ].join("\n");
2179
+ return queueHandlers.map((handler, index2) => {
2180
+ const normalizedFile = handler.file.replace(/^server\//, "").replace(/\\/g, "/");
2181
+ return `import queueHandler${index2} from '@game-server/${normalizedFile}'`;
2182
+ }).join("\n");
2186
2183
  }
2187
2184
  function buildQueueHandlerCode(queueHandlers) {
2188
2185
  if (queueHandlers.length === 0) {
@@ -4017,7 +4014,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as rea
4017
4014
  import { join as join13 } from "node:path";
4018
4015
 
4019
4016
  // src/version.ts
4020
- var cliVersion = false ? "0.0.0-dev" : "0.18.0";
4017
+ var cliVersion = false ? "0.0.0-dev" : "0.18.2";
4021
4018
 
4022
4019
  // src/lib/init/database.ts
4023
4020
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -6381,8 +6378,8 @@ async function registerCustomRoutes(app, routes) {
6381
6378
  }
6382
6379
 
6383
6380
  // src/lib/dev/server.ts
6384
- import { mkdir as mkdir4 } from "fs/promises";
6385
- import { join as join29 } from "path";
6381
+ import { mkdir as mkdir5 } from "fs/promises";
6382
+ import { join as join30 } from "path";
6386
6383
  import { Log, LogLevel, Miniflare } from "miniflare";
6387
6384
 
6388
6385
  // ../edge-play/src/constants.ts
@@ -6484,7 +6481,12 @@ Stop the other server or specify a different port with --port <number>.`
6484
6481
  }
6485
6482
 
6486
6483
  // src/lib/deploy/bundle.ts
6487
- import { existsSync as existsSync22, readFileSync as readFileSync13 } from "node:fs";
6484
+ import { join as join29, relative as relative4 } from "node:path";
6485
+
6486
+ // src/lib/deploy/backend-runtime.ts
6487
+ import { existsSync as existsSync22 } from "node:fs";
6488
+ import { copyFile, mkdir as mkdir4, readFile as readFile4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
6489
+ import { tmpdir as tmpdir3 } from "node:os";
6488
6490
  import { join as join28 } from "node:path";
6489
6491
 
6490
6492
  // ../utils/src/path.ts
@@ -6740,56 +6742,33 @@ function getMonorepoRoot() {
6740
6742
  var isCompiledBinary = typeof IS_COMPILED_BINARY !== "undefined" && IS_COMPILED_BINARY;
6741
6743
 
6742
6744
  // src/lib/build/plugins.ts
6745
+ var TEXT_LOADER_PATTERNS = [
6746
+ /edge-play\/src\/stub-entry\.ts$/,
6747
+ /edge-play\/src\/routes\/root\.html$/,
6748
+ /templates\/sample-route\.ts$/
6749
+ ];
6743
6750
  function textLoaderPlugin() {
6744
6751
  return {
6745
6752
  name: "text-loader",
6746
6753
  setup(build3) {
6747
- build3.onLoad({ filter: /edge-play\/src\/entry\.ts$/ }, async (args) => {
6748
- const fs2 = await import("fs/promises");
6749
- const text5 = await fs2.readFile(args.path, "utf8");
6750
- return {
6751
- contents: `export default ${JSON.stringify(text5)}`,
6752
- loader: "js"
6753
- };
6754
- });
6755
- build3.onLoad({ filter: /edge-play\/src\/stub-entry\.ts$/ }, async (args) => {
6756
- const fs2 = await import("fs/promises");
6757
- const text5 = await fs2.readFile(args.path, "utf8");
6758
- return {
6759
- contents: `export default ${JSON.stringify(text5)}`,
6760
- loader: "js"
6761
- };
6762
- });
6763
- build3.onLoad({ filter: /edge-play\/src\/routes\/root\.html$/ }, async (args) => {
6764
- const fs2 = await import("fs/promises");
6765
- const text5 = await fs2.readFile(args.path, "utf8");
6766
- return {
6767
- contents: `export default ${JSON.stringify(text5)}`,
6768
- loader: "js"
6769
- };
6770
- });
6771
- build3.onLoad({ filter: /templates\/sample-route\.ts$/ }, async (args) => {
6772
- const fs2 = await import("fs/promises");
6773
- const text5 = await fs2.readFile(args.path, "utf8");
6774
- return {
6775
- contents: `export default ${JSON.stringify(text5)}`,
6776
- loader: "js"
6777
- };
6778
- });
6754
+ for (const filter of TEXT_LOADER_PATTERNS) {
6755
+ build3.onLoad({ filter }, async (args) => {
6756
+ const fs2 = await import("fs/promises");
6757
+ const text5 = await fs2.readFile(args.path, "utf8");
6758
+ return {
6759
+ contents: `export default ${JSON.stringify(text5)}`,
6760
+ loader: "js"
6761
+ };
6762
+ });
6763
+ }
6779
6764
  }
6780
6765
  };
6781
6766
  }
6782
6767
 
6783
- // src/lib/deploy/extract-sources.ts
6768
+ // src/lib/build/embed.ts
6784
6769
  import { existsSync as existsSync21, mkdirSync as mkdirSync8, writeFileSync as writeFileSync13 } from "node:fs";
6785
6770
  import { dirname as dirname6, join as join27 } from "node:path";
6786
-
6787
- // src/lib/deploy/embedded-sources.ts
6788
- var EMBEDDED_SOURCES = null;
6789
-
6790
- // src/lib/deploy/extract-sources.ts
6791
- var EXTRACTION_MARKER = ".embedded-sources.ok";
6792
- function writeTree(baseDir, files) {
6771
+ function writeFileTree(baseDir, files) {
6793
6772
  for (const [relativePath, content] of Object.entries(files)) {
6794
6773
  const fullPath = join27(baseDir, relativePath);
6795
6774
  const dir = dirname6(fullPath);
@@ -6799,27 +6778,225 @@ function writeTree(baseDir, files) {
6799
6778
  writeFileSync13(fullPath, content);
6800
6779
  }
6801
6780
  }
6802
- var cachedPaths = null;
6803
- function extractEmbeddedSources() {
6804
- if (cachedPaths) {
6805
- return cachedPaths;
6781
+ function createBundleExtractor(options) {
6782
+ const { name, cacheSubdir, getBundle } = options;
6783
+ const marker = `.${name}.ok`;
6784
+ let cachedDir = null;
6785
+ return () => {
6786
+ if (cachedDir) {
6787
+ return cachedDir;
6788
+ }
6789
+ const bundle = getBundle();
6790
+ if (!bundle) {
6791
+ throw new Error(`${name}: embedded bundle is null (not available in dev mode)`);
6792
+ }
6793
+ const dir = join27(cacheVersionDir(cliVersion), cacheSubdir);
6794
+ const markerPath = join27(dir, marker);
6795
+ if (!existsSync21(markerPath)) {
6796
+ mkdirSync8(dir, { recursive: true });
6797
+ writeFileTree(dir, bundle);
6798
+ writeFileSync13(markerPath, `${cliVersion}
6799
+ `, "utf8");
6800
+ }
6801
+ cachedDir = dir;
6802
+ return dir;
6803
+ };
6804
+ }
6805
+
6806
+ // src/lib/deploy/embedded-runtime.ts
6807
+ var EMBEDDED_BACKEND_RUNTIME = null;
6808
+
6809
+ // src/lib/deploy/extract-runtime.ts
6810
+ var extractEmbeddedBackendRuntime = createBundleExtractor({
6811
+ name: "embedded-backend-runtime",
6812
+ cacheSubdir: "backend-runtime",
6813
+ getBundle: () => EMBEDDED_BACKEND_RUNTIME
6814
+ });
6815
+
6816
+ // src/lib/deploy/backend-runtime.ts
6817
+ var BACKEND_RUNTIME_ENTRY = "index.js";
6818
+ var BACKEND_RUNTIME_POLYFILLS = "polyfills.js";
6819
+ var BACKEND_RUNTIME_MANIFEST = "manifest.json";
6820
+ var DEV_RUNTIME_CACHE_DIR = "backend-runtime-dev";
6821
+ function createNodePolyfillAliases(polyfillsPath) {
6822
+ return {
6823
+ fs: polyfillsPath,
6824
+ "fs/promises": polyfillsPath,
6825
+ path: polyfillsPath,
6826
+ os: polyfillsPath,
6827
+ process: polyfillsPath,
6828
+ "node:fs": polyfillsPath,
6829
+ "node:fs/promises": polyfillsPath,
6830
+ "node:path": polyfillsPath,
6831
+ "node:os": polyfillsPath,
6832
+ "node:process": polyfillsPath
6833
+ };
6834
+ }
6835
+ var WORKSPACE_SOURCE_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
6836
+ var RUNTIME_SOURCE_DIRS = [
6837
+ "packages/edge-play/src",
6838
+ "packages/sdk/src",
6839
+ "packages/constants/src",
6840
+ "packages/timeback/src",
6841
+ "packages/types/src",
6842
+ "packages/utils/src",
6843
+ "packages/cloudflare/src",
6844
+ "packages/logger/src"
6845
+ ];
6846
+ async function getPackageVersion2(monorepoRoot, packageName) {
6847
+ try {
6848
+ const packageJson = JSON.parse(
6849
+ await readFile4(join28(monorepoRoot, "packages", packageName, "package.json"), "utf8")
6850
+ );
6851
+ return packageJson.version ?? "0.0.0-dev";
6852
+ } catch {
6853
+ return "0.0.0-dev";
6854
+ }
6855
+ }
6856
+ async function computeRuntimeInputFingerprint(monorepoRoot, sdkVersion) {
6857
+ const entries = await Promise.all(
6858
+ RUNTIME_SOURCE_DIRS.map(async (dir) => [
6859
+ dir,
6860
+ await hashDirectory(join28(monorepoRoot, dir), ["ts", "js", "json", "html"])
6861
+ ])
6862
+ );
6863
+ return hashContent({ ...Object.fromEntries(entries), sdkVersion });
6864
+ }
6865
+ async function getRuntimeBuildMetadata(monorepoRoot) {
6866
+ const [cliVersion2, sdkVersion] = await Promise.all([
6867
+ getPackageVersion2(monorepoRoot, "cli"),
6868
+ getPackageVersion2(monorepoRoot, "sdk")
6869
+ ]);
6870
+ const inputFingerprint = await computeRuntimeInputFingerprint(monorepoRoot, sdkVersion);
6871
+ return {
6872
+ cliVersion: cliVersion2,
6873
+ sdkVersion,
6874
+ runtimeBuildId: inputFingerprint.slice(0, 12),
6875
+ inputFingerprint,
6876
+ entry: BACKEND_RUNTIME_ENTRY,
6877
+ polyfills: BACKEND_RUNTIME_POLYFILLS
6878
+ };
6879
+ }
6880
+ function resolveWorkspaceSourceImport(monorepoRoot, specifier) {
6881
+ if (!specifier.startsWith("@playcademy/")) {
6882
+ return null;
6806
6883
  }
6807
- if (!EMBEDDED_SOURCES) {
6808
- throw new Error("extractEmbeddedSources called but EMBEDDED_SOURCES is null (dev mode)");
6884
+ const pathParts = specifier.slice("@playcademy/".length).split("/");
6885
+ const packageName = pathParts.shift();
6886
+ if (!packageName) {
6887
+ return null;
6809
6888
  }
6810
- const cacheRoot = cacheVersionDir(cliVersion);
6811
- const edgePlaySrc = join27(cacheRoot, "edge-play", "src");
6812
- const constantsSrc = join27(cacheRoot, "constants", "src");
6813
- const markerPath = join27(cacheRoot, EXTRACTION_MARKER);
6814
- if (!existsSync21(markerPath)) {
6815
- mkdirSync8(cacheRoot, { recursive: true });
6816
- writeTree(edgePlaySrc, EMBEDDED_SOURCES.edgePlay);
6817
- writeTree(constantsSrc, EMBEDDED_SOURCES.constants);
6818
- writeFileSync13(markerPath, `${cliVersion}
6819
- `, "utf8");
6889
+ const basePath = join28(monorepoRoot, "packages", packageName, "src");
6890
+ const targetPath = pathParts.length > 0 ? join28(basePath, ...pathParts) : join28(basePath, "index");
6891
+ for (const extension of WORKSPACE_SOURCE_EXTENSIONS) {
6892
+ const withExtension = `${targetPath}${extension}`;
6893
+ if (existsSync22(withExtension)) {
6894
+ return withExtension;
6895
+ }
6896
+ }
6897
+ for (const extension of WORKSPACE_SOURCE_EXTENSIONS) {
6898
+ const indexPath = join28(targetPath, `index${extension}`);
6899
+ if (existsSync22(indexPath)) {
6900
+ return indexPath;
6901
+ }
6820
6902
  }
6821
- cachedPaths = { edgePlaySrc, constantsSrc };
6822
- return cachedPaths;
6903
+ return null;
6904
+ }
6905
+ function createWorkspaceSourceResolverPlugin(monorepoRoot) {
6906
+ return {
6907
+ name: "playcademy-workspace-source-resolver",
6908
+ setup(build3) {
6909
+ build3.onResolve({ filter: /^@playcademy\// }, (args) => {
6910
+ const resolved = resolveWorkspaceSourceImport(monorepoRoot, args.path);
6911
+ return resolved ? { path: resolved } : null;
6912
+ });
6913
+ }
6914
+ };
6915
+ }
6916
+ async function writeRuntimeManifest(outdir, manifest) {
6917
+ await writeFile4(
6918
+ join28(outdir, BACKEND_RUNTIME_MANIFEST),
6919
+ `${JSON.stringify(manifest, null, 2)}
6920
+ `,
6921
+ "utf8"
6922
+ );
6923
+ }
6924
+ async function buildBackendRuntime(outdir) {
6925
+ const monorepoRoot = getMonorepoRoot();
6926
+ const manifest = await getRuntimeBuildMetadata(monorepoRoot);
6927
+ const runtimeEntry = join28(monorepoRoot, "packages", "edge-play", "src", "entry", "index.ts");
6928
+ const polyfillsEntry = join28(monorepoRoot, "packages", "edge-play", "src", "polyfills.js");
6929
+ const esbuild4 = await import("esbuild");
6930
+ await rm2(outdir, { recursive: true, force: true });
6931
+ await mkdir4(outdir, { recursive: true });
6932
+ await esbuild4.build({
6933
+ entryPoints: [runtimeEntry],
6934
+ outfile: join28(outdir, BACKEND_RUNTIME_ENTRY),
6935
+ bundle: true,
6936
+ format: "esm",
6937
+ platform: "browser",
6938
+ target: "es2022",
6939
+ write: true,
6940
+ sourcemap: false,
6941
+ minify: false,
6942
+ logLevel: "error",
6943
+ alias: createNodePolyfillAliases(polyfillsEntry),
6944
+ plugins: [textLoaderPlugin(), createWorkspaceSourceResolverPlugin(monorepoRoot)],
6945
+ define: {
6946
+ PLAYCADEMY_RUNTIME_CLI_VERSION: JSON.stringify(manifest.cliVersion),
6947
+ PLAYCADEMY_RUNTIME_SDK_VERSION: JSON.stringify(manifest.sdkVersion),
6948
+ PLAYCADEMY_RUNTIME_BUILD_ID: JSON.stringify(manifest.runtimeBuildId)
6949
+ }
6950
+ });
6951
+ await copyFile(polyfillsEntry, join28(outdir, BACKEND_RUNTIME_POLYFILLS));
6952
+ await writeRuntimeManifest(outdir, manifest);
6953
+ return manifest;
6954
+ }
6955
+ async function ensureDevBackendRuntime() {
6956
+ const monorepoRoot = getMonorepoRoot();
6957
+ const manifest = await getRuntimeBuildMetadata(monorepoRoot);
6958
+ const runtimeDir = join28(
6959
+ tmpdir3(),
6960
+ DEV_RUNTIME_CACHE_DIR,
6961
+ manifest.cliVersion,
6962
+ manifest.inputFingerprint
6963
+ );
6964
+ if (!existsSync22(join28(runtimeDir, BACKEND_RUNTIME_MANIFEST))) {
6965
+ await buildBackendRuntime(runtimeDir);
6966
+ }
6967
+ return runtimeDir;
6968
+ }
6969
+ async function readRuntimeManifest(runtimeDir) {
6970
+ const content = await readFile4(join28(runtimeDir, BACKEND_RUNTIME_MANIFEST), "utf8");
6971
+ return JSON.parse(content);
6972
+ }
6973
+ async function resolveBackendRuntimePaths() {
6974
+ const workspace = getWorkspace();
6975
+ const workspaceNodeModules = join28(workspace, "node_modules");
6976
+ if (isCompiledBinary) {
6977
+ const runtimeDir2 = extractEmbeddedBackendRuntime();
6978
+ const manifest2 = await readRuntimeManifest(runtimeDir2);
6979
+ const cliDepsRoot = join28(workspace, ".playcademy", "node_modules");
6980
+ return {
6981
+ runtimeDir: runtimeDir2,
6982
+ runtimeEntry: join28(runtimeDir2, manifest2.entry),
6983
+ polyfillsEntry: join28(runtimeDir2, manifest2.polyfills),
6984
+ workspaceNodeModules,
6985
+ cliDepsRoot,
6986
+ manifest: manifest2
6987
+ };
6988
+ }
6989
+ const monorepoRoot = getMonorepoRoot();
6990
+ const runtimeDir = await ensureDevBackendRuntime();
6991
+ const manifest = await readRuntimeManifest(runtimeDir);
6992
+ return {
6993
+ runtimeDir,
6994
+ runtimeEntry: join28(runtimeDir, manifest.entry),
6995
+ polyfillsEntry: join28(runtimeDir, manifest.polyfills),
6996
+ workspaceNodeModules,
6997
+ cliDepsRoot: monorepoRoot,
6998
+ manifest
6999
+ };
6823
7000
  }
6824
7001
 
6825
7002
  // src/lib/deploy/bundle.ts
@@ -6827,153 +7004,134 @@ async function discoverCustomRoutes(config) {
6827
7004
  const workspace = getWorkspace();
6828
7005
  const customRoutesConfig = config.integrations?.customRoutes;
6829
7006
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
6830
- const customRoutes = await discoverRoutes(join28(workspace, customRoutesDir));
7007
+ const customRoutes = await discoverRoutes(join29(workspace, customRoutesDir));
6831
7008
  const customRouteData = customRoutes.map((r) => ({
6832
7009
  path: r.path,
6833
7010
  file: r.file,
6834
- // Use relative path (e.g., 'server/api/test.ts'), not absolute
7011
+ // relative path (e.g., 'server/api/test.ts'), not absolute
6835
7012
  methods: r.methods
6836
7013
  }));
6837
7014
  return { customRouteData, customRoutesDir };
6838
7015
  }
6839
- function resolveEmbeddedSourcePaths() {
6840
- const workspace = getWorkspace();
6841
- if (isCompiledBinary) {
6842
- const { edgePlaySrc: edgePlaySrc2, constantsSrc } = extractEmbeddedSources();
6843
- const constantsEntry2 = join28(constantsSrc, "index.ts");
6844
- const workspaceNodeModules2 = join28(workspace, "node_modules");
6845
- const playcademyNodeModules = join28(workspace, ".playcademy", "node_modules");
6846
- return {
6847
- isBuiltPackage: true,
6848
- edgePlaySrc: edgePlaySrc2,
6849
- constantsEntry: constantsEntry2,
6850
- workspaceNodeModules: workspaceNodeModules2,
6851
- cliDepsRoot: playcademyNodeModules
6852
- };
7016
+ function getForbiddenImportMessage(specifier) {
7017
+ if (specifier === "@playcademy/sdk/server") {
7018
+ return "Use `c.get('sdk')` instead of importing the server SDK directly.";
6853
7019
  }
6854
- const distDir = new URL(".", import.meta.url).pathname;
6855
- const embeddedEdgeSrc = join28(distDir, "edge-play", "src");
6856
- const isBuiltPackage = existsSync22(embeddedEdgeSrc);
6857
- const monorepoRoot = getMonorepoRoot();
6858
- const monorepoEdgeSrc = join28(monorepoRoot, "packages/edge-play/src");
6859
- const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
6860
- const cliPackageRoot = isBuiltPackage ? join28(distDir, "../../..") : join28(monorepoRoot, "packages/cli");
6861
- const cliDepsRoot = isBuiltPackage ? join28(cliPackageRoot, "node_modules") : monorepoRoot;
6862
- const workspaceNodeModules = join28(workspace, "node_modules");
6863
- const constantsEntry = isBuiltPackage ? join28(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join28(monorepoRoot, "packages", "constants", "src", "index.ts");
7020
+ if (specifier.startsWith("@playcademy/edge-play")) {
7021
+ return "Built-in backend runtime helpers are internal to the CLI-owned runtime.";
7022
+ }
7023
+ return "CLI-owned backend runtime packages are not available to game backend code.";
7024
+ }
7025
+ function createForbiddenRuntimeImportsPlugin(workspace) {
6864
7026
  return {
6865
- isBuiltPackage,
6866
- edgePlaySrc,
6867
- constantsEntry,
6868
- workspaceNodeModules,
6869
- cliDepsRoot
7027
+ name: "forbidden-backend-runtime-imports",
7028
+ setup(build3) {
7029
+ build3.onResolve(
7030
+ {
7031
+ filter: /^@playcademy\/(?:sdk\/server|edge-play(?:\/.*)?|constants(?:\/.*)?)$/
7032
+ },
7033
+ (args) => {
7034
+ if (!args.importer) {
7035
+ return null;
7036
+ }
7037
+ const importerPath = relative4(workspace, args.importer).replace(/\\/g, "/");
7038
+ return {
7039
+ errors: [
7040
+ {
7041
+ text: `Unsupported backend import "${args.path}" in ${importerPath}. ${getForbiddenImportMessage(args.path)} Use \`Context\`, \`c.env\`, \`c.get('sdk')\`, and \`c.get('playcademyUser')\` instead.`
7042
+ }
7043
+ ]
7044
+ };
7045
+ }
7046
+ );
7047
+ }
6870
7048
  };
6871
7049
  }
6872
7050
  function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, options) {
6873
7051
  const workspace = getWorkspace();
6874
- const { edgePlaySrc, constantsEntry, workspaceNodeModules, cliDepsRoot } = paths;
7052
+ const { runtimeEntry, polyfillsEntry, workspaceNodeModules, cliDepsRoot } = paths;
6875
7053
  return {
6876
- // ──── Input Configuration ────
7054
+ // ──── Input: generated entry code, not a real file ────
7055
+ // resolveDir = workspace so that bare specifiers in the generated code
7056
+ // (e.g. '@game-api/save.ts') resolve relative to the user's project root.
6877
7057
  stdin: {
6878
7058
  contents: entryCode,
6879
- // Generated entry code with custom route imports
6880
- resolveDir: edgePlaySrc,
6881
- // Resolve relative imports from edge-play/src
7059
+ resolveDir: workspace,
6882
7060
  loader: "ts"
6883
- // Treat input as TypeScript
6884
7061
  },
6885
- // ──── Output Configuration ────
7062
+ // ──── Output configuration ────
6886
7063
  bundle: true,
6887
- // Bundle all dependencies into single file
6888
7064
  format: "esm",
6889
- // Output ES modules (required for Cloudflare Workers)
7065
+ // Cloudflare Workers require ES modules
6890
7066
  platform: "browser",
6891
- // Workers use browser APIs, not Node.js
7067
+ // Workers run in a browser-like environment, not Node.js
6892
7068
  target: "es2022",
6893
- // Modern JavaScript for Workers runtime
6894
7069
  write: false,
6895
- // Return code as string (don't write to disk)
7070
+ // Return code as a string; the caller writes/uploads it
6896
7071
  sourcemap: options.sourcemap ? "inline" : false,
6897
7072
  minify: options.minify || false,
6898
- logLevel: "error",
6899
- // Only show errors (suppress warnings)
6900
- // ──── Module Resolution ────
6901
- // Tell esbuild where to find node_modules for bare imports
6902
- // Dev: workspace node_modules + monorepo root (hoisted deps)
6903
- // Published: workspace node_modules + same (user's project)
6904
- // Binary: workspace node_modules + .playcademy/node_modules (hono lives there)
7073
+ logLevel: "silent",
7074
+ // errors propagate as thrown exceptions; we don't want esbuild double-printing to stderr
7075
+ // ──── Module resolution ────
7076
+ // esbuild searches nodePaths in order for bare specifiers.
7077
+ // workspaceNodeModules: game project's own deps (hono, etc. if user installed them)
7078
+ // cliDepsRoot: CLI-managed deps (.playcademy/node_modules in binary mode,
7079
+ // monorepo root in dev mode) — where hono, @hono/node-server live
6905
7080
  nodePaths: [workspaceNodeModules, cliDepsRoot],
6906
- // ──── Build-time Constants ────
6907
- // Inject the Playcademy config as a global constant
6908
- // Code can access it via: const config = PLAYCADEMY_CONFIG
7081
+ // ──── Build-time constants ────
7082
+ // PLAYCADEMY_CONFIG is accessed as a global in the generated entry code and in
7083
+ // the sealed runtime itself (passed to createBaseApp). It carries integrations
7084
+ // config, route metadata, queue metadata, etc.
6909
7085
  define: {
6910
7086
  PLAYCADEMY_CONFIG: JSON.stringify(bundleConfig)
6911
7087
  },
6912
- // ──── Import Aliases ────
6913
7088
  alias: {
6914
- // ┌─ Workspace-only package resolution ─────────────────────────────┐
6915
- // │ @playcademy/constants is a workspace package that users don't
6916
- // │ install. We embed it in dist/ and alias imports to point there.
6917
- // └─────────────────────────────────────────────────────────────────┘
6918
- "@playcademy/constants": constantsEntry,
6919
- // ┌─ Workspace-only edge-play package ──────────────────────────────┐
6920
- // @playcademy/edge-play is used in generated entry code for auth │
6921
- // │ middleware. It's embedded in dist/ like constants.
6922
- // └─────────────────────────────────────────────────────────────────┘
6923
- "@playcademy/edge-play": edgePlaySrc,
6924
- // ┌─ User's custom routes ──────────────────────────────────────────┐
6925
- // │ @game-api is a virtual module that maps to the user's API dir. │
6926
- // Example: import * as route from '@game-api/hello.ts' │
6927
- // │ Resolves to: /user-project/server/api/hello.ts
6928
- // └─────────────────────────────────────────────────────────────────┘
6929
- "@game-api": join28(workspace, customRoutesDir),
6930
- // ┌─ User's server lib directory ───────────────────────────────────┐
6931
- // │ @game-server is a virtual module for server utilities/config │
6932
- // Example: import { getAuth } from '@game-server/lib/auth' │
6933
- // │ Resolves to: /user-project/server/lib/auth.ts
6934
- // └─────────────────────────────────────────────────────────────────┘
6935
- "@game-server": join28(workspace, "server"),
6936
- // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
6937
- // │ Workers don't have fs, path, os, etc. Redirect to polyfills │
6938
- // │ that throw helpful errors if user code tries to use them. │
6939
- // └─────────────────────────────────────────────────────────────────┘
6940
- fs: join28(edgePlaySrc, "polyfills.js"),
6941
- "fs/promises": join28(edgePlaySrc, "polyfills.js"),
6942
- path: join28(edgePlaySrc, "polyfills.js"),
6943
- os: join28(edgePlaySrc, "polyfills.js"),
6944
- process: join28(edgePlaySrc, "polyfills.js")
7089
+ // ┌─ Sealed backend runtime ────────────────────────────────────────────┐
7090
+ // │ The pre-bundled artifact: edge-play + sdk/server + all internal
7091
+ // │ @playcademy/* deps, zero external imports remaining. This alias
7092
+ // │ is what enforces version isolation — game node_modules are ignored. │
7093
+ // └─────────────────────────────────────────────────────────────────────┘
7094
+ "@playcademy/backend-runtime": runtimeEntry,
7095
+ // ┌─ Game's custom route files ─────────────────────────────────────────┐
7096
+ // │ @game-api is a virtual module pointing to the game's API directory.
7097
+ // │ Example: import * as route from '@game-api/save.ts' │
7098
+ // │ Resolves to: /user-project/server/api/save.ts │
7099
+ // └─────────────────────────────────────────────────────────────────────┘
7100
+ "@game-api": join29(workspace, customRoutesDir),
7101
+ // ┌─ Game's server library directory ───────────────────────────────────┐
7102
+ // │ @game-server is a virtual module for server-side utilities.
7103
+ // │ Example: import { getAuth } from '@game-server/lib/auth' │
7104
+ // │ Resolves to: /user-project/server/lib/auth.ts │
7105
+ // └─────────────────────────────────────────────────────────────────────┘
7106
+ "@game-server": join29(workspace, "server"),
7107
+ // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────────┐
7108
+ // │ Workers don't have fs, path, os, process, etc. Redirect them to a
7109
+ // │ polyfills stub bundled into the sealed runtime that throws a clear │
7110
+ // error if user code actually calls them at runtime. │
7111
+ // └─────────────────────────────────────────────────────────────────────┘
7112
+ ...createNodePolyfillAliases(polyfillsEntry)
6945
7113
  },
6946
- // ──── Build Plugins ────
6947
- plugins: [textLoaderPlugin()],
6948
- // Support Bun's 'with { type: "text" }' imports
6949
- // ──── External Dependencies ────
7114
+ plugins: [createForbiddenRuntimeImportsPlugin(workspace)],
6950
7115
  external: []
6951
- // Bundle everything (no externals for Workers)
7116
+ // Bundle everything no externals allowed in Workers
6952
7117
  };
6953
7118
  }
6954
- function loadEntryTemplate(paths) {
6955
- const entryPath = paths ? join28(paths.edgePlaySrc, "entry.ts") : join28(getMonorepoRoot(), "packages", "edge-play", "src", "entry.ts");
6956
- return readFileSync13(entryPath, "utf8");
6957
- }
6958
7119
  async function bundleBackend(config, options = {}) {
6959
7120
  const esbuild4 = await import("esbuild");
6960
7121
  const { customRouteData, customRoutesDir } = await discoverCustomRoutes(config);
6961
7122
  const queueHandlers = discoverQueueHandlers();
6962
7123
  validateQueueHandlersConfig(config, queueHandlers);
6963
- const paths = resolveEmbeddedSourcePaths();
6964
- const entryTemplate = loadEntryTemplate(paths);
7124
+ const paths = await resolveBackendRuntimePaths();
6965
7125
  const bundleConfig = {
6966
7126
  ...config,
6967
7127
  __routeMetadata: customRouteData,
6968
7128
  __queueMetadata: queueHandlers
6969
7129
  };
6970
- const hasAuth = Boolean(config.integrations?.auth);
6971
7130
  const entryCode = generateEntryCode(
6972
7131
  customRouteData,
6973
7132
  customRoutesDir,
6974
7133
  queueHandlers,
6975
- hasAuth,
6976
- entryTemplate
7134
+ Boolean(config.integrations?.auth)
6977
7135
  );
6978
7136
  const buildConfig = createEsbuildConfig(
6979
7137
  entryCode,
@@ -6986,9 +7144,8 @@ async function bundleBackend(config, options = {}) {
6986
7144
  if (!result.outputFiles?.[0]) {
6987
7145
  throw new Error("Backend bundling failed: no output");
6988
7146
  }
6989
- const code = result.outputFiles[0].text;
6990
7147
  return {
6991
- code,
7148
+ code: result.outputFiles[0].text,
6992
7149
  config,
6993
7150
  customRoutes: customRouteData
6994
7151
  };
@@ -6996,50 +7153,69 @@ async function bundleBackend(config, options = {}) {
6996
7153
  function buildCustomRouteImportStatements(customRoutes, customRoutesDir) {
6997
7154
  const normalizedRoutesDir = customRoutesDir.replace(/\\/g, "/").replace(/\/+/g, "/");
6998
7155
  const customRoutesPrefix = normalizedRoutesDir.endsWith("/") ? normalizedRoutesDir : `${normalizedRoutesDir}/`;
6999
- return customRoutes.map((route, i) => {
7156
+ return customRoutes.map((route, index2) => {
7000
7157
  const normalizedFile = route.file.replace(/\\/g, "/").replace(/\/+/g, "/");
7001
7158
  const importPath = normalizedFile.startsWith(customRoutesPrefix) ? normalizedFile.slice(customRoutesPrefix.length) : normalizedFile;
7002
- return `import * as customRoute${i} from '@game-api/${importPath}'`;
7159
+ return `import * as customRoute${index2} from '@game-api/${importPath}'`;
7003
7160
  }).join("\n");
7004
7161
  }
7005
- function generateEntryCode(customRoutes, customRoutesDir, queueHandlers, hasAuth, entryTemplateInput = loadEntryTemplate()) {
7006
- const importStatements = customRoutes.length > 0 ? buildCustomRouteImportStatements(customRoutes, customRoutesDir) : "// No custom routes";
7007
- const queueImportStatements = buildQueueImportStatements(queueHandlers);
7008
- const withCustomRouteImports = entryTemplateInput.replace(
7009
- "// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTE_IMPORTS \u26A0\uFE0F",
7010
- importStatements
7011
- );
7012
- const withImports = withCustomRouteImports.replace(
7013
- "// \u26A0\uFE0F BUILD_MARKER: QUEUE_HANDLER_IMPORTS \u26A0\uFE0F",
7014
- queueImportStatements
7015
- );
7016
- const registrationStatements = customRoutes.length > 0 ? customRoutes.flatMap((route, i) => {
7017
- const methods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
7018
- return methods.map((method) => {
7019
- const methodLower = method.toLowerCase();
7020
- return `if (customRoute${i}.${method}) app.${methodLower}('${route.path}', customRoute${i}.${method})`;
7021
- });
7022
- }) : [];
7023
- const registrationCode = registrationStatements.length > 0 ? ["// Custom routes", ...registrationStatements, ""].join("\n") : "// No custom routes\n";
7024
- const withRoutes = withImports.replace("// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTES \u26A0\uFE0F", registrationCode);
7025
- const queueHandlerCode = buildQueueHandlerCode(queueHandlers);
7026
- const withQueueHandlers = withRoutes.replace(
7027
- "// \u26A0\uFE0F BUILD_MARKER: QUEUE_HANDLER \u26A0\uFE0F",
7028
- queueHandlerCode
7029
- );
7030
- const exportCode = buildWorkerExportCode(queueHandlers);
7031
- const withExport = withQueueHandlers.replace("// \u26A0\uFE0F BUILD_MARKER: WORKER_EXPORT \u26A0\uFE0F", exportCode);
7032
- const sessionMiddlewareStatements = hasAuth ? [
7033
- "import { createSessionMiddleware } from '@playcademy/edge-play'",
7034
- "import { getAuth } from '@game-server/lib/auth'",
7035
- "app.use('*', createSessionMiddleware(getAuth))"
7036
- ] : [];
7037
- const sessionMiddlewareCode = sessionMiddlewareStatements.join("\n");
7038
- const result = withExport.replace(
7039
- "// \u26A0\uFE0F BUILD_MARKER: SESSION_MIDDLEWARE \u26A0\uFE0F",
7040
- sessionMiddlewareCode
7162
+ function buildCustomRouteModuleCode(customRoutes) {
7163
+ if (customRoutes.length === 0) {
7164
+ return "const __customRoutes = []";
7165
+ }
7166
+ return [
7167
+ "const __customRoutes = [",
7168
+ ...customRoutes.map(
7169
+ (route, index2) => ` { path: ${JSON.stringify(route.path)}, module: customRoute${index2} },`
7170
+ ),
7171
+ "]"
7172
+ ].join("\n");
7173
+ }
7174
+ function generateEntryCode(customRoutes, customRoutesDir, queueHandlers, hasAuth = false) {
7175
+ const runtimeImports = [
7176
+ "createBaseApp",
7177
+ "registerBuiltinRoutes",
7178
+ "registerCustomRouteModules",
7179
+ "registerTerminalHandlers"
7180
+ ];
7181
+ if (hasAuth) {
7182
+ runtimeImports.push("createSessionMiddleware");
7183
+ }
7184
+ if (queueHandlers.length > 0) {
7185
+ runtimeImports.push("createQueueRouter", "registerQueueIngressRoute");
7186
+ }
7187
+ const importBlocks = [
7188
+ `import { ${runtimeImports.join(", ")} } from '@playcademy/backend-runtime'`
7189
+ ];
7190
+ if (hasAuth) {
7191
+ importBlocks.push("import { getAuth } from '@game-server/lib/auth'");
7192
+ }
7193
+ if (customRoutes.length > 0) {
7194
+ importBlocks.push(buildCustomRouteImportStatements(customRoutes, customRoutesDir));
7195
+ }
7196
+ if (queueHandlers.length > 0) {
7197
+ importBlocks.push(buildQueueImportStatements(queueHandlers));
7198
+ }
7199
+ const body = [
7200
+ "// PLAYCADEMY_CONFIG is injected at bundle time via esbuild define",
7201
+ "const app = createBaseApp(PLAYCADEMY_CONFIG)",
7202
+ ""
7203
+ ];
7204
+ if (hasAuth) {
7205
+ body.push("app.use('*', createSessionMiddleware(getAuth))", "");
7206
+ }
7207
+ body.push(
7208
+ "await registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)",
7209
+ "",
7210
+ buildCustomRouteModuleCode(customRoutes),
7211
+ "registerCustomRouteModules(app, __customRoutes)",
7212
+ "",
7213
+ buildQueueHandlerCode(queueHandlers),
7214
+ "registerTerminalHandlers(app)",
7215
+ "",
7216
+ buildWorkerExportCode(queueHandlers)
7041
7217
  );
7042
- return result;
7218
+ return [...importBlocks, "", ...body].join("\n");
7043
7219
  }
7044
7220
 
7045
7221
  // src/lib/dev/server.ts
@@ -7159,9 +7335,9 @@ async function startDevServer(options) {
7159
7335
  return { server: mf, port };
7160
7336
  }
7161
7337
  async function ensureDatabaseDirectory() {
7162
- const dbDir = join29(getWorkspace(), CLI_DIRECTORIES.DATABASE);
7338
+ const dbDir = join30(getWorkspace(), CLI_DIRECTORIES.DATABASE);
7163
7339
  try {
7164
- await mkdir4(dbDir, { recursive: true });
7340
+ await mkdir5(dbDir, { recursive: true });
7165
7341
  } catch (error) {
7166
7342
  throw new Error(`Failed to create database directory: ${getErrorMessage(error)}`, {
7167
7343
  cause: error
@@ -7170,9 +7346,9 @@ async function ensureDatabaseDirectory() {
7170
7346
  return dbDir;
7171
7347
  }
7172
7348
  async function ensureKvDirectory() {
7173
- const kvDir = join29(getWorkspace(), CLI_DIRECTORIES.KV);
7349
+ const kvDir = join30(getWorkspace(), CLI_DIRECTORIES.KV);
7174
7350
  try {
7175
- await mkdir4(kvDir, { recursive: true });
7351
+ await mkdir5(kvDir, { recursive: true });
7176
7352
  } catch (error) {
7177
7353
  throw new Error(`Failed to create KV directory: ${getErrorMessage(error)}`, {
7178
7354
  cause: error
@@ -7181,9 +7357,9 @@ async function ensureKvDirectory() {
7181
7357
  return kvDir;
7182
7358
  }
7183
7359
  async function ensureBucketDirectory() {
7184
- const bucketDir = join29(getWorkspace(), CLI_DIRECTORIES.BUCKET);
7360
+ const bucketDir = join30(getWorkspace(), CLI_DIRECTORIES.BUCKET);
7185
7361
  try {
7186
- await mkdir4(bucketDir, { recursive: true });
7362
+ await mkdir5(bucketDir, { recursive: true });
7187
7363
  } catch (error) {
7188
7364
  throw new Error(`Failed to create bucket directory: ${getErrorMessage(error)}`, {
7189
7365
  cause: error
@@ -7554,13 +7730,13 @@ import { confirm as confirm6, input as input3, select as select5 } from "@inquir
7554
7730
 
7555
7731
  // src/lib/deploy/build-discovery.ts
7556
7732
  init_file_loader();
7557
- import { join as join30, relative as relative4 } from "path";
7733
+ import { join as join31, relative as relative5 } from "path";
7558
7734
  function findSingleBuildZip() {
7559
7735
  const workspace = getWorkspace();
7560
- const playcademyDir = join30(workspace, CLI_DIRECTORIES.WORKSPACE);
7736
+ const playcademyDir = join31(workspace, CLI_DIRECTORIES.WORKSPACE);
7561
7737
  const zipFiles = findFilesByExtension(playcademyDir, "zip");
7562
7738
  if (zipFiles.length === 1) {
7563
- return zipFiles[0] ? relative4(workspace, zipFiles[0]) : null;
7739
+ return zipFiles[0] ? relative5(workspace, zipFiles[0]) : null;
7564
7740
  }
7565
7741
  return null;
7566
7742
  }
@@ -7568,12 +7744,12 @@ function findSingleBuildZip() {
7568
7744
  // src/lib/deploy/schema.ts
7569
7745
  import { existsSync as existsSync24 } from "fs";
7570
7746
  import { createRequire } from "module";
7571
- import { join as join32 } from "path";
7747
+ import { join as join33 } from "path";
7572
7748
 
7573
7749
  // src/lib/deploy/migrations.ts
7574
7750
  import { existsSync as existsSync23 } from "fs";
7575
- import { readFile as readFile4 } from "fs/promises";
7576
- import { join as join31 } from "path";
7751
+ import { readFile as readFile5 } from "fs/promises";
7752
+ import { join as join32 } from "path";
7577
7753
  import { bold as bold8 } from "colorette";
7578
7754
 
7579
7755
  // src/lib/deploy/utils.ts
@@ -7595,25 +7771,25 @@ function getDeploymentId(gameSlug) {
7595
7771
  }
7596
7772
 
7597
7773
  // src/lib/deploy/migrations.ts
7598
- var DB_RELATIVE_CANDIDATES = ["migrations", join31("..", "migrations"), "drizzle"];
7599
- var ROOT_RELATIVE_CANDIDATES = [join31("db", "migrations"), "drizzle"];
7774
+ var DB_RELATIVE_CANDIDATES = ["migrations", join32("..", "migrations"), "drizzle"];
7775
+ var ROOT_RELATIVE_CANDIDATES = [join32("db", "migrations"), "drizzle"];
7600
7776
  var META_DIR = "meta";
7601
7777
  var JOURNAL_FILE = "_journal.json";
7602
7778
  var STATEMENT_BREAKPOINT_RE = /--> statement-breakpoint/g;
7603
7779
  async function getMigrationsDirectory() {
7604
7780
  const workspace = getWorkspace();
7605
7781
  const dbDirectory = await getDatabaseDirectory();
7606
- const dbDirAbs = join31(workspace, dbDirectory);
7782
+ const dbDirAbs = join32(workspace, dbDirectory);
7607
7783
  for (const candidate of DB_RELATIVE_CANDIDATES) {
7608
- const candidateAbs = join31(dbDirAbs, candidate);
7609
- const journalPath = join31(candidateAbs, META_DIR, JOURNAL_FILE);
7784
+ const candidateAbs = join32(dbDirAbs, candidate);
7785
+ const journalPath = join32(candidateAbs, META_DIR, JOURNAL_FILE);
7610
7786
  if (existsSync23(journalPath)) {
7611
7787
  return candidateAbs;
7612
7788
  }
7613
7789
  }
7614
7790
  for (const candidate of ROOT_RELATIVE_CANDIDATES) {
7615
- const candidateAbs = join31(workspace, candidate);
7616
- const journalPath = join31(candidateAbs, META_DIR, JOURNAL_FILE);
7791
+ const candidateAbs = join32(workspace, candidate);
7792
+ const journalPath = join32(candidateAbs, META_DIR, JOURNAL_FILE);
7617
7793
  if (existsSync23(journalPath)) {
7618
7794
  return candidateAbs;
7619
7795
  }
@@ -7643,9 +7819,9 @@ async function readMigrationJournal() {
7643
7819
  if (!migrationsDir) {
7644
7820
  return null;
7645
7821
  }
7646
- const journalPath = join31(migrationsDir, META_DIR, JOURNAL_FILE);
7822
+ const journalPath = join32(migrationsDir, META_DIR, JOURNAL_FILE);
7647
7823
  try {
7648
- const raw = await readFile4(journalPath, "utf8");
7824
+ const raw = await readFile5(journalPath, "utf8");
7649
7825
  const journal = JSON.parse(raw);
7650
7826
  if (!Array.isArray(journal.entries)) {
7651
7827
  logger.warn("Migration journal has no entries array");
@@ -7689,14 +7865,14 @@ async function getUnappliedMigrations(lastAppliedTag) {
7689
7865
  }
7690
7866
  const results = [];
7691
7867
  for (const entry of unapplied) {
7692
- const sqlPath = join31(migrationsDir, `${entry.tag}.sql`);
7868
+ const sqlPath = join32(migrationsDir, `${entry.tag}.sql`);
7693
7869
  if (!existsSync23(sqlPath)) {
7694
7870
  throw new Error(
7695
7871
  `Migration file not found: ${sqlPath}
7696
7872
  The journal references migration "${entry.tag}" but the .sql file is missing.`
7697
7873
  );
7698
7874
  }
7699
- const raw = await readFile4(sqlPath, "utf8");
7875
+ const raw = await readFile5(sqlPath, "utf8");
7700
7876
  const sql2 = raw.replace(STATEMENT_BREAKPOINT_RE, "").trim();
7701
7877
  results.push({ tag: entry.tag, sql: sql2 });
7702
7878
  }
@@ -7800,7 +7976,7 @@ async function loadAndValidateMigrations(workspace) {
7800
7976
  // src/lib/deploy/schema.ts
7801
7977
  function getDrizzleKitApiExports() {
7802
7978
  try {
7803
- const projectRequire = createRequire(join32(process.cwd(), "package.json"));
7979
+ const projectRequire = createRequire(join33(process.cwd(), "package.json"));
7804
7980
  return projectRequire("drizzle-kit/api");
7805
7981
  } catch {
7806
7982
  const cliRequire = createRequire(import.meta.url);
@@ -7810,7 +7986,7 @@ function getDrizzleKitApiExports() {
7810
7986
  async function getPushSchemaInfo(previousSchemaSnapshot) {
7811
7987
  const workspace = getWorkspace();
7812
7988
  const dbDirectory = await getDatabaseDirectory();
7813
- const schemaPath = join32(workspace, dbDirectory, SCHEMA_SUBDIRECTORY, DB_FILES.SCHEMA_INDEX);
7989
+ const schemaPath = join33(workspace, dbDirectory, SCHEMA_SUBDIRECTORY, DB_FILES.SCHEMA_INDEX);
7814
7990
  if (!existsSync24(schemaPath)) {
7815
7991
  return null;
7816
7992
  }
@@ -8293,7 +8469,7 @@ function logDeploymentPlanDebug(plan, context2) {
8293
8469
 
8294
8470
  // src/lib/deploy/preparation.ts
8295
8471
  import { stat } from "node:fs/promises";
8296
- import { join as join33 } from "node:path";
8472
+ import { join as join34 } from "node:path";
8297
8473
 
8298
8474
  // ../data/src/domains/game/helpers.ts
8299
8475
  function isHostedGame(game) {
@@ -8906,8 +9082,8 @@ async function bundleAndHash(context2) {
8906
9082
  const bundle = await bundleBackend(context2.fullConfig);
8907
9083
  const bundleHash = hashContent(bundle.code);
8908
9084
  const [serverSize, dbSize] = await Promise.all([
8909
- getDirectorySize(join33(context2.projectPath, SERVER_ROOT_DIRECTORY)),
8910
- getDirectorySize(join33(context2.projectPath, DEFAULT_DATABASE_DIRECTORY))
9085
+ getDirectorySize(join34(context2.projectPath, SERVER_ROOT_DIRECTORY)),
9086
+ getDirectorySize(join34(context2.projectPath, DEFAULT_DATABASE_DIRECTORY))
8911
9087
  ]);
8912
9088
  context2.currentBackendBundle = bundle;
8913
9089
  context2.currentBackendBundleHash = bundleHash;
@@ -9082,8 +9258,8 @@ function hasOptionalFieldsMissing(missing) {
9082
9258
 
9083
9259
  // src/lib/deploy/steps.ts
9084
9260
  import { existsSync as existsSync26 } from "fs";
9085
- import { readFile as readFile5 } from "fs/promises";
9086
- import { basename as basename2, join as join34, resolve as resolve11 } from "path";
9261
+ import { readFile as readFile6 } from "fs/promises";
9262
+ import { basename as basename2, join as join35, resolve as resolve11 } from "path";
9087
9263
 
9088
9264
  // src/lib/deploy/stub.ts
9089
9265
  import * as esbuild3 from "esbuild";
@@ -9129,7 +9305,7 @@ function prepareGameMetadata(config) {
9129
9305
  async function prepareBuildFile(buildPath) {
9130
9306
  const resolvedPath = resolve11(buildPath);
9131
9307
  if (resolvedPath.endsWith(".zip") && existsSync26(resolvedPath)) {
9132
- const buffer = await readFile5(resolvedPath);
9308
+ const buffer = await readFile6(resolvedPath);
9133
9309
  const uint8Array = new Uint8Array(buffer);
9134
9310
  const blob = new Blob([uint8Array], { type: "application/zip" });
9135
9311
  return new File([blob], basename2(resolvedPath), { type: "application/zip" });
@@ -9305,8 +9481,8 @@ async function deployGame(context2, shouldUploadBuild, shouldDeployBackend) {
9305
9481
  schema: deploymentPrep.schemaInfo || void 0
9306
9482
  };
9307
9483
  const [serverSize, dbSize] = await Promise.all([
9308
- getDirectorySize(join34(context2.projectPath, SERVER_ROOT_DIRECTORY)),
9309
- getDirectorySize(join34(context2.projectPath, DEFAULT_DATABASE_DIRECTORY))
9484
+ getDirectorySize(join35(context2.projectPath, SERVER_ROOT_DIRECTORY)),
9485
+ getDirectorySize(join35(context2.projectPath, DEFAULT_DATABASE_DIRECTORY))
9310
9486
  ]);
9311
9487
  const isMigrateMode = Boolean(deploymentPrep.schemaInfo?.migrationTag);
9312
9488
  backendMetadata = {
@@ -9368,7 +9544,7 @@ async function deployGame(context2, shouldUploadBuild, shouldDeployBackend) {
9368
9544
 
9369
9545
  // src/lib/db/path.ts
9370
9546
  import { copyFileSync, existsSync as existsSync27, mkdirSync as mkdirSync9, readdirSync as readdirSync4, unlinkSync } from "fs";
9371
- import { join as join35 } from "path";
9547
+ import { join as join36 } from "path";
9372
9548
  var DB_DIRECTORY = CLI_DIRECTORIES.DATABASE;
9373
9549
  var INITIAL_DB_NAME = CLI_FILES.INITIAL_DATABASE;
9374
9550
  function ensureDirectoryExists(dir) {
@@ -9377,7 +9553,7 @@ function ensureDirectoryExists(dir) {
9377
9553
  }
9378
9554
  }
9379
9555
  function findMiniflareDatabase(dbDir) {
9380
- const miniflareDir = join35(dbDir, MINIFLARE_D1_DIRECTORY);
9556
+ const miniflareDir = join36(dbDir, MINIFLARE_D1_DIRECTORY);
9381
9557
  if (!existsSync27(miniflareDir)) {
9382
9558
  return null;
9383
9559
  }
@@ -9385,7 +9561,7 @@ function findMiniflareDatabase(dbDir) {
9385
9561
  if (sqliteFiles.length === 0) {
9386
9562
  return null;
9387
9563
  }
9388
- return join35(miniflareDir, sqliteFiles[0]);
9564
+ return join36(miniflareDir, sqliteFiles[0]);
9389
9565
  }
9390
9566
  function migrateInitialDbToTarget(initialPath, targetPath) {
9391
9567
  if (!existsSync27(initialPath)) {
@@ -9395,7 +9571,7 @@ function migrateInitialDbToTarget(initialPath, targetPath) {
9395
9571
  unlinkSync(initialPath);
9396
9572
  }
9397
9573
  function getDevDbPath() {
9398
- const initialDbPath = join35(DB_DIRECTORY, INITIAL_DB_NAME);
9574
+ const initialDbPath = join36(DB_DIRECTORY, INITIAL_DB_NAME);
9399
9575
  ensureDirectoryExists(DB_DIRECTORY);
9400
9576
  const miniflareDbPath = findMiniflareDatabase(DB_DIRECTORY);
9401
9577
  if (miniflareDbPath) {
@@ -9634,10 +9810,10 @@ async function bundleSeedWorker(seedFilePath, projectPath) {
9634
9810
  // src/lib/db/reset.ts
9635
9811
  import { execSync as execSync7 } from "child_process";
9636
9812
  import { rmSync as rmSync3 } from "fs";
9637
- import { join as join36 } from "path";
9813
+ import { join as join37 } from "path";
9638
9814
  async function resetDatabase(workspace, mf, options = { debug: false }) {
9639
9815
  const { debug } = options;
9640
- const dbDir = join36(workspace, CLI_DIRECTORIES.DATABASE);
9816
+ const dbDir = join37(workspace, CLI_DIRECTORIES.DATABASE);
9641
9817
  await runStep(
9642
9818
  "Resetting database...",
9643
9819
  async () => {
@@ -10080,14 +10256,14 @@ function getCallbackUrl() {
10080
10256
  }
10081
10257
 
10082
10258
  // src/lib/bucket/bulk.ts
10083
- import { readFileSync as readFileSync14 } from "fs";
10259
+ import { readFileSync as readFileSync13 } from "fs";
10084
10260
  import { dim as dim9 } from "colorette";
10085
10261
 
10086
10262
  // src/lib/bucket/collect.ts
10087
10263
  import { readdirSync as readdirSync5, statSync as statSync3 } from "fs";
10088
- import { join as join37, relative as relative5, sep as sep2 } from "path";
10264
+ import { join as join38, relative as relative6, sep as sep2 } from "path";
10089
10265
  function shouldSkipFile(filePath, baseDir, gitignorePatterns) {
10090
- const relativePath = relative5(baseDir, filePath);
10266
+ const relativePath = relative6(baseDir, filePath);
10091
10267
  const pathParts = relativePath.split(sep2);
10092
10268
  const alwaysSkip = BUCKET_ALWAYS_SKIP;
10093
10269
  for (const part of pathParts) {
@@ -10102,7 +10278,7 @@ function collectFiles(directory, baseDir, gitignorePatterns) {
10102
10278
  try {
10103
10279
  const entries = readdirSync5(directory, { withFileTypes: true });
10104
10280
  for (const entry of entries) {
10105
- const fullPath = join37(directory, entry.name);
10281
+ const fullPath = join38(directory, entry.name);
10106
10282
  if (!shouldSkipFile(fullPath, baseDir, gitignorePatterns)) {
10107
10283
  if (entry.isDirectory()) {
10108
10284
  files.push(...collectFiles(fullPath, baseDir, gitignorePatterns));
@@ -10110,7 +10286,7 @@ function collectFiles(directory, baseDir, gitignorePatterns) {
10110
10286
  const stats = statSync3(fullPath);
10111
10287
  files.push({
10112
10288
  absolutePath: fullPath,
10113
- relativePath: relative5(baseDir, fullPath),
10289
+ relativePath: relative6(baseDir, fullPath),
10114
10290
  size: stats.size
10115
10291
  });
10116
10292
  }
@@ -10208,7 +10384,7 @@ async function uploadFilesRemote(files, gameSlug, prefix, uploadFn) {
10208
10384
  `Uploading ${files.length} files ${dim9(`[${formatBytes(totalSize)}]`)}`,
10209
10385
  async () => {
10210
10386
  for (const file of files) {
10211
- const fileBuffer = readFileSync14(file.absolutePath);
10387
+ const fileBuffer = readFileSync13(file.absolutePath);
10212
10388
  const contentType = getContentType(file.absolutePath);
10213
10389
  const key = getBucketKey(file.relativePath, prefix);
10214
10390
  await uploadFn(gameSlug, key, fileBuffer, contentType);
@@ -10224,7 +10400,7 @@ async function uploadFilesLocal(files, prefix, uploadFn) {
10224
10400
  `Uploading ${files.length} files ${dim9(`[${formatBytes(totalSize)}]`)}`,
10225
10401
  async () => {
10226
10402
  for (const file of files) {
10227
- const fileBuffer = readFileSync14(file.absolutePath);
10403
+ const fileBuffer = readFileSync13(file.absolutePath);
10228
10404
  const contentType = getContentType(file.absolutePath);
10229
10405
  const key = getBucketKey(file.relativePath, prefix);
10230
10406
  await uploadFn(key, new Uint8Array(fileBuffer).buffer, contentType);
@@ -11019,6 +11195,39 @@ function normalizeVersion(version) {
11019
11195
  }
11020
11196
  return trimmed.replace(/^[vV]/, "");
11021
11197
  }
11198
+ function parseVersion(v) {
11199
+ const [version, ...pre] = v.split("-");
11200
+ const parts = version.split(".").map(Number);
11201
+ if (parts.some((n) => Number.isNaN(n))) {
11202
+ return null;
11203
+ }
11204
+ return { parts, prerelease: pre.length > 0 ? pre.join("-") : null };
11205
+ }
11206
+ function compareVersions(a, b) {
11207
+ const va = parseVersion(a);
11208
+ const vb = parseVersion(b);
11209
+ if (!va || !vb) {
11210
+ return 0;
11211
+ }
11212
+ const maxLen = Math.max(va.parts.length, vb.parts.length);
11213
+ for (let i = 0; i < maxLen; i++) {
11214
+ const pa = va.parts[i] ?? 0;
11215
+ const pb = vb.parts[i] ?? 0;
11216
+ if (pa > pb) {
11217
+ return 1;
11218
+ }
11219
+ if (pa < pb) {
11220
+ return -1;
11221
+ }
11222
+ }
11223
+ if (!va.prerelease && vb.prerelease) {
11224
+ return 1;
11225
+ }
11226
+ if (va.prerelease && !vb.prerelease) {
11227
+ return -1;
11228
+ }
11229
+ return 0;
11230
+ }
11022
11231
  async function fetchLatestVersion() {
11023
11232
  const response = await fetch(LATEST_VERSION_URL);
11024
11233
  if (!response.ok) {
@@ -11055,6 +11264,7 @@ export {
11055
11264
  collectBulkFiles,
11056
11265
  collectFiles,
11057
11266
  compareIntegrationKeys,
11267
+ compareVersions,
11058
11268
  computeSecretsDiff,
11059
11269
  computeSecretsHashes,
11060
11270
  confirmDeploymentPlan,