playcademy 0.13.22 → 0.14.0

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/index.js CHANGED
@@ -2130,10 +2130,43 @@ var init_config = __esm({
2130
2130
  }
2131
2131
  });
2132
2132
 
2133
+ // src/constants/bucket.ts
2134
+ var BUCKET_ALWAYS_SKIP;
2135
+ var init_bucket = __esm({
2136
+ "src/constants/bucket.ts"() {
2137
+ "use strict";
2138
+ init_config();
2139
+ BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
2140
+ }
2141
+ });
2142
+
2143
+ // src/constants/cloudflare.ts
2144
+ var CLOUDFLARE_COMPATIBILITY_DATE, CLOUDFLARE_BINDINGS, MINIFLARE_D1_DIRECTORY;
2145
+ var init_cloudflare = __esm({
2146
+ "src/constants/cloudflare.ts"() {
2147
+ "use strict";
2148
+ CLOUDFLARE_COMPATIBILITY_DATE = "2024-01-01";
2149
+ CLOUDFLARE_BINDINGS = {
2150
+ /** R2 bucket binding name */
2151
+ BUCKET: "BUCKET",
2152
+ /** KV namespace binding name */
2153
+ KV: "KV",
2154
+ /** D1 database binding name */
2155
+ DB: "DB"
2156
+ };
2157
+ MINIFLARE_D1_DIRECTORY = "miniflare-D1DatabaseObject";
2158
+ }
2159
+ });
2160
+
2133
2161
  // src/constants/database.ts
2162
+ var DEFAULT_DATABASE_DIRECTORY, SCHEMA_SUBDIRECTORY, SCHEMA_INDEX_FILE, DEFAULT_SEED_FILE_NAME;
2134
2163
  var init_database = __esm({
2135
2164
  "src/constants/database.ts"() {
2136
2165
  "use strict";
2166
+ DEFAULT_DATABASE_DIRECTORY = "db";
2167
+ SCHEMA_SUBDIRECTORY = "schema";
2168
+ SCHEMA_INDEX_FILE = "index.ts";
2169
+ DEFAULT_SEED_FILE_NAME = "seed.ts";
2137
2170
  }
2138
2171
  });
2139
2172
 
@@ -2337,11 +2370,12 @@ var init_urls = __esm({
2337
2370
  });
2338
2371
 
2339
2372
  // src/constants/index.ts
2340
- var CLOUDFLARE_COMPATIBILITY_DATE;
2341
2373
  var init_constants = __esm({
2342
2374
  "src/constants/index.ts"() {
2343
2375
  "use strict";
2344
2376
  init_api();
2377
+ init_bucket();
2378
+ init_cloudflare();
2345
2379
  init_config();
2346
2380
  init_database();
2347
2381
  init_http_server();
@@ -2349,7 +2383,6 @@ var init_constants = __esm({
2349
2383
  init_ports();
2350
2384
  init_timeback();
2351
2385
  init_urls();
2352
- CLOUDFLARE_COMPATIBILITY_DATE = "2024-01-01";
2353
2386
  }
2354
2387
  });
2355
2388
 
@@ -2800,6 +2833,68 @@ var init_storage = __esm({
2800
2833
  }
2801
2834
  });
2802
2835
 
2836
+ // src/lib/core/client.ts
2837
+ import { PlaycademyClient } from "@playcademy/sdk";
2838
+ async function createClient() {
2839
+ const profile = await getCurrentProfile();
2840
+ const baseUrl = getBaseUrl();
2841
+ const client = new PlaycademyClient({
2842
+ baseUrl,
2843
+ token: profile?.token
2844
+ });
2845
+ if (profile?.token && profile?.tokenType) {
2846
+ client.setToken(profile.token, profile.tokenType);
2847
+ }
2848
+ return client;
2849
+ }
2850
+ async function requireAuthenticatedClient() {
2851
+ const profile = await getCurrentProfile();
2852
+ const environment = getEnvironment();
2853
+ if (!profile) {
2854
+ logger.newLine();
2855
+ logger.admonition("warning", "Login Required", [
2856
+ `You are not logged into ${environment}.`,
2857
+ environment === "production" ? `Run \`playcademy login --env ${environment}\` to authenticate.` : "Run `playcademy login` to authenticate."
2858
+ ]);
2859
+ logger.newLine();
2860
+ process.exit(1);
2861
+ }
2862
+ const client = await createClient();
2863
+ return client;
2864
+ }
2865
+ var init_client = __esm({
2866
+ "src/lib/core/client.ts"() {
2867
+ "use strict";
2868
+ init_storage();
2869
+ init_logger();
2870
+ init_config2();
2871
+ }
2872
+ });
2873
+
2874
+ // src/lib/core/errors.ts
2875
+ function getErrorMessage(error) {
2876
+ if (error instanceof Error) return error.message;
2877
+ if (typeof error === "string") return error;
2878
+ if (error && typeof error === "object" && "message" in error) {
2879
+ return String(error.message);
2880
+ }
2881
+ return "Unknown error";
2882
+ }
2883
+ function logAndExit(error, logger2, options = {}) {
2884
+ const { code = 1, prefix } = options;
2885
+ const message = getErrorMessage(error);
2886
+ const fullMessage = prefix ? `${prefix}: ${message}` : message;
2887
+ logger2.newLine();
2888
+ logger2.error(fullMessage);
2889
+ logger2.newLine();
2890
+ process.exit(code);
2891
+ }
2892
+ var init_errors = __esm({
2893
+ "src/lib/core/errors.ts"() {
2894
+ "use strict";
2895
+ }
2896
+ });
2897
+
2803
2898
  // src/lib/config/loader.ts
2804
2899
  import { dirname as dirname2, resolve as resolve3 } from "path";
2805
2900
  async function findConfigPath(configPath) {
@@ -3135,68 +3230,6 @@ var init_constants3 = __esm({
3135
3230
  }
3136
3231
  });
3137
3232
 
3138
- // src/lib/core/game.ts
3139
- function getSlugFromConfig(config) {
3140
- return generateSlug(config.name);
3141
- }
3142
- async function ensureGameExists(client, config) {
3143
- const slug = getSlugFromConfig(config);
3144
- let game = await runStep(
3145
- `Checking for game "${slug}"`,
3146
- async () => {
3147
- try {
3148
- return await client.games.fetch(slug);
3149
- } catch {
3150
- return null;
3151
- }
3152
- },
3153
- (result) => result ? "Found existing game" : "Game not found"
3154
- );
3155
- if (!game) {
3156
- game = await runStep(
3157
- `Creating game metadata for "${slug}"`,
3158
- () => client.dev.games.upsert(slug, {
3159
- displayName: config.name,
3160
- platform: config.platform ?? "web",
3161
- gameType: config.gameType ?? "hosted",
3162
- metadata: {
3163
- ...config.description && { description: config.description },
3164
- ...config.emoji && { emoji: config.emoji }
3165
- },
3166
- ...config.gameType === "external" && config.externalUrl && {
3167
- externalUrl: config.externalUrl
3168
- }
3169
- }),
3170
- "Game metadata created"
3171
- );
3172
- }
3173
- return game;
3174
- }
3175
- async function getGameFromConfig(client) {
3176
- const config = await runStep("Loading configuration", loadConfig, "Configuration loaded");
3177
- const slug = getSlugFromConfig(config);
3178
- const game = await runStep(
3179
- `Finding game "${slug}"`,
3180
- async () => {
3181
- try {
3182
- return await client.games.fetch(slug);
3183
- } catch {
3184
- throw new Error(`Game "${slug}" not found`);
3185
- }
3186
- },
3187
- "Game found"
3188
- );
3189
- return { game, config };
3190
- }
3191
- var init_game = __esm({
3192
- "src/lib/core/game.ts"() {
3193
- "use strict";
3194
- init_src();
3195
- init_slug();
3196
- init_config3();
3197
- }
3198
- });
3199
-
3200
3233
  // src/lib/config/timeback-derive.ts
3201
3234
  function generateCourseCode(slug) {
3202
3235
  return slug.split("-").slice(0, 2).map((word) => word.substring(0, 3).toUpperCase()).join("-");
@@ -3622,81 +3655,131 @@ var init_config3 = __esm({
3622
3655
  }
3623
3656
  });
3624
3657
 
3625
- // src/lib/core/client.ts
3626
- import { PlaycademyClient } from "@playcademy/sdk";
3627
- async function createClient() {
3628
- const profile = await getCurrentProfile();
3629
- const baseUrl = getBaseUrl();
3630
- const client = new PlaycademyClient({
3631
- baseUrl,
3632
- token: profile?.token
3633
- });
3634
- if (profile?.token && profile?.tokenType) {
3635
- client.setToken(profile.token, profile.tokenType);
3636
- }
3637
- return client;
3658
+ // src/lib/core/game.ts
3659
+ function getSlugFromConfig(config) {
3660
+ return generateSlug(config.name);
3638
3661
  }
3639
- async function requireAuthenticatedClient() {
3640
- const profile = await getCurrentProfile();
3641
- const environment = getEnvironment();
3642
- if (!profile) {
3643
- logger.newLine();
3644
- logger.admonition("warning", "Login Required", [
3645
- `You are not logged into ${environment}.`,
3646
- environment === "production" ? `Run \`playcademy login --env ${environment}\` to authenticate.` : "Run `playcademy login` to authenticate."
3647
- ]);
3648
- logger.newLine();
3649
- process.exit(1);
3662
+ async function ensureGameExists(client, config) {
3663
+ const slug = getSlugFromConfig(config);
3664
+ let game = await runStep(
3665
+ `Checking for game "${slug}"`,
3666
+ async () => {
3667
+ try {
3668
+ return await client.games.fetch(slug);
3669
+ } catch {
3670
+ return null;
3671
+ }
3672
+ },
3673
+ (result) => result ? "Found existing game" : "Game not found"
3674
+ );
3675
+ if (!game) {
3676
+ game = await runStep(
3677
+ `Creating game metadata for "${slug}"`,
3678
+ () => client.dev.games.upsert(slug, {
3679
+ displayName: config.name,
3680
+ platform: config.platform ?? "web",
3681
+ gameType: config.gameType ?? "hosted",
3682
+ metadata: {
3683
+ ...config.description && { description: config.description },
3684
+ ...config.emoji && { emoji: config.emoji }
3685
+ },
3686
+ ...config.gameType === "external" && config.externalUrl && {
3687
+ externalUrl: config.externalUrl
3688
+ }
3689
+ }),
3690
+ "Game metadata created"
3691
+ );
3650
3692
  }
3651
- const client = await createClient();
3652
- return client;
3693
+ return game;
3653
3694
  }
3654
- var init_client = __esm({
3655
- "src/lib/core/client.ts"() {
3695
+ async function getGameFromConfig(client) {
3696
+ const config = await runStep("Loading configuration", loadConfig, "Configuration loaded");
3697
+ const slug = getSlugFromConfig(config);
3698
+ const game = await runStep(
3699
+ `Finding game "${slug}"`,
3700
+ async () => {
3701
+ try {
3702
+ return await client.games.fetch(slug);
3703
+ } catch {
3704
+ throw new Error(`Game "${slug}" not found`);
3705
+ }
3706
+ },
3707
+ "Game found"
3708
+ );
3709
+ return { game, config };
3710
+ }
3711
+ var init_game = __esm({
3712
+ "src/lib/core/game.ts"() {
3656
3713
  "use strict";
3657
- init_storage();
3658
- init_logger();
3659
- init_config2();
3714
+ init_src();
3715
+ init_slug();
3716
+ init_config3();
3660
3717
  }
3661
3718
  });
3662
3719
 
3663
- // src/lib/core/errors.ts
3664
- function getErrorMessage(error) {
3665
- if (error instanceof Error) return error.message;
3666
- if (typeof error === "string") return error;
3667
- if (error && typeof error === "object" && "message" in error) {
3668
- return String(error.message);
3669
- }
3670
- return "Unknown error";
3671
- }
3672
- function logAndExit(error, logger2, options = {}) {
3673
- const { code = 1, prefix } = options;
3674
- const message = getErrorMessage(error);
3675
- const fullMessage = prefix ? `${prefix}: ${message}` : message;
3676
- logger2.newLine();
3677
- logger2.error(fullMessage);
3678
- logger2.newLine();
3679
- process.exit(code);
3720
+ // src/lib/core/gitignore.ts
3721
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
3722
+ import { join as join4, sep } from "path";
3723
+ function normalizeGitignoreEntry(entry) {
3724
+ return entry.replace(/^\/+/, "").replace(/\/+$/, "");
3680
3725
  }
3681
- var init_errors = __esm({
3682
- "src/lib/core/errors.ts"() {
3683
- "use strict";
3726
+ function loadGitignorePatterns(directory) {
3727
+ const gitignorePath = join4(directory, ".gitignore");
3728
+ if (!existsSync5(gitignorePath)) {
3729
+ return [];
3684
3730
  }
3685
- });
3686
-
3687
- // src/lib/core/import.ts
3688
- import { mkdtempSync, rmSync } from "fs";
3689
- import { tmpdir } from "os";
3690
- import { join as join4 } from "path";
3691
- import { pathToFileURL } from "url";
3692
- import * as esbuild from "esbuild";
3693
- async function importTypescriptFile(filePath, bundleOptions) {
3694
- const tempDir = mkdtempSync(join4(tmpdir(), "playcademy-import-"));
3695
- const outFile = join4(tempDir, "bundle.mjs");
3696
3731
  try {
3697
- await esbuild.build({
3698
- entryPoints: [filePath],
3699
- outfile: outFile,
3732
+ const content = readFileSync3(gitignorePath, "utf-8");
3733
+ return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
3734
+ } catch {
3735
+ return [];
3736
+ }
3737
+ }
3738
+ function matchesGitignorePattern(filePath, pattern) {
3739
+ if (pattern.endsWith("/")) {
3740
+ const dirPattern = pattern.slice(0, -1);
3741
+ if (filePath.includes(dirPattern + sep) || filePath === dirPattern) {
3742
+ return true;
3743
+ }
3744
+ }
3745
+ if (pattern.includes("*")) {
3746
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
3747
+ if (regex.test(filePath)) {
3748
+ return true;
3749
+ }
3750
+ }
3751
+ if (filePath === pattern || filePath.includes(sep + pattern) || filePath.endsWith(pattern)) {
3752
+ return true;
3753
+ }
3754
+ return false;
3755
+ }
3756
+ function isIgnoredByGitignore(filePath, patterns) {
3757
+ for (const pattern of patterns) {
3758
+ if (matchesGitignorePattern(filePath, pattern)) {
3759
+ return true;
3760
+ }
3761
+ }
3762
+ return false;
3763
+ }
3764
+ var init_gitignore = __esm({
3765
+ "src/lib/core/gitignore.ts"() {
3766
+ "use strict";
3767
+ }
3768
+ });
3769
+
3770
+ // src/lib/core/import.ts
3771
+ import { mkdtempSync, rmSync } from "fs";
3772
+ import { tmpdir } from "os";
3773
+ import { join as join5 } from "path";
3774
+ import { pathToFileURL } from "url";
3775
+ import * as esbuild from "esbuild";
3776
+ async function importTypescriptFile(filePath, bundleOptions) {
3777
+ const tempDir = mkdtempSync(join5(tmpdir(), "playcademy-import-"));
3778
+ const outFile = join5(tempDir, "bundle.mjs");
3779
+ try {
3780
+ await esbuild.build({
3781
+ entryPoints: [filePath],
3782
+ outfile: outFile,
3700
3783
  bundle: true,
3701
3784
  platform: "node",
3702
3785
  format: "esm",
@@ -3724,6 +3807,57 @@ var init_import = __esm({
3724
3807
  }
3725
3808
  });
3726
3809
 
3810
+ // src/lib/core/mime.ts
3811
+ function getContentType(filePath) {
3812
+ const ext = filePath.split(".").pop()?.toLowerCase();
3813
+ const types = {
3814
+ // Images
3815
+ jpg: "image/jpeg",
3816
+ jpeg: "image/jpeg",
3817
+ png: "image/png",
3818
+ gif: "image/gif",
3819
+ webp: "image/webp",
3820
+ svg: "image/svg+xml",
3821
+ ico: "image/x-icon",
3822
+ // Audio
3823
+ mp3: "audio/mpeg",
3824
+ wav: "audio/wav",
3825
+ ogg: "audio/ogg",
3826
+ m4a: "audio/mp4",
3827
+ // Video
3828
+ mp4: "video/mp4",
3829
+ webm: "video/webm",
3830
+ mov: "video/quicktime",
3831
+ // Documents
3832
+ pdf: "application/pdf",
3833
+ txt: "text/plain",
3834
+ md: "text/markdown",
3835
+ // Web
3836
+ html: "text/html",
3837
+ htm: "text/html",
3838
+ css: "text/css",
3839
+ js: "application/javascript",
3840
+ mjs: "application/javascript",
3841
+ json: "application/json",
3842
+ xml: "application/xml",
3843
+ // Archives
3844
+ zip: "application/zip",
3845
+ gz: "application/gzip",
3846
+ tar: "application/x-tar",
3847
+ // Fonts
3848
+ woff: "font/woff",
3849
+ woff2: "font/woff2",
3850
+ ttf: "font/ttf",
3851
+ otf: "font/otf"
3852
+ };
3853
+ return types[ext || ""] || "application/octet-stream";
3854
+ }
3855
+ var init_mime2 = __esm({
3856
+ "src/lib/core/mime.ts"() {
3857
+ "use strict";
3858
+ }
3859
+ });
3860
+
3727
3861
  // src/lib/core/index.ts
3728
3862
  var core_exports = {};
3729
3863
  __export(core_exports, {
@@ -3733,6 +3867,7 @@ __export(core_exports, {
3733
3867
  getApiUrl: () => getApiUrl,
3734
3868
  getBaseUrl: () => getBaseUrl,
3735
3869
  getCliContext: () => getCliContext,
3870
+ getContentType: () => getContentType,
3736
3871
  getEnvironment: () => getEnvironment,
3737
3872
  getErrorMessage: () => getErrorMessage,
3738
3873
  getGameFromConfig: () => getGameFromConfig,
@@ -3744,9 +3879,13 @@ __export(core_exports, {
3744
3879
  hasPackageJson: () => hasPackageJson,
3745
3880
  importTypescriptDefault: () => importTypescriptDefault,
3746
3881
  importTypescriptFile: () => importTypescriptFile,
3882
+ isIgnoredByGitignore: () => isIgnoredByGitignore,
3883
+ loadGitignorePatterns: () => loadGitignorePatterns,
3747
3884
  logAndExit: () => logAndExit,
3748
3885
  logger: () => logger,
3886
+ matchesGitignorePattern: () => matchesGitignorePattern,
3749
3887
  normalizeEnvironment: () => normalizeEnvironment,
3888
+ normalizeGitignoreEntry: () => normalizeGitignoreEntry,
3750
3889
  requireAuthenticatedClient: () => requireAuthenticatedClient,
3751
3890
  setCliContext: () => setCliContext
3752
3891
  });
@@ -3758,8 +3897,10 @@ var init_core = __esm({
3758
3897
  init_context();
3759
3898
  init_errors();
3760
3899
  init_game();
3900
+ init_gitignore();
3761
3901
  init_import();
3762
3902
  init_logger();
3903
+ init_mime2();
3763
3904
  }
3764
3905
  });
3765
3906
 
@@ -3790,7 +3931,7 @@ import { program } from "commander";
3790
3931
 
3791
3932
  // src/commands/init/index.ts
3792
3933
  import { execSync as execSync4 } from "child_process";
3793
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync9 } from "fs";
3934
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync9 } from "fs";
3794
3935
  import { resolve as resolve10 } from "path";
3795
3936
 
3796
3937
  // ../../node_modules/@inquirer/core/dist/esm/lib/errors.js
@@ -4586,19 +4727,166 @@ function getCallbackUrl() {
4586
4727
  return `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
4587
4728
  }
4588
4729
 
4730
+ // src/lib/bucket/bulk.ts
4731
+ init_src();
4732
+ init_core();
4733
+ import { readFileSync as readFileSync4 } from "fs";
4734
+ import { dim as dim3 } from "colorette";
4735
+
4736
+ // src/lib/bucket/collect.ts
4737
+ init_constants2();
4738
+ init_core();
4739
+ init_core();
4740
+ import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
4741
+ import { join as join6, relative, sep as sep2 } from "path";
4742
+ function shouldSkipFile(filePath, baseDir, gitignorePatterns) {
4743
+ const relativePath = relative(baseDir, filePath);
4744
+ const pathParts = relativePath.split(sep2);
4745
+ const alwaysSkip = BUCKET_ALWAYS_SKIP;
4746
+ for (const part of pathParts) {
4747
+ if (alwaysSkip.includes(part)) return true;
4748
+ }
4749
+ return isIgnoredByGitignore(relativePath, gitignorePatterns);
4750
+ }
4751
+ function collectFiles(directory, baseDir, gitignorePatterns) {
4752
+ const files = [];
4753
+ try {
4754
+ const entries = readdirSync2(directory, { withFileTypes: true });
4755
+ for (const entry of entries) {
4756
+ const fullPath = join6(directory, entry.name);
4757
+ if (shouldSkipFile(fullPath, baseDir, gitignorePatterns)) {
4758
+ continue;
4759
+ }
4760
+ if (entry.isDirectory()) {
4761
+ files.push(...collectFiles(fullPath, baseDir, gitignorePatterns));
4762
+ } else if (entry.isFile()) {
4763
+ const stats = statSync2(fullPath);
4764
+ files.push({
4765
+ absolutePath: fullPath,
4766
+ relativePath: relative(baseDir, fullPath),
4767
+ size: stats.size
4768
+ });
4769
+ }
4770
+ }
4771
+ } catch {
4772
+ }
4773
+ return files;
4774
+ }
4775
+ function formatBytes(bytes) {
4776
+ if (bytes === 0) return "0 B";
4777
+ const k = 1024;
4778
+ const sizes = ["B", "KB", "MB", "GB"];
4779
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
4780
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
4781
+ }
4782
+ function getBucketKey(relativePath, prefix) {
4783
+ const key = relativePath.replace(/\\/g, "/");
4784
+ return prefix ? `${prefix}/${key}` : key;
4785
+ }
4786
+
4787
+ // src/lib/bucket/bulk.ts
4788
+ function collectBulkFiles(directory) {
4789
+ const gitignorePatterns = loadGitignorePatterns(directory);
4790
+ const files = collectFiles(directory, directory, gitignorePatterns);
4791
+ const totalSize = files.reduce((sum, file) => sum + file.size, 0);
4792
+ return { files, totalSize };
4793
+ }
4794
+ function outputDryRunResults(files, totalSize, prefix, json, raw) {
4795
+ if (json) {
4796
+ logger.json({
4797
+ files: files.map((f) => ({
4798
+ path: f.relativePath,
4799
+ key: getBucketKey(f.relativePath, prefix),
4800
+ size: f.size
4801
+ })),
4802
+ totalFiles: files.length,
4803
+ totalSize
4804
+ });
4805
+ return;
4806
+ }
4807
+ if (raw) {
4808
+ for (const file of files) {
4809
+ logger.raw(getBucketKey(file.relativePath, prefix));
4810
+ }
4811
+ return;
4812
+ }
4813
+ logger.remark(`Files to upload ${dim3(`[${files.length}]`)}`);
4814
+ logger.newLine();
4815
+ for (const file of files) {
4816
+ logger.data(getBucketKey(file.relativePath, prefix), formatBytes(file.size), 1);
4817
+ }
4818
+ logger.newLine();
4819
+ logger.data("Total", `${files.length} files (${formatBytes(totalSize)})`, 0);
4820
+ logger.newLine();
4821
+ }
4822
+ function outputUploadResults(uploaded, totalSize, environment, prefix, json, raw) {
4823
+ if (json) {
4824
+ logger.json({
4825
+ success: true,
4826
+ uploaded,
4827
+ totalSize,
4828
+ environment,
4829
+ prefix
4830
+ });
4831
+ return;
4832
+ }
4833
+ if (raw) {
4834
+ logger.raw(`Uploaded files`);
4835
+ return;
4836
+ }
4837
+ if (environment || prefix) {
4838
+ logger.newLine();
4839
+ if (environment) logger.data("Environment", environment, 1);
4840
+ if (prefix) logger.data("Prefix", prefix, 1);
4841
+ }
4842
+ logger.newLine();
4843
+ }
4844
+ async function uploadFilesRemote(files, gameSlug, prefix, uploadFn) {
4845
+ const totalSize = files.reduce((sum, f) => sum + f.size, 0);
4846
+ await runStep(
4847
+ `Uploading ${files.length} files ${dim3(`[${formatBytes(totalSize)}]`)}`,
4848
+ async () => {
4849
+ for (const file of files) {
4850
+ const fileBuffer = readFileSync4(file.absolutePath);
4851
+ const contentType = getContentType(file.absolutePath);
4852
+ const key = getBucketKey(file.relativePath, prefix);
4853
+ await uploadFn(gameSlug, key, fileBuffer, contentType);
4854
+ }
4855
+ },
4856
+ `Uploaded ${files.length} files ${dim3(`[${formatBytes(totalSize)}]`)}`
4857
+ );
4858
+ return files.length;
4859
+ }
4860
+ async function uploadFilesLocal(files, prefix, uploadFn) {
4861
+ const totalSize = files.reduce((sum, f) => sum + f.size, 0);
4862
+ await runStep(
4863
+ `Uploading ${files.length} files ${dim3(`[${formatBytes(totalSize)}]`)}`,
4864
+ async () => {
4865
+ for (const file of files) {
4866
+ const fileBuffer = readFileSync4(file.absolutePath);
4867
+ const contentType = getContentType(file.absolutePath);
4868
+ const key = getBucketKey(file.relativePath, prefix);
4869
+ await uploadFn(key, new Uint8Array(fileBuffer).buffer, contentType);
4870
+ }
4871
+ },
4872
+ `Uploaded ${files.length} files ${dim3(`[${formatBytes(totalSize)}]`)}`
4873
+ );
4874
+ return files.length;
4875
+ }
4876
+
4589
4877
  // src/lib/index.ts
4590
4878
  init_config3();
4591
4879
  init_core();
4592
4880
 
4593
4881
  // src/lib/db/path.ts
4594
4882
  init_constants2();
4595
- import { copyFileSync, existsSync as existsSync5, mkdirSync, readdirSync as readdirSync2, unlinkSync } from "fs";
4596
- import { join as join5 } from "path";
4883
+ import { copyFileSync, existsSync as existsSync6, mkdirSync, readdirSync as readdirSync3, unlinkSync } from "fs";
4884
+ import { join as join7 } from "path";
4597
4885
  import Database from "better-sqlite3";
4598
4886
  var DB_DIRECTORY = CLI_DIRECTORIES.DATABASE;
4599
4887
  var INITIAL_DB_NAME = CLI_FILES.INITIAL_DATABASE;
4600
4888
  var ensureDirectoryExists = (dir) => {
4601
- if (!existsSync5(dir)) {
4889
+ if (!existsSync6(dir)) {
4602
4890
  mkdirSync(dir, { recursive: true });
4603
4891
  }
4604
4892
  };
@@ -4607,79 +4895,150 @@ var createEmptyDatabase = (path2) => {
4607
4895
  db.close();
4608
4896
  };
4609
4897
  var findMiniflareDatabase = (dbDir) => {
4610
- const miniflareDir = join5(dbDir, "miniflare-D1DatabaseObject");
4611
- if (!existsSync5(miniflareDir)) return null;
4612
- const sqliteFiles = readdirSync2(miniflareDir).filter((file) => file.endsWith(".sqlite"));
4898
+ const miniflareDir = join7(dbDir, MINIFLARE_D1_DIRECTORY);
4899
+ if (!existsSync6(miniflareDir)) return null;
4900
+ const sqliteFiles = readdirSync3(miniflareDir).filter((file) => file.endsWith(".sqlite"));
4613
4901
  if (sqliteFiles.length === 0) return null;
4614
- return join5(miniflareDir, sqliteFiles[0]);
4902
+ return join7(miniflareDir, sqliteFiles[0]);
4615
4903
  };
4616
4904
  var migrateInitialDbToTarget = (initialPath, targetPath) => {
4617
- if (!existsSync5(initialPath)) return;
4905
+ if (!existsSync6(initialPath)) return;
4618
4906
  copyFileSync(initialPath, targetPath);
4619
4907
  unlinkSync(initialPath);
4620
4908
  };
4621
4909
  function getDevDbPath() {
4622
- const initialDbPath = join5(DB_DIRECTORY, INITIAL_DB_NAME);
4910
+ const initialDbPath = join7(DB_DIRECTORY, INITIAL_DB_NAME);
4623
4911
  ensureDirectoryExists(DB_DIRECTORY);
4624
4912
  const miniflareDbPath = findMiniflareDatabase(DB_DIRECTORY);
4625
4913
  if (miniflareDbPath) {
4626
4914
  migrateInitialDbToTarget(initialDbPath, miniflareDbPath);
4627
4915
  return miniflareDbPath;
4628
4916
  }
4629
- if (!existsSync5(initialDbPath)) {
4917
+ if (!existsSync6(initialDbPath)) {
4630
4918
  createEmptyDatabase(initialDbPath);
4631
4919
  }
4632
4920
  return initialDbPath;
4633
4921
  }
4634
4922
 
4923
+ // src/lib/db/bundle-seed.ts
4924
+ async function bundleSeedWorker(seedFilePath, projectPath) {
4925
+ const esbuild2 = await import("esbuild");
4926
+ const entryCode = `
4927
+ import { seed } from '${seedFilePath}'
4928
+
4929
+ export default {
4930
+ async fetch(req, env, ctx) {
4931
+ try {
4932
+ // Create Hono-like context
4933
+ const c = { env, ctx, req }
4934
+ await seed(c)
4935
+ return Response.json({ success: true })
4936
+ } catch (error) {
4937
+ return Response.json({
4938
+ success: false,
4939
+ error: error instanceof Error ? error.message : String(error),
4940
+ stack: error instanceof Error ? error.stack : undefined,
4941
+ }, 500)
4942
+ }
4943
+ }
4944
+ }
4945
+ `;
4946
+ const buildConfig = {
4947
+ stdin: {
4948
+ contents: entryCode,
4949
+ resolveDir: projectPath,
4950
+ loader: "ts"
4951
+ },
4952
+ bundle: true,
4953
+ format: "esm",
4954
+ platform: "browser",
4955
+ target: "esnext",
4956
+ external: [],
4957
+ // Bundle everything for self-contained worker
4958
+ write: false,
4959
+ minify: false,
4960
+ sourcemap: false,
4961
+ logLevel: "error"
4962
+ };
4963
+ const result = await esbuild2.build(buildConfig);
4964
+ if (!result.outputFiles?.[0]) {
4965
+ throw new Error("Seed worker bundling failed: no output");
4966
+ }
4967
+ const code = result.outputFiles[0].text;
4968
+ return {
4969
+ code,
4970
+ size: code.length
4971
+ };
4972
+ }
4973
+
4635
4974
  // src/lib/db/reset.ts
4636
- init_log();
4975
+ init_src();
4637
4976
  init_package_manager();
4977
+ init_constants2();
4638
4978
  init_core();
4639
4979
  import { execSync as execSync2 } from "child_process";
4640
- import Database2 from "better-sqlite3";
4641
- function resetDatabase() {
4642
- const dbPath = getDevDbPath();
4643
- const sqlite = new Database2(dbPath);
4644
- const TURN_FOREIGN_KEYS_OFF_SQL = "PRAGMA foreign_keys = OFF";
4645
- const TURN_FOREIGN_KEYS_ON_SQL = "PRAGMA foreign_keys = ON";
4646
- const DROP_ALL_USER_TABLES_SQL = `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`;
4647
- const DROP_TABLE_SQL = (tableName) => `DROP TABLE IF EXISTS ${tableName}`;
4648
- runStep(
4649
- "Dropping all tables",
4980
+ import { rmSync as rmSync2 } from "fs";
4981
+ import { join as join8 } from "path";
4982
+ async function resetDatabase(workspace, mf, options = { debug: false }) {
4983
+ const { debug } = options;
4984
+ const dbDir = join8(workspace, CLI_DIRECTORIES.DATABASE);
4985
+ await runStep(
4986
+ "Resetting database...",
4650
4987
  async () => {
4651
- sqlite.prepare(TURN_FOREIGN_KEYS_OFF_SQL).run();
4652
- const tables = sqlite.prepare(DROP_ALL_USER_TABLES_SQL).all();
4653
- for (const table of tables) {
4654
- sqlite.prepare(DROP_TABLE_SQL(table.name)).run();
4988
+ rmSync2(dbDir, { recursive: true, force: true });
4989
+ try {
4990
+ const d1 = await mf.getD1Database(CLOUDFLARE_BINDINGS.DB);
4991
+ await d1.exec("SELECT 1");
4992
+ } catch {
4993
+ logger.error("Database recreation failed");
4994
+ logger.newLine();
4995
+ process.exit(1);
4655
4996
  }
4656
- sqlite.prepare(TURN_FOREIGN_KEYS_ON_SQL).run();
4997
+ const pm = getPackageManager();
4998
+ const pushCommand = getRunCommand(pm, "db:push");
4999
+ execSync2(pushCommand, {
5000
+ cwd: workspace,
5001
+ stdio: debug ? ["inherit", "inherit", "inherit"] : ["inherit", "ignore", "ignore"]
5002
+ });
4657
5003
  },
4658
- "Dropped all tables"
5004
+ "Database reset"
4659
5005
  );
4660
- try {
4661
- const pm = getPackageManager();
4662
- const pushCommand = getRunCommand(pm, "db:push");
4663
- runStep(
4664
- "Pushing schema",
4665
- async () => {
4666
- execSync2(pushCommand, { stdio: ["inherit", "ignore", "inherit"] });
4667
- },
4668
- "Pushed schema"
4669
- );
4670
- return sqlite;
4671
- } catch (error) {
4672
- logger.error(`Failed to push schema: ${getErrorMessage(error)}`);
5006
+ }
5007
+
5008
+ // src/lib/db/seed.ts
5009
+ init_src();
5010
+ init_constants2();
5011
+ init_core();
5012
+ async function importSeedModule(seedPath) {
5013
+ return await importTypescriptFile(seedPath);
5014
+ }
5015
+ async function executeSeedFile(seedFilePath, mf) {
5016
+ const d1 = await mf.getD1Database(CLOUDFLARE_BINDINGS.DB);
5017
+ const seedModule = await importSeedModule(seedFilePath);
5018
+ if (typeof seedModule.seed !== "function") {
5019
+ logger.error("Seed file must export a seed function");
5020
+ logger.newLine();
5021
+ logger.admonition("warning", "Invalid Seed File", [
5022
+ `Expected: \`export async function seed(c: Context) { ... }\``,
5023
+ `Found in: <${seedFilePath}>`
5024
+ ]);
5025
+ logger.newLine();
4673
5026
  process.exit(1);
4674
5027
  }
5028
+ await runStep(
5029
+ "Seeding database...",
5030
+ async () => seedModule.seed?.({ env: { DB: d1 } }),
5031
+ "Database seeded successfully!"
5032
+ );
5033
+ logger.newLine();
4675
5034
  }
4676
5035
 
4677
5036
  // src/lib/deploy/backend.ts
4678
5037
  init_src();
4679
5038
  init_constants2();
4680
5039
  init_core();
4681
- import { existsSync as existsSync13 } from "node:fs";
4682
- import { join as join15 } from "node:path";
5040
+ import { existsSync as existsSync14 } from "node:fs";
5041
+ import { join as join18 } from "node:path";
4683
5042
 
4684
5043
  // src/lib/init/bucket.ts
4685
5044
  function hasBucketSetup(config) {
@@ -4692,14 +5051,15 @@ init_slug();
4692
5051
  init_core();
4693
5052
  init_logger();
4694
5053
  init_loader2();
4695
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4696
- import { join as join6 } from "path";
5054
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
5055
+ import { join as join9 } from "path";
4697
5056
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
4698
5057
  var dbSchemaUsersTemplate = loadTemplateString("database/db-schema-users.ts");
4699
5058
  var dbSchemaScoresTemplate = loadTemplateString("database/db-schema-scores.ts");
4700
5059
  var dbSchemaIndexTemplate = loadTemplateString("database/db-schema-index.ts");
4701
5060
  var dbIndexTemplate = loadTemplateString("database/db-index.ts");
4702
5061
  var dbTypesTemplate = loadTemplateString("database/db-types.ts");
5062
+ var dbSeedTemplate = loadTemplateString("database/db-seed.ts");
4703
5063
  var packageTemplate = loadTemplateString("database/package.json");
4704
5064
  async function scaffoldDatabaseSetup(options) {
4705
5065
  const workspace = getWorkspace();
@@ -4707,25 +5067,27 @@ async function scaffoldDatabaseSetup(options) {
4707
5067
  await runStep(
4708
5068
  "Configuring database...",
4709
5069
  async () => {
4710
- const dbDir = join6(workspace, "db");
4711
- const schemaDir = join6(dbDir, "schema");
4712
- if (!existsSync6(dbDir)) {
5070
+ const dbDir = join9(workspace, "db");
5071
+ const schemaDir = join9(dbDir, "schema");
5072
+ if (!existsSync7(dbDir)) {
4713
5073
  mkdirSync2(dbDir, { recursive: true });
4714
5074
  }
4715
- if (!existsSync6(schemaDir)) {
5075
+ if (!existsSync7(schemaDir)) {
4716
5076
  mkdirSync2(schemaDir, { recursive: true });
4717
5077
  }
4718
- const usersSchemaPath = join6(schemaDir, "users.ts");
5078
+ const usersSchemaPath = join9(schemaDir, "users.ts");
4719
5079
  writeFileSync2(usersSchemaPath, dbSchemaUsersTemplate);
4720
- const scoresSchemaPath = join6(schemaDir, "scores.ts");
5080
+ const scoresSchemaPath = join9(schemaDir, "scores.ts");
4721
5081
  writeFileSync2(scoresSchemaPath, dbSchemaScoresTemplate);
4722
- const schemaIndexPath = join6(schemaDir, "index.ts");
5082
+ const schemaIndexPath = join9(schemaDir, "index.ts");
4723
5083
  writeFileSync2(schemaIndexPath, dbSchemaIndexTemplate);
4724
- const dbIndexPath = join6(dbDir, "index.ts");
5084
+ const dbIndexPath = join9(dbDir, "index.ts");
4725
5085
  writeFileSync2(dbIndexPath, dbIndexTemplate);
4726
- const dbTypesPath = join6(dbDir, "types.ts");
5086
+ const dbTypesPath = join9(dbDir, "types.ts");
4727
5087
  writeFileSync2(dbTypesPath, dbTypesTemplate);
4728
- const drizzleConfigPath = join6(workspace, "drizzle.config.ts");
5088
+ const dbSeedPath = join9(dbDir, "seed.ts");
5089
+ writeFileSync2(dbSeedPath, dbSeedTemplate);
5090
+ const drizzleConfigPath = join9(workspace, "drizzle.config.ts");
4729
5091
  writeFileSync2(drizzleConfigPath, drizzleConfigTemplate);
4730
5092
  packagesUpdated = await setupPackageJson(workspace, options.gameName);
4731
5093
  },
@@ -4734,7 +5096,7 @@ async function scaffoldDatabaseSetup(options) {
4734
5096
  return packagesUpdated;
4735
5097
  }
4736
5098
  async function setupPackageJson(workspace, gameName) {
4737
- const pkgPath = join6(workspace, "package.json");
5099
+ const pkgPath = join9(workspace, "package.json");
4738
5100
  const dbDeps = {
4739
5101
  "drizzle-orm": "^0.42.0",
4740
5102
  "better-sqlite3": "^12.0.0"
@@ -4748,13 +5110,13 @@ async function setupPackageJson(workspace, gameName) {
4748
5110
  "db:generate": "drizzle-kit generate",
4749
5111
  "db:push": "drizzle-kit push",
4750
5112
  "db:studio": "drizzle-kit studio",
4751
- "db:seed": "tsx db/seed.ts"
5113
+ "db:seed": "playcademy db seed"
4752
5114
  };
4753
- if (existsSync6(pkgPath)) {
5115
+ if (existsSync7(pkgPath)) {
4754
5116
  await runStep(
4755
5117
  "Updating package.json deps",
4756
5118
  async () => {
4757
- const existing = JSON.parse(readFileSync3(pkgPath, "utf-8"));
5119
+ const existing = JSON.parse(readFileSync5(pkgPath, "utf-8"));
4758
5120
  existing.dependencies = { ...existing.dependencies, ...dbDeps };
4759
5121
  existing.devDependencies = { ...existing.devDependencies, ...dbDevDeps };
4760
5122
  existing.scripts = { ...existing.scripts, ...dbScripts };
@@ -4773,9 +5135,9 @@ async function setupPackageJson(workspace, gameName) {
4773
5135
  }
4774
5136
  function hasDatabaseSetup() {
4775
5137
  const workspace = getWorkspace();
4776
- const drizzleConfigPath = join6(workspace, "drizzle.config.ts");
4777
- const drizzleConfigJsPath = join6(workspace, "drizzle.config.js");
4778
- return existsSync6(drizzleConfigPath) || existsSync6(drizzleConfigJsPath);
5138
+ const drizzleConfigPath = join9(workspace, "drizzle.config.ts");
5139
+ const drizzleConfigJsPath = join9(workspace, "drizzle.config.js");
5140
+ return existsSync7(drizzleConfigPath) || existsSync7(drizzleConfigJsPath);
4779
5141
  }
4780
5142
 
4781
5143
  // src/lib/init/kv.ts
@@ -4912,8 +5274,8 @@ var integrationChangeDetectors = {
4912
5274
  };
4913
5275
 
4914
5276
  // src/lib/deploy/bundle.ts
4915
- import { existsSync as existsSync7 } from "fs";
4916
- import { join as join8 } from "path";
5277
+ import { existsSync as existsSync8 } from "fs";
5278
+ import { join as join11 } from "path";
4917
5279
 
4918
5280
  // ../edge-play/src/entry.ts
4919
5281
  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'\n\nimport { registerCors, registerEnvSetup, registerSdkInit } from './entry/middleware'\nimport { setupProcessGlobal } from './entry/setup'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { RuntimeConfig } from './entry/types'\nimport type { HonoEnv } from './types'\n\n/**\n * Config injected at build time by esbuild\n *\n * The `declare const` tells TypeScript \"this exists at runtime, trust me.\"\n * During bundling, esbuild's `define` option does literal text replacement:\n *\n * Example bundling:\n * Source: if (PLAYCADEMY_CONFIG.integrations.timeback) { ... }\n * Define: { 'PLAYCADEMY_CONFIG': JSON.stringify({ integrations: { timeback: {...} } }) }\n * Output: if ({\"integrations\":{\"timeback\":{...}}}.integrations.timeback) { ... }\n *\n * This enables tree-shaking: if timeback is not configured, those code paths are removed.\n * The bundled Worker only includes the routes that are actually enabled.\n */\ndeclare const PLAYCADEMY_CONFIG: RuntimeConfig\n\n// Setup process global polyfill for SDK compatibility\nsetupProcessGlobal()\n\n// Create Hono app\nconst app = new Hono<HonoEnv>()\n\n// Register middleware\nregisterCors(app)\nregisterEnvSetup(app, PLAYCADEMY_CONFIG)\nregisterSdkInit(app, PLAYCADEMY_CONFIG)\n\n// Register built-in integration routes based on enabled integrations\n// This function conditionally imports and registers routes like:\n// - GET /api (always included)\n// - GET /api/health (always included)\n// - POST /api/integrations/timeback/end-activity (if timeback enabled)\n//\n// Uses dynamic imports for tree-shaking: if an integration is not enabled,\n// its route code is completely removed from the bundle.\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\nexport default app\n";
@@ -4936,6 +5298,9 @@ var isDevelopment = () => {
4936
5298
  var isInteractiveTTY = () => {
4937
5299
  return typeof process !== "undefined" && Boolean(process.stdout && process.stdout.isTTY);
4938
5300
  };
5301
+ var isSilent = () => {
5302
+ return typeof process !== "undefined" && process.env.LOG_SILENT === "true";
5303
+ };
4939
5304
  var detectOutputFormat = () => {
4940
5305
  if (isBrowser()) {
4941
5306
  return "browser";
@@ -5056,6 +5421,7 @@ var getMinimumLogLevel = () => {
5056
5421
  return isProduction() ? "info" : "debug";
5057
5422
  };
5058
5423
  var shouldLog = (level) => {
5424
+ if (isSilent()) return false;
5059
5425
  const minLevel = getMinimumLogLevel();
5060
5426
  return levelPriority[level] >= levelPriority[minLevel];
5061
5427
  };
@@ -5176,7 +5542,7 @@ init_file_loader();
5176
5542
  init_core();
5177
5543
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
5178
5544
  import { tmpdir as tmpdir2 } from "os";
5179
- import { join as join7, relative } from "path";
5545
+ import { join as join10, relative as relative2 } from "path";
5180
5546
 
5181
5547
  // src/lib/deploy/hash.ts
5182
5548
  init_file_loader();
@@ -5224,8 +5590,8 @@ async function discoverRoutes(apiDir) {
5224
5590
  const routes = await Promise.all(
5225
5591
  files.map(async (file) => {
5226
5592
  const routePath = filePathToRoutePath(file);
5227
- const absolutePath = join7(apiDir, file);
5228
- const relativePath = relative(getWorkspace(), absolutePath);
5593
+ const absolutePath = join10(apiDir, file);
5594
+ const relativePath = relative2(getWorkspace(), absolutePath);
5229
5595
  const methods = await detectExportedMethods(absolutePath);
5230
5596
  return {
5231
5597
  path: routePath,
@@ -5285,10 +5651,10 @@ async function transpileRoute(filePath) {
5285
5651
  if (!result.outputFiles?.[0]) {
5286
5652
  throw new Error("Transpilation failed: no output");
5287
5653
  }
5288
- const tempDir = join7(tmpdir2(), "playcademy-dev");
5654
+ const tempDir = join10(tmpdir2(), "playcademy-dev");
5289
5655
  await mkdir2(tempDir, { recursive: true });
5290
5656
  const hash = hashContent(filePath).slice(0, 12);
5291
- const jsPath = join7(tempDir, `${hash}.mjs`);
5657
+ const jsPath = join10(tempDir, `${hash}.mjs`);
5292
5658
  await writeFile2(jsPath, result.outputFiles[0].text);
5293
5659
  return jsPath;
5294
5660
  }
@@ -5316,7 +5682,7 @@ async function discoverCustomRoutes(config) {
5316
5682
  const workspace = getWorkspace();
5317
5683
  const customRoutesConfig = config.integrations?.customRoutes;
5318
5684
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
5319
- const customRoutes = await discoverRoutes(join8(workspace, customRoutesDir));
5685
+ const customRoutes = await discoverRoutes(join11(workspace, customRoutesDir));
5320
5686
  const customRouteData = customRoutes.map((r) => ({
5321
5687
  path: r.path,
5322
5688
  file: r.file,
@@ -5328,15 +5694,15 @@ async function discoverCustomRoutes(config) {
5328
5694
  function resolveEmbeddedSourcePaths() {
5329
5695
  const workspace = getWorkspace();
5330
5696
  const distDir = new URL(".", import.meta.url).pathname;
5331
- const embeddedEdgeSrc = join8(distDir, "edge-play", "src");
5332
- const isBuiltPackage = existsSync7(embeddedEdgeSrc);
5697
+ const embeddedEdgeSrc = join11(distDir, "edge-play", "src");
5698
+ const isBuiltPackage = existsSync8(embeddedEdgeSrc);
5333
5699
  const monorepoRoot = getMonorepoRoot();
5334
- const monorepoEdgeSrc = join8(monorepoRoot, "packages/edge-play/src");
5700
+ const monorepoEdgeSrc = join11(monorepoRoot, "packages/edge-play/src");
5335
5701
  const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
5336
- const cliPackageRoot = isBuiltPackage ? join8(distDir, "../../..") : join8(monorepoRoot, "packages/cli");
5337
- const cliNodeModules = isBuiltPackage ? join8(cliPackageRoot, "node_modules") : monorepoRoot;
5338
- const workspaceNodeModules = join8(workspace, "node_modules");
5339
- const constantsEntry = isBuiltPackage ? join8(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join8(monorepoRoot, "packages", "constants", "src", "index.ts");
5702
+ const cliPackageRoot = isBuiltPackage ? join11(distDir, "../../..") : join11(monorepoRoot, "packages/cli");
5703
+ const cliNodeModules = isBuiltPackage ? join11(cliPackageRoot, "node_modules") : monorepoRoot;
5704
+ const workspaceNodeModules = join11(workspace, "node_modules");
5705
+ const constantsEntry = isBuiltPackage ? join11(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join11(monorepoRoot, "packages", "constants", "src", "index.ts");
5340
5706
  return {
5341
5707
  isBuiltPackage,
5342
5708
  edgePlaySrc,
@@ -5396,16 +5762,16 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
5396
5762
  // │ Example: import * as route from '@game-api/hello.ts' │
5397
5763
  // │ Resolves to: /user-project/server/api/hello.ts │
5398
5764
  // └─────────────────────────────────────────────────────────────────┘
5399
- "@game-api": join8(workspace, customRoutesDir),
5765
+ "@game-api": join11(workspace, customRoutesDir),
5400
5766
  // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
5401
5767
  // │ Workers don't have fs, path, os, etc. Redirect to polyfills │
5402
5768
  // │ that throw helpful errors if user code tries to use them. │
5403
5769
  // └─────────────────────────────────────────────────────────────────┘
5404
- fs: join8(edgePlaySrc, "polyfills.js"),
5405
- "fs/promises": join8(edgePlaySrc, "polyfills.js"),
5406
- path: join8(edgePlaySrc, "polyfills.js"),
5407
- os: join8(edgePlaySrc, "polyfills.js"),
5408
- process: join8(edgePlaySrc, "polyfills.js")
5770
+ fs: join11(edgePlaySrc, "polyfills.js"),
5771
+ "fs/promises": join11(edgePlaySrc, "polyfills.js"),
5772
+ path: join11(edgePlaySrc, "polyfills.js"),
5773
+ os: join11(edgePlaySrc, "polyfills.js"),
5774
+ process: join11(edgePlaySrc, "polyfills.js")
5409
5775
  },
5410
5776
  // ──── Build Plugins ────
5411
5777
  plugins: [textLoaderPlugin()],
@@ -5468,10 +5834,12 @@ function generateEntryCode(customRoutes, customRoutesDir) {
5468
5834
  }
5469
5835
 
5470
5836
  // src/lib/deploy/schema.ts
5837
+ init_constants2();
5838
+ init_config3();
5471
5839
  init_core();
5472
- import { existsSync as existsSync12 } from "fs";
5840
+ import { existsSync as existsSync13 } from "fs";
5473
5841
  import { createRequire } from "module";
5474
- import { join as join14 } from "path";
5842
+ import { join as join17 } from "path";
5475
5843
 
5476
5844
  // src/lib/init/prompts.ts
5477
5845
  init_constants3();
@@ -5483,8 +5851,8 @@ import { bold as bold3, cyan as cyan2 } from "colorette";
5483
5851
  init_constants2();
5484
5852
  init_core();
5485
5853
  init_loader2();
5486
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5487
- import { join as join9, resolve as resolve6 } from "path";
5854
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5855
+ import { join as join12, resolve as resolve6 } from "path";
5488
5856
  var sampleCustomRouteTemplate = loadTemplateString("api/sample-custom.ts");
5489
5857
  var sampleDatabaseRouteTemplate = loadTemplateString("api/sample-database.ts");
5490
5858
  var sampleKvRouteTemplate = loadTemplateString("api/sample-kv.ts");
@@ -5492,31 +5860,31 @@ var sampleBucketRouteTemplate = loadTemplateString("api/sample-bucket.ts");
5492
5860
  var playcademyGitignoreTemplate = loadTemplateString("playcademy-gitignore");
5493
5861
  async function scaffoldApiDirectory(apiDirectory, sampleRoutes) {
5494
5862
  const apiPath = resolve6(getWorkspace(), apiDirectory);
5495
- const samplePath = join9(apiPath, "sample");
5496
- if (!existsSync8(apiPath)) {
5863
+ const samplePath = join12(apiPath, "sample");
5864
+ if (!existsSync9(apiPath)) {
5497
5865
  mkdirSync3(apiPath, { recursive: true });
5498
5866
  }
5499
- if (!existsSync8(samplePath)) {
5867
+ if (!existsSync9(samplePath)) {
5500
5868
  mkdirSync3(samplePath, { recursive: true });
5501
5869
  }
5502
5870
  for (const route of sampleRoutes) {
5503
- writeFileSync3(join9(samplePath, route.filename), route.template, "utf-8");
5871
+ writeFileSync3(join12(samplePath, route.filename), route.template, "utf-8");
5504
5872
  }
5505
5873
  }
5506
5874
  function validateApiDirectoryDoesNotExist(value) {
5507
5875
  const dirPath = resolve6(getWorkspace(), value.trim());
5508
- if (existsSync8(dirPath)) {
5876
+ if (existsSync9(dirPath)) {
5509
5877
  return `Directory "${value.trim()}" already exists. Please choose a different name or remove the existing directory.`;
5510
5878
  }
5511
5879
  return true;
5512
5880
  }
5513
5881
  function ensurePlaycademyGitignore() {
5514
5882
  const workspace = getWorkspace();
5515
- const playcademyDir = join9(workspace, CLI_DIRECTORIES.WORKSPACE);
5516
- if (!existsSync8(playcademyDir)) {
5883
+ const playcademyDir = join12(workspace, CLI_DIRECTORIES.WORKSPACE);
5884
+ if (!existsSync9(playcademyDir)) {
5517
5885
  mkdirSync3(playcademyDir, { recursive: true });
5518
5886
  }
5519
- const gitignorePath = join9(playcademyDir, ".gitignore");
5887
+ const gitignorePath = join12(playcademyDir, ".gitignore");
5520
5888
  writeFileSync3(gitignorePath, playcademyGitignoreTemplate);
5521
5889
  }
5522
5890
  async function scaffoldIntegrations(gameName, options) {
@@ -5772,14 +6140,14 @@ init_loader();
5772
6140
  init_core();
5773
6141
  import { execSync as execSync3 } from "child_process";
5774
6142
  import { writeFileSync as writeFileSync5 } from "fs";
5775
- import { dirname as dirname4, join as join12 } from "path";
6143
+ import { dirname as dirname4, join as join15 } from "path";
5776
6144
  import { fileURLToPath as fileURLToPath2 } from "url";
5777
6145
 
5778
6146
  // src/lib/secrets/env.ts
5779
6147
  init_file_loader();
5780
6148
  init_constants2();
5781
- import { existsSync as existsSync9 } from "fs";
5782
- import { join as join10 } from "path";
6149
+ import { existsSync as existsSync10 } from "fs";
6150
+ import { join as join13 } from "path";
5783
6151
  function parseEnvFile(contents) {
5784
6152
  const secrets = {};
5785
6153
  for (const line of contents.split("\n")) {
@@ -5815,10 +6183,10 @@ async function readEnvFile(workspace) {
5815
6183
  return secrets;
5816
6184
  }
5817
6185
  function getLoadedEnvFiles(workspace) {
5818
- return ENV_FILES.filter((filename) => existsSync9(join10(workspace, filename)));
6186
+ return ENV_FILES.filter((filename) => existsSync10(join13(workspace, filename)));
5819
6187
  }
5820
6188
  function hasEnvFile(workspace) {
5821
- return ENV_FILES.some((filename) => existsSync9(join10(workspace, filename)));
6189
+ return ENV_FILES.some((filename) => existsSync10(join13(workspace, filename)));
5822
6190
  }
5823
6191
 
5824
6192
  // src/lib/init/types.ts
@@ -5827,8 +6195,8 @@ init_loader2();
5827
6195
  // src/lib/init/tsconfig.ts
5828
6196
  init_file_loader();
5829
6197
  init_constants2();
5830
- import { existsSync as existsSync10, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
5831
- import { join as join11 } from "path";
6198
+ import { existsSync as existsSync11, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
6199
+ import { join as join14 } from "path";
5832
6200
  function hasPlaycademyEnv(config) {
5833
6201
  return config.include?.includes("playcademy-env.d.ts") ?? false;
5834
6202
  }
@@ -5885,8 +6253,8 @@ function addToIncludeArrayPreservingComments(content) {
5885
6253
  }
5886
6254
  async function ensureTsconfigIncludes(workspace) {
5887
6255
  for (const filename of TSCONFIG_FILES) {
5888
- const configPath = join11(workspace, filename);
5889
- if (!existsSync10(configPath)) {
6256
+ const configPath = join14(workspace, filename);
6257
+ if (!existsSync11(configPath)) {
5890
6258
  continue;
5891
6259
  }
5892
6260
  try {
@@ -5902,7 +6270,7 @@ async function ensureTsconfigIncludes(workspace) {
5902
6270
  return filename;
5903
6271
  }
5904
6272
  try {
5905
- const rawContent = readFileSync4(configPath, "utf-8");
6273
+ const rawContent = readFileSync6(configPath, "utf-8");
5906
6274
  const updatedContent = addToIncludeArrayPreservingComments(rawContent);
5907
6275
  if (updatedContent && updatedContent !== rawContent) {
5908
6276
  writeFileSync4(configPath, updatedContent);
@@ -5935,8 +6303,8 @@ function hasAnyBackend(features) {
5935
6303
  return Object.values(features).some(Boolean);
5936
6304
  }
5937
6305
  async function setupPlaycademyDependencies(workspace) {
5938
- const playcademyDir = join12(workspace, CLI_DIRECTORIES.WORKSPACE);
5939
- const playcademyPkgPath = join12(playcademyDir, "package.json");
6306
+ const playcademyDir = join15(workspace, CLI_DIRECTORIES.WORKSPACE);
6307
+ const playcademyPkgPath = join15(playcademyDir, "package.json");
5940
6308
  const __dirname2 = dirname4(fileURLToPath2(import.meta.url));
5941
6309
  const cliPkg = await loadPackageJson({ cwd: __dirname2, searchUp: true, required: true });
5942
6310
  const workersTypesVersion = cliPkg?.devDependencies?.["@cloudflare/workers-types"] || "latest";
@@ -6006,7 +6374,7 @@ async function ensurePlaycademyTypes(options = {}) {
6006
6374
  const secretsStr = await generateSecretsTypeString(workspace, verbose);
6007
6375
  let envContent = playcademyEnvTemplate.replace("{{BINDINGS}}", bindingsStr);
6008
6376
  envContent = envContent.replace("{{SECRETS}}", secretsStr);
6009
- const envPath = join12(workspace, "playcademy-env.d.ts");
6377
+ const envPath = join15(workspace, "playcademy-env.d.ts");
6010
6378
  writeFileSync5(envPath, envContent);
6011
6379
  if (verbose) {
6012
6380
  logger.success(`Generated <playcademy-env.d.ts>`);
@@ -6026,19 +6394,16 @@ async function ensurePlaycademyTypes(options = {}) {
6026
6394
  // src/lib/init/gitignore.ts
6027
6395
  init_core();
6028
6396
  init_loader2();
6029
- import { existsSync as existsSync11, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
6030
- import { join as join13 } from "path";
6397
+ import { existsSync as existsSync12, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
6398
+ import { join as join16 } from "path";
6031
6399
  var rootGitignoreTemplate = loadTemplateString("gitignore");
6032
- function normalizeGitignoreEntry(entry) {
6033
- return entry.replace(/^\/+/, "").replace(/\/+$/, "");
6034
- }
6035
6400
  function ensureRootGitignore(workspace = getWorkspace()) {
6036
- const gitignorePath = join13(workspace, ".gitignore");
6037
- if (!existsSync11(gitignorePath)) {
6401
+ const gitignorePath = join16(workspace, ".gitignore");
6402
+ if (!existsSync12(gitignorePath)) {
6038
6403
  writeFileSync6(gitignorePath, rootGitignoreTemplate);
6039
6404
  return;
6040
6405
  }
6041
- const existingContent = readFileSync5(gitignorePath, "utf-8");
6406
+ const existingContent = readFileSync7(gitignorePath, "utf-8");
6042
6407
  const existingNormalized = new Set(
6043
6408
  existingContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map(normalizeGitignoreEntry)
6044
6409
  );
@@ -6059,6 +6424,18 @@ function ensureRootGitignore(workspace = getWorkspace()) {
6059
6424
  }
6060
6425
 
6061
6426
  // src/lib/deploy/schema.ts
6427
+ async function getDatabaseDirectory() {
6428
+ try {
6429
+ const config = await loadConfig();
6430
+ const dbIntegration = config.integrations?.database;
6431
+ if (!dbIntegration || dbIntegration === true) {
6432
+ return DEFAULT_DATABASE_DIRECTORY;
6433
+ }
6434
+ return dbIntegration.directory ?? DEFAULT_DATABASE_DIRECTORY;
6435
+ } catch {
6436
+ return DEFAULT_DATABASE_DIRECTORY;
6437
+ }
6438
+ }
6062
6439
  function getDrizzleKitApiExports() {
6063
6440
  const require2 = createRequire(import.meta.url);
6064
6441
  const drizzleKitApi = require2("drizzle-kit/api");
@@ -6073,8 +6450,9 @@ async function getSchemaInfo(previousSchemaSnapshot) {
6073
6450
  if (!hasDatabaseSetup()) {
6074
6451
  return null;
6075
6452
  }
6076
- const schemaPath = join14(workspace, "db/schema/index.ts");
6077
- if (!existsSync12(schemaPath)) {
6453
+ const dbDirectory = await getDatabaseDirectory();
6454
+ const schemaPath = join17(workspace, dbDirectory, SCHEMA_SUBDIRECTORY, SCHEMA_INDEX_FILE);
6455
+ if (!existsSync13(schemaPath)) {
6078
6456
  return null;
6079
6457
  }
6080
6458
  try {
@@ -6157,11 +6535,11 @@ var CUSTOM_ROUTES_EXTENSIONS_WITH_DOT = [".ts", ".js", ".mjs", ".cjs"];
6157
6535
  function getCustomRoutesDirectory(projectPath, config) {
6158
6536
  const customRoutes = config?.integrations?.customRoutes;
6159
6537
  const customRoutesDir = typeof customRoutes === "object" && customRoutes.directory || DEFAULT_API_ROUTES_DIRECTORY;
6160
- return join15(projectPath, customRoutesDir);
6538
+ return join18(projectPath, customRoutesDir);
6161
6539
  }
6162
6540
  function hasLocalCustomRoutes(projectPath, config) {
6163
6541
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
6164
- return existsSync13(customRoutesDir);
6542
+ return existsSync14(customRoutesDir);
6165
6543
  }
6166
6544
  async function getCustomRoutesHash(projectPath, config) {
6167
6545
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
@@ -6169,16 +6547,16 @@ async function getCustomRoutesHash(projectPath, config) {
6169
6547
  }
6170
6548
  async function getCustomRoutesSize(projectPath, config) {
6171
6549
  const { stat: stat3, readdir } = await import("node:fs/promises");
6172
- const { join: join37 } = await import("node:path");
6550
+ const { join: join41 } = await import("node:path");
6173
6551
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
6174
- if (!existsSync13(customRoutesDir)) {
6552
+ if (!existsSync14(customRoutesDir)) {
6175
6553
  return null;
6176
6554
  }
6177
6555
  let totalSize = 0;
6178
6556
  async function calculateDirSize(dir) {
6179
6557
  const entries = await readdir(dir, { withFileTypes: true });
6180
6558
  for (const entry of entries) {
6181
- const fullPath = join37(dir, entry.name);
6559
+ const fullPath = join41(dir, entry.name);
6182
6560
  if (entry.isDirectory()) {
6183
6561
  await calculateDirSize(fullPath);
6184
6562
  } else if (entry.isFile()) {
@@ -6372,7 +6750,7 @@ async function loadDeployConfig(configPath) {
6372
6750
  // src/lib/deploy/diff.ts
6373
6751
  init_string();
6374
6752
  init_logger();
6375
- import { dim as dim3, green as green2, red as red2 } from "colorette";
6753
+ import { dim as dim4, green as green2, red as red2 } from "colorette";
6376
6754
  function calculateConfigDiff(existingGame, newConfig) {
6377
6755
  const diff = {};
6378
6756
  if (existingGame.displayName !== newConfig.displayName) {
@@ -6489,7 +6867,7 @@ function displayDeploymentDiff(options) {
6489
6867
  const oldSize = formatSize(previousSize);
6490
6868
  const newSize = formatSize(currentSize);
6491
6869
  const maxWidth = Math.max(oldSize.length, newSize.length);
6492
- const delta = dim3(`(${formatDelta(currentSize - previousSize)})`);
6870
+ const delta = dim4(`(${formatDelta(currentSize - previousSize)})`);
6493
6871
  buildInfo = `${red2(oldSize.padEnd(maxWidth))} \u2192 ${green2(newSize.padEnd(maxWidth))} ${delta}`;
6494
6872
  } else if (currentSize !== void 0) {
6495
6873
  buildInfo = green2(formatSize(currentSize));
@@ -6509,7 +6887,7 @@ function displayDeploymentDiff(options) {
6509
6887
  const oldSize = formatSize(previousSize);
6510
6888
  const newSize = formatSize(currentSize);
6511
6889
  const maxWidth = Math.max(oldSize.length, newSize.length);
6512
- const delta = dim3(`(${formatDelta(currentSize - previousSize)})`);
6890
+ const delta = dim4(`(${formatDelta(currentSize - previousSize)})`);
6513
6891
  routesInfo = `${red2(oldSize.padEnd(maxWidth))} \u2192 ${green2(newSize.padEnd(maxWidth))} ${delta}`;
6514
6892
  } else if (currentSize !== void 0) {
6515
6893
  routesInfo = green2(formatSize(currentSize));
@@ -6523,12 +6901,12 @@ function displayDeploymentDiff(options) {
6523
6901
  const currentSize = backend?.currentBundleSize;
6524
6902
  let bundleInfo;
6525
6903
  if (forceBackend && !backendChanged) {
6526
- bundleInfo = dim3("(forced)");
6904
+ bundleInfo = dim4("(forced)");
6527
6905
  } else if (previousSize !== void 0 && currentSize !== void 0) {
6528
6906
  const oldSize = formatSize(previousSize);
6529
6907
  const newSize = formatSize(currentSize);
6530
6908
  const maxWidth = Math.max(oldSize.length, newSize.length);
6531
- const delta = dim3(`(${formatDelta(currentSize - previousSize)})`);
6909
+ const delta = dim4(`(${formatDelta(currentSize - previousSize)})`);
6532
6910
  bundleInfo = `${red2(oldSize.padEnd(maxWidth))} \u2192 ${green2(newSize.padEnd(maxWidth))} ${delta}`;
6533
6911
  } else if (currentSize !== void 0) {
6534
6912
  bundleInfo = green2(formatSize(currentSize));
@@ -6590,19 +6968,19 @@ init_context();
6590
6968
  init_logger();
6591
6969
  import { stat } from "node:fs/promises";
6592
6970
  import { confirm as confirm2, input as input2, select as select2 } from "@inquirer/prompts";
6593
- import { dim as dim4 } from "colorette";
6971
+ import { dim as dim5 } from "colorette";
6594
6972
 
6595
6973
  // src/lib/deploy/build-discovery.ts
6596
6974
  init_file_loader();
6597
6975
  init_constants2();
6598
6976
  init_core();
6599
- import { join as join16, relative as relative2 } from "path";
6977
+ import { join as join19, relative as relative3 } from "path";
6600
6978
  function findSingleBuildZip() {
6601
6979
  const workspace = getWorkspace();
6602
- const playcademyDir = join16(workspace, CLI_DIRECTORIES.WORKSPACE);
6980
+ const playcademyDir = join19(workspace, CLI_DIRECTORIES.WORKSPACE);
6603
6981
  const zipFiles = findFilesByExtension(playcademyDir, "zip");
6604
6982
  if (zipFiles.length === 1) {
6605
- return zipFiles[0] ? relative2(workspace, zipFiles[0]) : null;
6983
+ return zipFiles[0] ? relative3(workspace, zipFiles[0]) : null;
6606
6984
  }
6607
6985
  return null;
6608
6986
  }
@@ -6635,14 +7013,14 @@ function hasOptionalFieldsMissing(missing) {
6635
7013
 
6636
7014
  // src/lib/deploy/validate.ts
6637
7015
  init_logger();
6638
- import { existsSync as existsSync14 } from "fs";
7016
+ import { existsSync as existsSync15 } from "fs";
6639
7017
  import { resolve as resolve7 } from "path";
6640
7018
  function validateBuildPath(path2) {
6641
7019
  if (!path2.trim()) {
6642
7020
  return "Build path is required";
6643
7021
  }
6644
7022
  const resolvedPath = resolve7(path2.trim());
6645
- if (!existsSync14(resolvedPath)) {
7023
+ if (!existsSync15(resolvedPath)) {
6646
7024
  return `Build path not found: ${path2.trim()}`;
6647
7025
  }
6648
7026
  return true;
@@ -6740,8 +7118,8 @@ async function promptForMissingConfig(context2) {
6740
7118
  const gameType = await select2({
6741
7119
  message: "Game type:",
6742
7120
  choices: [
6743
- { value: "hosted", name: `Hosted ${dim4("[Upload a game build]")}` },
6744
- { value: "external", name: `External ${dim4("[Link to an external URL]")}` }
7121
+ { value: "hosted", name: `Hosted ${dim5("[Upload a game build]")}` },
7122
+ { value: "external", name: `External ${dim5("[Link to an external URL]")}` }
6745
7123
  ]
6746
7124
  });
6747
7125
  config.gameType = gameType;
@@ -7449,22 +7827,22 @@ init_context();
7449
7827
 
7450
7828
  // src/lib/games/storage.ts
7451
7829
  init_constants2();
7452
- import { existsSync as existsSync15 } from "node:fs";
7830
+ import { existsSync as existsSync16 } from "node:fs";
7453
7831
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
7454
7832
  import { homedir as homedir2 } from "node:os";
7455
- import { join as join17 } from "node:path";
7833
+ import { join as join20 } from "node:path";
7456
7834
  function getGamesStorePath() {
7457
- return join17(homedir2(), CLI_USER_DIRECTORIES.CONFIG, CLI_FILES.GAMES_STORE);
7835
+ return join20(homedir2(), CLI_USER_DIRECTORIES.CONFIG, CLI_FILES.GAMES_STORE);
7458
7836
  }
7459
7837
  async function ensureConfigDir() {
7460
- const configDir = join17(homedir2(), CLI_USER_DIRECTORIES.CONFIG);
7838
+ const configDir = join20(homedir2(), CLI_USER_DIRECTORIES.CONFIG);
7461
7839
  await mkdir3(configDir, { recursive: true });
7462
7840
  }
7463
7841
  async function loadGameStore() {
7464
7842
  try {
7465
7843
  await ensureConfigDir();
7466
7844
  const storePath = getGamesStorePath();
7467
- if (existsSync15(storePath)) {
7845
+ if (existsSync16(storePath)) {
7468
7846
  const content = await readFile3(storePath, "utf-8");
7469
7847
  return JSON.parse(content);
7470
7848
  }
@@ -7733,7 +8111,7 @@ async function calculateDeploymentPlan(context2, changes) {
7733
8111
  // src/lib/deploy/steps.ts
7734
8112
  init_src();
7735
8113
  init_logger();
7736
- import { existsSync as existsSync16 } from "fs";
8114
+ import { existsSync as existsSync17 } from "fs";
7737
8115
  import { readFile as readFile4 } from "fs/promises";
7738
8116
  import { basename as basename2, resolve as resolve8 } from "path";
7739
8117
  function prepareGameMetadata(config) {
@@ -7753,7 +8131,7 @@ function prepareGameMetadata(config) {
7753
8131
  }
7754
8132
  async function prepareBuildFile(buildPath) {
7755
8133
  const resolvedPath = resolve8(buildPath);
7756
- if (resolvedPath.endsWith(".zip") && existsSync16(resolvedPath)) {
8134
+ if (resolvedPath.endsWith(".zip") && existsSync17(resolvedPath)) {
7757
8135
  const buffer = await readFile4(resolvedPath);
7758
8136
  const uint8Array = new Uint8Array(buffer);
7759
8137
  const blob = new Blob([uint8Array], { type: "application/zip" });
@@ -7971,20 +8349,20 @@ async function saveDeploymentState(game, backendDeployment, context2) {
7971
8349
  // src/lib/dev/backend.ts
7972
8350
  init_constants2();
7973
8351
  init_core();
7974
- import { existsSync as existsSync17 } from "fs";
7975
- import { join as join18 } from "path";
8352
+ import { existsSync as existsSync18 } from "fs";
8353
+ import { join as join21 } from "path";
7976
8354
  function hasCustomRoutes(config) {
7977
8355
  const workspace = getWorkspace();
7978
8356
  const customRoutesConfig = config?.integrations?.customRoutes;
7979
8357
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
7980
- return existsSync17(join18(workspace, customRoutesDir));
8358
+ return existsSync18(join21(workspace, customRoutesDir));
7981
8359
  }
7982
8360
  function needsBackend(config) {
7983
8361
  return !!config?.integrations || hasCustomRoutes(config);
7984
8362
  }
7985
8363
 
7986
8364
  // src/lib/dev/display.ts
7987
- import { dim as dim5 } from "colorette";
8365
+ import { dim as dim6 } from "colorette";
7988
8366
 
7989
8367
  // ../edge-play/src/register-routes.ts
7990
8368
  init_constants4();
@@ -8068,16 +8446,16 @@ function displayRegisteredRoutes(integrations, customRoutes = []) {
8068
8446
  const maxPathLength = Math.max(...sortedRoutes.map((r) => r.path.length));
8069
8447
  sortedRoutes.forEach((route) => {
8070
8448
  const paddedPath = route.path.padEnd(maxPathLength + 2, " ");
8071
- logger.customRaw(`<${paddedPath}> ${dim5(route.method)}`, 1);
8449
+ logger.customRaw(`<${paddedPath}> ${dim6(route.method)}`, 1);
8072
8450
  });
8073
8451
  }
8074
8452
 
8075
8453
  // src/lib/dev/reload.ts
8076
8454
  init_constants2();
8077
8455
  init_core();
8078
- import { join as join19, relative as relative3 } from "path";
8456
+ import { join as join22, relative as relative4 } from "path";
8079
8457
  import chokidar from "chokidar";
8080
- import { bold as bold4, cyan as cyan3, dim as dim6, green as green3 } from "colorette";
8458
+ import { bold as bold4, cyan as cyan3, dim as dim7, green as green3 } from "colorette";
8081
8459
  function formatTime() {
8082
8460
  const now = /* @__PURE__ */ new Date();
8083
8461
  let hours = now.getHours();
@@ -8092,9 +8470,9 @@ function startHotReload(onReload, options = {}) {
8092
8470
  const customRoutesConfig = options.config?.integrations?.customRoutes;
8093
8471
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
8094
8472
  const watchPaths = [
8095
- join19(workspace, customRoutesDir),
8096
- join19(workspace, "playcademy.config.js"),
8097
- join19(workspace, "playcademy.config.json")
8473
+ join22(workspace, customRoutesDir),
8474
+ join22(workspace, "playcademy.config.js"),
8475
+ join22(workspace, "playcademy.config.json")
8098
8476
  ];
8099
8477
  const watcher = chokidar.watch(watchPaths, {
8100
8478
  persistent: true,
@@ -8106,11 +8484,11 @@ function startHotReload(onReload, options = {}) {
8106
8484
  });
8107
8485
  const logSuccess = options.onSuccess || ((changedPath, eventType) => {
8108
8486
  if (changedPath) {
8109
- const relativePath = relative3(workspace, changedPath);
8110
- const timestamp5 = dim6(formatTime());
8487
+ const relativePath = relative4(workspace, changedPath);
8488
+ const timestamp5 = dim7(formatTime());
8111
8489
  const brand = bold4(cyan3("[playcademy]"));
8112
8490
  const event = eventType === "changed" ? green3("reload") : green3(eventType || "reload");
8113
- console.log(`${timestamp5} ${brand} ${event} ${dim6(relativePath)}`);
8491
+ console.log(`${timestamp5} ${brand} ${event} ${dim7(relativePath)}`);
8114
8492
  } else {
8115
8493
  logger.success("Reloaded");
8116
8494
  }
@@ -8136,14 +8514,14 @@ function startHotReload(onReload, options = {}) {
8136
8514
  // src/lib/dev/server.ts
8137
8515
  init_src2();
8138
8516
  import { mkdir as mkdir4 } from "fs/promises";
8139
- import { join as join21 } from "path";
8517
+ import { join as join24 } from "path";
8140
8518
  import { Log, LogLevel, Miniflare } from "miniflare";
8141
8519
 
8142
8520
  // ../utils/src/port.ts
8143
- import { existsSync as existsSync18, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "node:fs";
8521
+ import { existsSync as existsSync19, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
8144
8522
  import { createServer as createServer2 } from "node:net";
8145
8523
  import { homedir as homedir3 } from "node:os";
8146
- import { join as join20 } from "node:path";
8524
+ import { join as join23 } from "node:path";
8147
8525
  async function isPortAvailableOnHost(port, host) {
8148
8526
  return new Promise((resolve11) => {
8149
8527
  const server = createServer2();
@@ -8182,19 +8560,19 @@ async function findAvailablePort(startPort = 4321) {
8182
8560
  }
8183
8561
  function getRegistryPath() {
8184
8562
  const home = homedir3();
8185
- const dir = join20(home, ".playcademy");
8186
- if (!existsSync18(dir)) {
8563
+ const dir = join23(home, ".playcademy");
8564
+ if (!existsSync19(dir)) {
8187
8565
  mkdirSync4(dir, { recursive: true });
8188
8566
  }
8189
- return join20(dir, ".proc");
8567
+ return join23(dir, ".proc");
8190
8568
  }
8191
8569
  function readRegistry() {
8192
8570
  const registryPath = getRegistryPath();
8193
- if (!existsSync18(registryPath)) {
8571
+ if (!existsSync19(registryPath)) {
8194
8572
  return {};
8195
8573
  }
8196
8574
  try {
8197
- const content = readFileSync6(registryPath, "utf-8");
8575
+ const content = readFileSync8(registryPath, "utf-8");
8198
8576
  return JSON.parse(content);
8199
8577
  } catch {
8200
8578
  return {};
@@ -8326,11 +8704,11 @@ async function startDevServer(options) {
8326
8704
  }
8327
8705
  ],
8328
8706
  bindings,
8329
- d1Databases: hasDatabase ? ["DB"] : [],
8707
+ d1Databases: hasDatabase ? [CLOUDFLARE_BINDINGS.DB] : [],
8330
8708
  d1Persist: dbDir,
8331
- kvNamespaces: hasKV ? ["KV"] : [],
8709
+ kvNamespaces: hasKV ? [CLOUDFLARE_BINDINGS.KV] : [],
8332
8710
  kvPersist: kvDir,
8333
- r2Buckets: hasBucket ? ["BUCKET"] : [],
8711
+ r2Buckets: hasBucket ? [CLOUDFLARE_BINDINGS.BUCKET] : [],
8334
8712
  r2Persist: bucketDir,
8335
8713
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
8336
8714
  });
@@ -8341,7 +8719,7 @@ async function startDevServer(options) {
8341
8719
  return { server: mf, port };
8342
8720
  }
8343
8721
  async function ensureDatabaseDirectory() {
8344
- const dbDir = join21(getWorkspace(), CLI_DIRECTORIES.DATABASE);
8722
+ const dbDir = join24(getWorkspace(), CLI_DIRECTORIES.DATABASE);
8345
8723
  try {
8346
8724
  await mkdir4(dbDir, { recursive: true });
8347
8725
  } catch (error) {
@@ -8350,7 +8728,7 @@ async function ensureDatabaseDirectory() {
8350
8728
  return dbDir;
8351
8729
  }
8352
8730
  async function ensureKvDirectory() {
8353
- const kvDir = join21(getWorkspace(), CLI_DIRECTORIES.KV);
8731
+ const kvDir = join24(getWorkspace(), CLI_DIRECTORIES.KV);
8354
8732
  try {
8355
8733
  await mkdir4(kvDir, { recursive: true });
8356
8734
  } catch (error) {
@@ -8359,7 +8737,7 @@ async function ensureKvDirectory() {
8359
8737
  return kvDir;
8360
8738
  }
8361
8739
  async function ensureBucketDirectory() {
8362
- const bucketDir = join21(getWorkspace(), CLI_DIRECTORIES.BUCKET);
8740
+ const bucketDir = join24(getWorkspace(), CLI_DIRECTORIES.BUCKET);
8363
8741
  try {
8364
8742
  await mkdir4(bucketDir, { recursive: true });
8365
8743
  } catch (error) {
@@ -8368,7 +8746,7 @@ async function ensureBucketDirectory() {
8368
8746
  return bucketDir;
8369
8747
  }
8370
8748
  async function initializeDatabase(mf) {
8371
- const d1 = await mf.getD1Database("DB");
8749
+ const d1 = await mf.getD1Database(CLOUDFLARE_BINDINGS.DB);
8372
8750
  await d1.exec("SELECT 1");
8373
8751
  }
8374
8752
  async function writeBackendServerInfo(port) {
@@ -8595,7 +8973,6 @@ var initCommand = new Command2("init").description("Initialize a playcademy.conf
8595
8973
  },
8596
8974
  "Project configured"
8597
8975
  );
8598
- logger.newLine();
8599
8976
  const configContent = configFormat === "js" ? generateJsConfig({
8600
8977
  name: gameInfo.name,
8601
8978
  description: gameInfo.description,
@@ -8633,7 +9010,7 @@ var initCommand = new Command2("init").description("Initialize a playcademy.conf
8633
9010
  });
8634
9011
  async function addPlaycademySdk() {
8635
9012
  const pkgPath = resolve10(getWorkspace(), "package.json");
8636
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
9013
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
8637
9014
  const hasSdk = pkg.dependencies?.["@playcademy/sdk"] || pkg.devDependencies?.["@playcademy/sdk"];
8638
9015
  if (hasSdk) {
8639
9016
  return false;
@@ -8650,7 +9027,7 @@ init_src();
8650
9027
  init_file_loader();
8651
9028
  init_constants2();
8652
9029
  import { input as input3, password, select as select3 } from "@inquirer/prompts";
8653
- import { bold as bold5, dim as dim7, whiteBright } from "colorette";
9030
+ import { bold as bold5, dim as dim8, whiteBright } from "colorette";
8654
9031
  import { Command as Command3 } from "commander";
8655
9032
  import open from "open";
8656
9033
  var loginCommand = new Command3("login").description("Authenticate with Playcademy").option("-e, --email <email>", "Email address (for email/password auth)").option("-p, --password <password>", "Password (for email/password auth)").option("--sso", "Use Timeback SSO authentication").option("--env <environment>", "Environment to login to (staging or production)").action(async (options) => {
@@ -8666,10 +9043,10 @@ var loginCommand = new Command3("login").description("Authenticate with Playcade
8666
9043
  logger.admonition("note", "Hello again", [
8667
9044
  bold5("You're already logged in"),
8668
9045
  "",
8669
- dim7("Email: ") + bold5(email2),
8670
- dim7("Environment: ") + bold5(environment),
8671
- dim7("Profile: ") + bold5(profileName),
8672
- ...otherProfile ? [dim7("Other Profile: ") + bold5(otherEnv)] : []
9046
+ dim8("Email: ") + bold5(email2),
9047
+ dim8("Environment: ") + bold5(environment),
9048
+ dim8("Profile: ") + bold5(profileName),
9049
+ ...otherProfile ? [dim8("Other Profile: ") + bold5(otherEnv)] : []
8673
9050
  ]);
8674
9051
  logger.newLine();
8675
9052
  return;
@@ -9507,139 +9884,329 @@ async function runDbInit() {
9507
9884
 
9508
9885
  // src/commands/db/reset.ts
9509
9886
  init_src();
9510
- import { spawn } from "child_process";
9511
- import { existsSync as existsSync19, rmSync as rmSync2 } from "fs";
9512
- import { join as join22 } from "path";
9513
- import { confirm as confirm6 } from "@inquirer/prompts";
9887
+ import { existsSync as existsSync20 } from "fs";
9888
+ import { join as join25 } from "path";
9889
+ import { confirm as confirm6, input as input7 } from "@inquirer/prompts";
9890
+ import { bold as bold6, redBright, underline as underline3 } from "colorette";
9514
9891
  import { Miniflare as Miniflare2 } from "miniflare";
9515
9892
  init_constants2();
9516
- async function runDbReset() {
9517
- try {
9518
- const workspace = getWorkspace();
9519
- const dbDir = join22(workspace, CLI_DIRECTORIES.DATABASE);
9520
- if (!existsSync19(dbDir)) {
9521
- logger.warn("No database found to reset");
9522
- logger.newLine();
9523
- logger.remark("Nothing to do");
9524
- logger.newLine();
9525
- return;
9526
- }
9893
+ async function runDbResetRemote(options) {
9894
+ const environment = ensureEnvironment(options.env);
9895
+ const client = await requireAuthenticatedClient();
9896
+ const workspace = getWorkspace();
9897
+ const deployedGame = await getDeployedGame(workspace);
9898
+ if (!deployedGame) {
9527
9899
  logger.newLine();
9528
- if (isServerRunning("backend", workspace)) {
9529
- logger.admonition("warning", "Stop Dev Server First", [
9530
- "The development server must be stopped before resetting the database.",
9531
- "Stop the server (Ctrl+C) and run this command again to ensure a clean reset."
9532
- ]);
9533
- logger.newLine();
9534
- process.exit(1);
9535
- }
9536
- const shouldReset = await confirm6({
9537
- message: "This will delete all local database data. Continue?",
9538
- default: false
9539
- });
9540
- if (!shouldReset) {
9541
- logger.newLine();
9542
- logger.remark("Nothing to do");
9543
- logger.newLine();
9544
- return;
9545
- }
9546
- runStep(
9547
- "Deleting database...",
9548
- async () => {
9549
- rmSync2(dbDir, { recursive: true, force: true });
9550
- },
9551
- "Database deleted"
9552
- );
9900
+ logger.admonition("warning", "Deploy First", [
9901
+ `Deploy your game before resetting remote database: \`playcademy deploy\``
9902
+ ]);
9553
9903
  logger.newLine();
9554
- if (!hasDatabaseSetup()) {
9555
- logger.remark("No drizzle config found, skipping schema setup");
9556
- logger.newLine();
9557
- return;
9558
- }
9559
- await loadConfig();
9560
- try {
9561
- const mf = new Miniflare2({
9562
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
9563
- d1Databases: ["DB"],
9564
- d1Persist: dbDir,
9565
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
9566
- });
9567
- const d1 = await mf.getD1Database("DB");
9568
- await d1.exec("SELECT 1");
9569
- await mf.dispose();
9570
- } catch {
9571
- logger.newLine();
9572
- logger.error("Database recreation failed");
9573
- logger.newLine();
9574
- process.exit(1);
9575
- }
9576
- const exitCode = await runStep(
9577
- "Database recreating...",
9578
- async () => {
9579
- const exitCode2 = await new Promise((resolve11) => {
9580
- const child = spawn("npx", ["drizzle-kit", "push", "--force"], {
9581
- cwd: workspace,
9582
- stdio: ["ignore", "ignore", "inherit"],
9583
- // stdin: ignore, stdout: ignore, stderr: inherit
9584
- env: process.env
9585
- });
9586
- child.on("close", (code) => resolve11(code || 0));
9587
- child.on("error", () => resolve11(1));
9588
- });
9589
- return exitCode2;
9590
- },
9591
- "Database recreated"
9592
- );
9593
- if (exitCode !== 0) {
9594
- logger.newLine();
9595
- logger.error("Schema push failed");
9596
- process.exit(exitCode ?? 1);
9597
- }
9904
+ process.exit(1);
9905
+ }
9906
+ const game = await client.games.fetch(deployedGame.gameId);
9907
+ logger.newLine();
9908
+ logger.admonition("warning", "DESTRUCTIVE OPERATION", [
9909
+ `Are you sure you want to ${redBright(underline3(bold6("DELETE ALL DATA")))} in your ${environment} database?`,
9910
+ `All tables will be dropped and recreated from schema.`,
9911
+ `This action is irreversible and ${underline3(bold6("cannot be undone"))}.`
9912
+ ]);
9913
+ logger.newLine();
9914
+ const confirmed = await confirm6({
9915
+ message: "Are you sure you want to reset this database? This cannot be undone.",
9916
+ default: false
9917
+ });
9918
+ if (!confirmed) {
9598
9919
  logger.newLine();
9599
- logger.success("Database reset complete!");
9920
+ logger.remark("Cancelled");
9600
9921
  logger.newLine();
9922
+ return;
9923
+ }
9924
+ const slugConfirmation = await input7({
9925
+ message: `Type the game slug "${game.slug}" to confirm:`,
9926
+ validate: (value) => {
9927
+ if (value !== game.slug) {
9928
+ return `Slug does not match. Please type exactly: ${game.slug}`;
9929
+ }
9930
+ return true;
9931
+ }
9932
+ });
9933
+ if (slugConfirmation !== game.slug) {
9934
+ logger.newLine();
9935
+ logger.remark("Cancelled");
9936
+ logger.newLine();
9937
+ return;
9938
+ }
9939
+ logger.newLine();
9940
+ const schema = await getSchemaInfo();
9941
+ if (!schema) {
9942
+ logger.error("Failed to generate schema. Make sure your database schema directory exists.");
9943
+ logger.newLine();
9944
+ process.exit(1);
9945
+ }
9946
+ const result = await runStep(
9947
+ `Resetting ${environment} database`,
9948
+ async () => {
9949
+ return client.dev.games.database.reset(game.slug, schema);
9950
+ },
9951
+ `${capitalize(environment)} database reset`
9952
+ );
9953
+ if (!result.schemaPushed) {
9954
+ logger.admonition("warning", "Failed to recreate schema", [
9955
+ "During reset, the schema was not successfully pushed to your database.",
9956
+ "Please retry the `db reset` command; if the problem persists, contact support."
9957
+ ]);
9958
+ logger.newLine();
9959
+ process.exit(1);
9960
+ }
9961
+ logger.newLine();
9962
+ logger.success("Database reset complete!");
9963
+ logger.newLine();
9964
+ }
9965
+ async function runDbResetLocal(options) {
9966
+ const workspace = getWorkspace();
9967
+ const dbDir = join25(workspace, CLI_DIRECTORIES.DATABASE);
9968
+ if (!existsSync20(dbDir)) {
9969
+ logger.warn("No database found to reset");
9970
+ logger.newLine();
9971
+ logger.remark("Nothing to do");
9972
+ logger.newLine();
9973
+ return;
9974
+ }
9975
+ logger.newLine();
9976
+ if (isServerRunning("backend", workspace)) {
9977
+ logger.admonition("warning", "Stop Dev Server First", [
9978
+ "The development server must be stopped before resetting the database.",
9979
+ "Stop the server (Ctrl+C) and run this command again to ensure a clean reset."
9980
+ ]);
9981
+ logger.newLine();
9982
+ process.exit(1);
9983
+ }
9984
+ const shouldReset = await confirm6({
9985
+ message: "This will delete all local database data. Continue?",
9986
+ default: false
9987
+ });
9988
+ if (!shouldReset) {
9989
+ logger.newLine();
9990
+ logger.remark("Nothing to do");
9991
+ logger.newLine();
9992
+ return;
9993
+ }
9994
+ if (!hasDatabaseSetup()) {
9995
+ logger.remark("No drizzle config found, skipping schema setup");
9996
+ logger.newLine();
9997
+ return;
9998
+ }
9999
+ const mf = new Miniflare2({
10000
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10001
+ d1Databases: [CLOUDFLARE_BINDINGS.DB],
10002
+ d1Persist: dbDir,
10003
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10004
+ });
10005
+ try {
10006
+ logger.newLine();
10007
+ await resetDatabase(workspace, mf, { debug: options.debug });
10008
+ } finally {
10009
+ await mf.dispose();
10010
+ }
10011
+ logger.newLine();
10012
+ }
10013
+ async function runDbReset(options = {}) {
10014
+ try {
10015
+ if (options.remote) {
10016
+ await runDbResetRemote(options);
10017
+ } else {
10018
+ await runDbResetLocal(options);
10019
+ }
9601
10020
  } catch (error) {
9602
10021
  logAndExit(error, logger, { prefix: "Failed to reset database" });
9603
10022
  }
9604
10023
  }
9605
10024
 
9606
10025
  // src/commands/db/seed.ts
9607
- init_package_manager();
9608
- import { execSync as execSync5 } from "child_process";
9609
- import { existsSync as existsSync20 } from "fs";
9610
- import { join as join23 } from "path";
9611
- async function runDbSeed(options) {
10026
+ init_src();
10027
+ init_constants2();
10028
+ import { existsSync as existsSync21 } from "fs";
10029
+ import { join as join26 } from "path";
10030
+ import { confirm as confirm7, input as input8 } from "@inquirer/prompts";
10031
+ import { bold as bold7, redBright as redBright2, underline as underline4 } from "colorette";
10032
+ import { Miniflare as Miniflare3 } from "miniflare";
10033
+ async function runDbResetRemote2(gameSlug, environment) {
10034
+ const client = await requireAuthenticatedClient();
10035
+ const schema = await getSchemaInfo();
10036
+ if (!schema) {
10037
+ logger.error("Failed to generate schema. Make sure your database schema directory exists.");
10038
+ logger.newLine();
10039
+ process.exit(1);
10040
+ }
10041
+ const result = await runStep(
10042
+ `Resetting ${environment} database`,
10043
+ async () => {
10044
+ return client.dev.games.database.reset(gameSlug, schema);
10045
+ },
10046
+ `${capitalize(environment)} database reset`
10047
+ );
10048
+ if (!result.schemaPushed) {
10049
+ logger.admonition("warning", "Failed to recreate schema", [
10050
+ "Schema was not successfully pushed after reset.",
10051
+ "Please retry the command."
10052
+ ]);
10053
+ logger.newLine();
10054
+ process.exit(1);
10055
+ }
10056
+ }
10057
+ async function runDbSeedRemote(seedFile, options) {
10058
+ const environment = ensureEnvironment(options.env);
10059
+ const client = await requireAuthenticatedClient();
9612
10060
  const workspace = getWorkspace();
9613
- try {
9614
- if (options.reset) {
9615
- resetDatabase();
10061
+ const deployedGame = await getDeployedGame(workspace);
10062
+ if (!deployedGame) {
10063
+ logger.newLine();
10064
+ logger.admonition("warning", "Deploy First", [
10065
+ `Deploy your game before seeding remote database: \`playcademy deploy\``
10066
+ ]);
10067
+ logger.newLine();
10068
+ process.exit(1);
10069
+ }
10070
+ const game = await client.games.fetch(deployedGame.gameId);
10071
+ const willReset = options.reset !== false;
10072
+ logger.newLine();
10073
+ if (willReset) {
10074
+ logger.admonition("warning", "Remote Database Seeding", [
10075
+ `Are you sure you want to ${redBright2(underline4(bold7("DELETE ALL DATA")))} in your ${environment} database?`,
10076
+ `This action is irreversible and ${underline4(bold7("cannot be undone"))}.`,
10077
+ `Run this command with \`--no-reset\` if you want to seed without resetting.`
10078
+ ]);
10079
+ } else {
10080
+ logger.admonition("warning", "Remote Database Seeding", [
10081
+ `Are you sure you want to seed your ${environment} database?`,
10082
+ `If existing data is present, this may produce unpredictable results.`,
10083
+ "Proceed with caution."
10084
+ ]);
10085
+ }
10086
+ logger.newLine();
10087
+ if (willReset) {
10088
+ const confirmed = await confirm7({
10089
+ message: "Are you sure you want to reset and seed this database? This cannot be undone.",
10090
+ default: false
10091
+ });
10092
+ if (!confirmed) {
10093
+ logger.newLine();
10094
+ logger.remark("Cancelled");
9616
10095
  logger.newLine();
10096
+ return;
9617
10097
  }
9618
- if (options.file) {
9619
- const seedPath = join23(workspace, options.file);
9620
- if (!existsSync20(seedPath)) {
9621
- logger.error(`Seed file not found: ${options.file}`);
9622
- logger.newLine();
9623
- process.exit(1);
10098
+ const slugConfirmation = await input8({
10099
+ message: `Type the game slug "${game.slug}" to confirm:`,
10100
+ validate: (value) => {
10101
+ if (value !== game.slug) {
10102
+ return `Slug does not match. Please type exactly: ${game.slug}`;
10103
+ }
10104
+ return true;
9624
10105
  }
9625
- const pm2 = getPackageManager();
9626
- const tsxCommand = getRunCommand(pm2, `tsx ${seedPath}`);
9627
- execSync5(tsxCommand, { cwd: workspace, stdio: "inherit" });
10106
+ });
10107
+ if (slugConfirmation !== game.slug) {
10108
+ logger.newLine();
10109
+ logger.remark("Cancelled");
10110
+ logger.newLine();
10111
+ return;
10112
+ }
10113
+ } else {
10114
+ const shouldSeed = await confirm7({
10115
+ message: `Seed ${environment} database for ${game.slug}?`,
10116
+ default: false
10117
+ });
10118
+ if (!shouldSeed) {
10119
+ logger.newLine();
10120
+ logger.remark("Cancelled");
10121
+ logger.newLine();
9628
10122
  return;
9629
10123
  }
9630
- const pm = getPackageManager();
9631
- const seedCommand = getRunCommand(pm, "db:seed");
9632
- execSync5(seedCommand, { cwd: workspace, stdio: "inherit" });
10124
+ }
10125
+ logger.newLine();
10126
+ if (willReset) {
10127
+ await runDbResetRemote2(game.slug, environment);
10128
+ }
10129
+ let bundle;
10130
+ await runStep(
10131
+ "Bundling seed task",
10132
+ async () => {
10133
+ bundle = await bundleSeedWorker(seedFile, workspace);
10134
+ },
10135
+ "Seed task bundled"
10136
+ );
10137
+ await runStep(
10138
+ `Seeding ${environment} database`,
10139
+ async () => {
10140
+ await client.dev.games.deploy.seed(game.slug, bundle.code, environment);
10141
+ },
10142
+ `${environment.charAt(0).toUpperCase() + environment.slice(1)} database seeded`
10143
+ );
10144
+ logger.newLine();
10145
+ }
10146
+ async function runDbSeedLocal(seedFile, options) {
10147
+ const workspace = getWorkspace();
10148
+ const dbDir = join26(workspace, CLI_DIRECTORIES.DATABASE);
10149
+ const mf = new Miniflare3({
10150
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10151
+ d1Databases: [CLOUDFLARE_BINDINGS.DB],
10152
+ d1Persist: dbDir,
10153
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10154
+ });
10155
+ logger.newLine();
10156
+ if (options.reset !== false) {
10157
+ await resetDatabase(workspace, mf, { debug: options.debug });
10158
+ }
10159
+ try {
10160
+ await executeSeedFile(seedFile, mf);
10161
+ } finally {
10162
+ await mf.dispose();
10163
+ }
10164
+ }
10165
+ async function runDbSeed(options = {}) {
10166
+ const workspace = getWorkspace();
10167
+ try {
10168
+ const seedFile = options.file ? join26(workspace, options.file) : join26(workspace, DEFAULT_DATABASE_DIRECTORY, DEFAULT_SEED_FILE_NAME);
10169
+ if (!existsSync21(seedFile)) {
10170
+ logger.newLine();
10171
+ logger.error(`Seed file not found: ${seedFile}`);
10172
+ logger.newLine();
10173
+ logger.admonition("tip", "Create Seed File", [
10174
+ "Run `playcademy db init` to scaffold database with seed file",
10175
+ `Or create ${seedFile} with: \`export async function seed(c: Context) { ... }\``
10176
+ ]);
10177
+ logger.newLine();
10178
+ process.exit(1);
10179
+ }
10180
+ if (options.remote) {
10181
+ await runDbSeedRemote(seedFile, options);
10182
+ } else {
10183
+ await runDbSeedLocal(seedFile, options);
10184
+ }
9633
10185
  } catch (error) {
9634
- logAndExit(error, logger, { prefix: "Failed to run seed" });
10186
+ logger.newLine();
10187
+ logger.error(
10188
+ `Failed to seed database: ${error instanceof Error ? error.message : String(error)}`
10189
+ );
10190
+ logger.newLine();
10191
+ process.exit(1);
9635
10192
  }
9636
10193
  }
9637
10194
 
9638
10195
  // src/commands/db/index.ts
9639
10196
  var dbCommand = new Command14("db").description("Database management commands");
9640
10197
  dbCommand.command("init").description("Add database to your project").action(runDbInit);
9641
- dbCommand.command("reset").description("Reset local development database (deletes data and recreates with schema)").action(runDbReset);
9642
- dbCommand.command("seed [file]").description("Seed local database with initial data").option("-r, --reset", "Reset database before seeding").action((file, options) => runDbSeed({ file, reset: options.reset }));
10198
+ dbCommand.command("reset").description("Reset database").option("--remote", "Reset remote deployed database").option("--env <environment>", "Environment: staging (default) or production").option("--debug", "Enable debug mode").action(
10199
+ (options) => runDbReset({ debug: options.debug, remote: options.remote, env: options.env })
10200
+ );
10201
+ dbCommand.command("seed [file]").description("Seed database with initial data").option("--no-reset", "Skip database reset before seeding").option("--remote", "Seed remote deployed database").option("--debug", "Enable debug mode").option("--env <environment>", "Environment: staging (default) or production").action(
10202
+ (file, options) => runDbSeed({
10203
+ file,
10204
+ reset: options.reset,
10205
+ remote: options.remote,
10206
+ env: options.env,
10207
+ debug: options.debug
10208
+ })
10209
+ );
9643
10210
  dbCommand.command("diff").description("Show schema changes since last deployment").action(runDbDiff);
9644
10211
 
9645
10212
  // src/commands/kv/index.ts
@@ -9648,9 +10215,9 @@ import { Command as Command15 } from "commander";
9648
10215
  // src/commands/kv/clear.ts
9649
10216
  init_string();
9650
10217
  init_constants2();
9651
- import { join as join24 } from "path";
9652
- import { confirm as confirm7 } from "@inquirer/prompts";
9653
- import { Miniflare as Miniflare3 } from "miniflare";
10218
+ import { join as join27 } from "path";
10219
+ import { confirm as confirm8 } from "@inquirer/prompts";
10220
+ import { Miniflare as Miniflare4 } from "miniflare";
9654
10221
  async function runKVClear(options = {}) {
9655
10222
  try {
9656
10223
  if (!options.raw && !options.json) {
@@ -9681,15 +10248,15 @@ async function runKVClear(options = {}) {
9681
10248
  }
9682
10249
  process.exit(1);
9683
10250
  }
9684
- const kvDir = join24(getWorkspace(), CLI_DIRECTORIES.KV);
9685
- const mf = new Miniflare3({
10251
+ const kvDir = join27(getWorkspace(), CLI_DIRECTORIES.KV);
10252
+ const mf = new Miniflare4({
9686
10253
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
9687
- kvNamespaces: ["KV"],
10254
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
9688
10255
  kvPersist: kvDir,
9689
10256
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
9690
10257
  });
9691
10258
  try {
9692
- const kv = await mf.getKVNamespace("KV");
10259
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
9693
10260
  const listResult = await kv.list();
9694
10261
  const keyCount = listResult.keys?.length || 0;
9695
10262
  if (keyCount === 0) {
@@ -9711,7 +10278,7 @@ async function runKVClear(options = {}) {
9711
10278
  if (!options.force && !options.raw && !options.json) {
9712
10279
  logger.warn(`This will delete ${keyCount} ${pluralize(keyCount, "key")}`);
9713
10280
  logger.newLine();
9714
- const confirmed = await confirm7({
10281
+ const confirmed = await confirm8({
9715
10282
  message: "Are you sure you want to clear all keys?",
9716
10283
  default: false
9717
10284
  });
@@ -9753,8 +10320,8 @@ async function runKVClear(options = {}) {
9753
10320
 
9754
10321
  // src/commands/kv/delete.ts
9755
10322
  init_constants2();
9756
- import { join as join25 } from "path";
9757
- import { Miniflare as Miniflare4 } from "miniflare";
10323
+ import { join as join28 } from "path";
10324
+ import { Miniflare as Miniflare5 } from "miniflare";
9758
10325
  async function runKVDelete(key, options = {}) {
9759
10326
  try {
9760
10327
  if (!options.raw && !options.json) {
@@ -9794,15 +10361,15 @@ async function runKVDelete(key, options = {}) {
9794
10361
  }
9795
10362
  process.exit(1);
9796
10363
  }
9797
- const kvDir = join25(getWorkspace(), CLI_DIRECTORIES.KV);
9798
- const mf = new Miniflare4({
10364
+ const kvDir = join28(getWorkspace(), CLI_DIRECTORIES.KV);
10365
+ const mf = new Miniflare5({
9799
10366
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
9800
- kvNamespaces: ["KV"],
10367
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
9801
10368
  kvPersist: kvDir,
9802
10369
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
9803
10370
  });
9804
10371
  try {
9805
- const kv = await mf.getKVNamespace("KV");
10372
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
9806
10373
  await kv.delete(key);
9807
10374
  if (options.json) {
9808
10375
  logger.json({
@@ -9831,8 +10398,8 @@ async function runKVDelete(key, options = {}) {
9831
10398
 
9832
10399
  // src/commands/kv/get.ts
9833
10400
  init_constants2();
9834
- import { join as join26 } from "path";
9835
- import { Miniflare as Miniflare5 } from "miniflare";
10401
+ import { join as join29 } from "path";
10402
+ import { Miniflare as Miniflare6 } from "miniflare";
9836
10403
  async function runKVGet(key, options = {}) {
9837
10404
  try {
9838
10405
  if (!options.raw && !options.json) {
@@ -9872,15 +10439,15 @@ async function runKVGet(key, options = {}) {
9872
10439
  }
9873
10440
  process.exit(1);
9874
10441
  }
9875
- const kvDir = join26(getWorkspace(), CLI_DIRECTORIES.KV);
9876
- const mf = new Miniflare5({
10442
+ const kvDir = join29(getWorkspace(), CLI_DIRECTORIES.KV);
10443
+ const mf = new Miniflare6({
9877
10444
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
9878
- kvNamespaces: ["KV"],
10445
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
9879
10446
  kvPersist: kvDir,
9880
10447
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
9881
10448
  });
9882
10449
  try {
9883
- const kv = await mf.getKVNamespace("KV");
10450
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
9884
10451
  const value = await kv.get(key);
9885
10452
  if (value === null) {
9886
10453
  if (!options.raw && !options.json) {
@@ -9937,7 +10504,7 @@ async function runKVGet(key, options = {}) {
9937
10504
 
9938
10505
  // src/commands/kv/init.ts
9939
10506
  init_constants2();
9940
- import { input as input7 } from "@inquirer/prompts";
10507
+ import { input as input9 } from "@inquirer/prompts";
9941
10508
  init_writer();
9942
10509
  init_loader2();
9943
10510
  var sampleCustomRouteTemplate3 = loadTemplateString("api/sample-custom.ts");
@@ -9970,7 +10537,7 @@ async function runKVInit() {
9970
10537
  logger.success("Added KV integration to config");
9971
10538
  logger.newLine();
9972
10539
  if (!hasLocalCustomRoutes(getWorkspace(), config)) {
9973
- const apiDirectory = await input7({
10540
+ const apiDirectory = await input9({
9974
10541
  message: " \u2514\u2500 API routes directory:",
9975
10542
  default: DEFAULT_API_ROUTES_DIRECTORY,
9976
10543
  validate: (value) => {
@@ -10011,8 +10578,8 @@ async function runKVInit() {
10011
10578
 
10012
10579
  // src/commands/kv/inspect.ts
10013
10580
  init_constants2();
10014
- import { join as join27 } from "path";
10015
- import { Miniflare as Miniflare6 } from "miniflare";
10581
+ import { join as join30 } from "path";
10582
+ import { Miniflare as Miniflare7 } from "miniflare";
10016
10583
  async function runKVInspect(key, options = {}) {
10017
10584
  try {
10018
10585
  if (!options.raw && !options.json) {
@@ -10052,15 +10619,15 @@ async function runKVInspect(key, options = {}) {
10052
10619
  }
10053
10620
  process.exit(1);
10054
10621
  }
10055
- const kvDir = join27(getWorkspace(), CLI_DIRECTORIES.KV);
10056
- const mf = new Miniflare6({
10622
+ const kvDir = join30(getWorkspace(), CLI_DIRECTORIES.KV);
10623
+ const mf = new Miniflare7({
10057
10624
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10058
- kvNamespaces: ["KV"],
10625
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
10059
10626
  kvPersist: kvDir,
10060
10627
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10061
10628
  });
10062
10629
  try {
10063
- const kv = await mf.getKVNamespace("KV");
10630
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
10064
10631
  const value = await kv.get(key);
10065
10632
  if (value === null) {
10066
10633
  const metadata2 = {
@@ -10140,8 +10707,8 @@ async function runKVInspect(key, options = {}) {
10140
10707
  // src/commands/kv/list.ts
10141
10708
  init_string();
10142
10709
  init_constants2();
10143
- import { join as join28 } from "path";
10144
- import { Miniflare as Miniflare7 } from "miniflare";
10710
+ import { join as join31 } from "path";
10711
+ import { Miniflare as Miniflare8 } from "miniflare";
10145
10712
  async function runKVList(options = {}) {
10146
10713
  try {
10147
10714
  if (!options.raw && !options.json) {
@@ -10172,15 +10739,15 @@ async function runKVList(options = {}) {
10172
10739
  }
10173
10740
  process.exit(1);
10174
10741
  }
10175
- const kvDir = join28(getWorkspace(), CLI_DIRECTORIES.KV);
10176
- const mf = new Miniflare7({
10742
+ const kvDir = join31(getWorkspace(), CLI_DIRECTORIES.KV);
10743
+ const mf = new Miniflare8({
10177
10744
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10178
- kvNamespaces: ["KV"],
10745
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
10179
10746
  kvPersist: kvDir,
10180
10747
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10181
10748
  });
10182
10749
  try {
10183
- const kv = await mf.getKVNamespace("KV");
10750
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
10184
10751
  const listResult = await kv.list();
10185
10752
  const keyNames = listResult.keys?.map((k) => k.name) || [];
10186
10753
  if (options.json) {
@@ -10221,9 +10788,9 @@ async function runKVList(options = {}) {
10221
10788
  init_file_loader();
10222
10789
  init_string();
10223
10790
  init_constants2();
10224
- import { join as join29 } from "path";
10225
- import { confirm as confirm8 } from "@inquirer/prompts";
10226
- import { Miniflare as Miniflare8 } from "miniflare";
10791
+ import { join as join32 } from "path";
10792
+ import { confirm as confirm9 } from "@inquirer/prompts";
10793
+ import { Miniflare as Miniflare9 } from "miniflare";
10227
10794
  async function runKVSeed(seedFile, options = {}) {
10228
10795
  try {
10229
10796
  if (!options.raw && !options.json) {
@@ -10287,15 +10854,15 @@ async function runKVSeed(seedFile, options = {}) {
10287
10854
  }
10288
10855
  process.exit(1);
10289
10856
  }
10290
- const kvDir = join29(workspace, CLI_DIRECTORIES.KV);
10291
- const mf = new Miniflare8({
10857
+ const kvDir = join32(workspace, CLI_DIRECTORIES.KV);
10858
+ const mf = new Miniflare9({
10292
10859
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10293
- kvNamespaces: ["KV"],
10860
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
10294
10861
  kvPersist: kvDir,
10295
10862
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10296
10863
  });
10297
10864
  try {
10298
- const kv = await mf.getKVNamespace("KV");
10865
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
10299
10866
  const keysToSeed = Object.keys(seedData);
10300
10867
  if (!options.force && !options.replace && !options.raw && !options.json) {
10301
10868
  const existingKeys = [];
@@ -10310,7 +10877,7 @@ async function runKVSeed(seedFile, options = {}) {
10310
10877
  `${existingKeys.length} ${pluralize(existingKeys.length, "key")} will be overwritten`
10311
10878
  );
10312
10879
  logger.newLine();
10313
- const confirmed = await confirm8({
10880
+ const confirmed = await confirm9({
10314
10881
  message: "Continue seeding?",
10315
10882
  default: false
10316
10883
  });
@@ -10368,8 +10935,8 @@ async function runKVSeed(seedFile, options = {}) {
10368
10935
  // src/commands/kv/set.ts
10369
10936
  init_file_loader();
10370
10937
  init_constants2();
10371
- import { join as join30 } from "path";
10372
- import { Miniflare as Miniflare9 } from "miniflare";
10938
+ import { join as join33 } from "path";
10939
+ import { Miniflare as Miniflare10 } from "miniflare";
10373
10940
  async function runKVSet(key, value, options = {}) {
10374
10941
  try {
10375
10942
  if (!options.raw && !options.json) {
@@ -10438,15 +11005,15 @@ async function runKVSet(key, value, options = {}) {
10438
11005
  }
10439
11006
  process.exit(1);
10440
11007
  }
10441
- const kvDir = join30(getWorkspace(), CLI_DIRECTORIES.KV);
10442
- const mf = new Miniflare9({
11008
+ const kvDir = join33(getWorkspace(), CLI_DIRECTORIES.KV);
11009
+ const mf = new Miniflare10({
10443
11010
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10444
- kvNamespaces: ["KV"],
11011
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
10445
11012
  kvPersist: kvDir,
10446
11013
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10447
11014
  });
10448
11015
  try {
10449
- const kv = await mf.getKVNamespace("KV");
11016
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
10450
11017
  await kv.put(key, valueToSet);
10451
11018
  if (options.json) {
10452
11019
  logger.json({
@@ -10476,8 +11043,8 @@ async function runKVSet(key, value, options = {}) {
10476
11043
  // src/commands/kv/stats.ts
10477
11044
  init_string();
10478
11045
  init_constants2();
10479
- import { join as join31 } from "path";
10480
- import { Miniflare as Miniflare10 } from "miniflare";
11046
+ import { join as join34 } from "path";
11047
+ import { Miniflare as Miniflare11 } from "miniflare";
10481
11048
  async function runKVStats(options = {}) {
10482
11049
  try {
10483
11050
  if (!options.raw && !options.json) {
@@ -10508,15 +11075,15 @@ async function runKVStats(options = {}) {
10508
11075
  }
10509
11076
  process.exit(1);
10510
11077
  }
10511
- const kvDir = join31(getWorkspace(), CLI_DIRECTORIES.KV);
10512
- const mf = new Miniflare10({
11078
+ const kvDir = join34(getWorkspace(), CLI_DIRECTORIES.KV);
11079
+ const mf = new Miniflare11({
10513
11080
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10514
- kvNamespaces: ["KV"],
11081
+ kvNamespaces: [CLOUDFLARE_BINDINGS.KV],
10515
11082
  kvPersist: kvDir,
10516
11083
  compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10517
11084
  });
10518
11085
  try {
10519
- const kv = await mf.getKVNamespace("KV");
11086
+ const kv = await mf.getKVNamespace(CLOUDFLARE_BINDINGS.KV);
10520
11087
  const listResult = await kv.list();
10521
11088
  const keys = listResult.keys || [];
10522
11089
  let totalSize = 0;
@@ -10623,87 +11190,246 @@ kvCommand.command("seed <file>").description("Seed KV namespace with key-value p
10623
11190
  // src/commands/bucket/index.ts
10624
11191
  import { Command as Command16 } from "commander";
10625
11192
 
10626
- // src/commands/bucket/delete.ts
11193
+ // src/commands/bucket/bulk.ts
10627
11194
  init_constants2();
10628
- import { join as join32 } from "path";
10629
- import { Miniflare as Miniflare11 } from "miniflare";
10630
- async function runBucketDelete(key, options = {}) {
10631
- try {
11195
+ import { existsSync as existsSync22, statSync as statSync3 } from "fs";
11196
+ import { join as join35 } from "path";
11197
+ import { Miniflare as Miniflare12 } from "miniflare";
11198
+ async function runBucketBulkRemote(directory, options) {
11199
+ const environment = ensureEnvironment(options.env);
11200
+ const client = await requireAuthenticatedClient();
11201
+ const workspace = getWorkspace();
11202
+ const deployedGame = await getDeployedGame(workspace);
11203
+ if (!deployedGame) {
10632
11204
  if (!options.raw && !options.json) {
11205
+ logger.admonition("warning", "Deploy First", [
11206
+ `Deploy your game before accessing remote bucket: \`playcademy deploy\``
11207
+ ]);
10633
11208
  logger.newLine();
10634
11209
  }
10635
- if (options.remote) {
10636
- if (!options.raw && !options.json) {
10637
- logger.newLine();
10638
- logger.warn("Remote bucket operations are not yet implemented");
10639
- logger.newLine();
10640
- logger.admonition("warning", "Coming Soon", [
10641
- "Remote bucket support is on the roadmap and will be available soon.",
10642
- "For now, bucket commands only work with local development storage."
10643
- ]);
10644
- logger.newLine();
10645
- }
10646
- process.exit(1);
11210
+ process.exit(1);
11211
+ }
11212
+ const { files, totalSize } = collectBulkFiles(directory);
11213
+ if (files.length === 0) {
11214
+ if (!options.raw && !options.json) {
11215
+ logger.warn("No files found to upload");
11216
+ logger.newLine();
10647
11217
  }
10648
- if (!key) {
10649
- if (!options.raw && !options.json) {
10650
- logger.error("File key is required");
10651
- logger.newLine();
10652
- logger.admonition("tip", "Usage", ["`playcademy bucket delete <key>`"]);
10653
- logger.newLine();
10654
- }
10655
- process.exit(1);
11218
+ process.exit(0);
11219
+ }
11220
+ if (options.dryRun) {
11221
+ outputDryRunResults(files, totalSize, options.prefix, options.json, options.raw);
11222
+ return;
11223
+ }
11224
+ const game = await client.games.fetch(deployedGame.gameId);
11225
+ const uploaded = await uploadFilesRemote(
11226
+ files,
11227
+ game.slug,
11228
+ options.prefix,
11229
+ client.dev.games.bucket.put.bind(client.dev.games.bucket)
11230
+ );
11231
+ outputUploadResults(uploaded, totalSize, environment, options.prefix, options.json, options.raw);
11232
+ }
11233
+ async function runBucketBulkLocal(directory, options) {
11234
+ const config = await loadConfig();
11235
+ if (!hasBucketSetup(config)) {
11236
+ if (!options.raw && !options.json) {
11237
+ logger.error("Bucket storage is not configured");
11238
+ logger.newLine();
11239
+ logger.admonition("tip", "Getting Started", [
11240
+ "Run `playcademy bucket init` to enable bucket storage"
11241
+ ]);
11242
+ logger.newLine();
10656
11243
  }
10657
- const config = await loadConfig();
10658
- if (!hasBucketSetup(config)) {
11244
+ process.exit(1);
11245
+ }
11246
+ const { files, totalSize } = collectBulkFiles(directory);
11247
+ if (files.length === 0) {
11248
+ if (!options.raw && !options.json) {
11249
+ logger.warn("No files found to upload");
11250
+ logger.newLine();
11251
+ }
11252
+ process.exit(0);
11253
+ }
11254
+ if (options.dryRun) {
11255
+ outputDryRunResults(files, totalSize, options.prefix, options.json, options.raw);
11256
+ return;
11257
+ }
11258
+ const bucketDir = join35(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11259
+ const mf = new Miniflare12({
11260
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11261
+ r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
11262
+ r2Persist: bucketDir,
11263
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11264
+ });
11265
+ try {
11266
+ const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET);
11267
+ const uploaded = await uploadFilesLocal(
11268
+ files,
11269
+ options.prefix,
11270
+ async (key, content, contentType) => {
11271
+ await bucket.put(key, content, {
11272
+ httpMetadata: {
11273
+ contentType
11274
+ }
11275
+ });
11276
+ }
11277
+ );
11278
+ outputUploadResults(
11279
+ uploaded,
11280
+ totalSize,
11281
+ void 0,
11282
+ options.prefix,
11283
+ options.json,
11284
+ options.raw
11285
+ );
11286
+ } finally {
11287
+ await mf.dispose();
11288
+ }
11289
+ }
11290
+ async function runBucketBulk(directory, options = {}) {
11291
+ try {
11292
+ if (!options.raw && !options.json) {
11293
+ logger.newLine();
11294
+ }
11295
+ if (!existsSync22(directory)) {
10659
11296
  if (!options.raw && !options.json) {
10660
- logger.error("Bucket storage is not configured");
11297
+ logger.error(`Directory not found: ${directory}`);
10661
11298
  logger.newLine();
10662
- logger.admonition("tip", "Getting Started", [
10663
- "Run `playcademy bucket init` to enable bucket storage"
10664
- ]);
11299
+ }
11300
+ process.exit(1);
11301
+ }
11302
+ const stats = statSync3(directory);
11303
+ if (!stats.isDirectory()) {
11304
+ if (!options.raw && !options.json) {
11305
+ logger.error(`Not a directory: ${directory}`);
10665
11306
  logger.newLine();
10666
11307
  }
10667
11308
  process.exit(1);
10668
11309
  }
10669
- const bucketDir = join32(getWorkspace(), CLI_DIRECTORIES.BUCKET);
10670
- const mf = new Miniflare11({
10671
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10672
- r2Buckets: ["BUCKET"],
10673
- r2Persist: bucketDir,
10674
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11310
+ if (options.remote) {
11311
+ await runBucketBulkRemote(directory, options);
11312
+ } else {
11313
+ await runBucketBulkLocal(directory, options);
11314
+ }
11315
+ } catch (error) {
11316
+ if (!options.raw && !options.json) {
11317
+ logger.error(
11318
+ `Failed to bulk upload: ${error instanceof Error ? error.message : String(error)}`
11319
+ );
11320
+ logger.newLine();
11321
+ }
11322
+ process.exit(1);
11323
+ }
11324
+ }
11325
+
11326
+ // src/commands/bucket/delete.ts
11327
+ init_constants2();
11328
+ import { join as join36 } from "path";
11329
+ import { Miniflare as Miniflare13 } from "miniflare";
11330
+ async function runBucketDeleteRemote(key, options) {
11331
+ const environment = ensureEnvironment(options.env);
11332
+ const client = await requireAuthenticatedClient();
11333
+ const workspace = getWorkspace();
11334
+ const deployedGame = await getDeployedGame(workspace);
11335
+ if (!deployedGame) {
11336
+ if (!options.raw && !options.json) {
11337
+ logger.admonition("warning", "Deploy First", [
11338
+ `Deploy your game before accessing remote bucket: \`playcademy deploy\``
11339
+ ]);
11340
+ logger.newLine();
11341
+ }
11342
+ process.exit(1);
11343
+ }
11344
+ const game = await client.games.fetch(deployedGame.gameId);
11345
+ await client.dev.games.bucket.delete(game.slug, key);
11346
+ if (options.json) {
11347
+ logger.json({
11348
+ success: true,
11349
+ key,
11350
+ message: "File deleted successfully"
10675
11351
  });
10676
- try {
10677
- const bucket = await mf.getR2Bucket("BUCKET");
10678
- const object = await bucket.get(key);
10679
- if (!object) {
10680
- if (!options.raw && !options.json) {
10681
- logger.warn(`File '${key}' not found`);
10682
- logger.newLine();
10683
- logger.admonition("tip", "Hint", [
10684
- "Use `playcademy bucket list` to see all available files"
10685
- ]);
10686
- logger.newLine();
10687
- }
10688
- process.exit(1);
10689
- }
10690
- await bucket.delete(key);
10691
- if (options.json) {
10692
- logger.json({
10693
- success: true,
10694
- key,
10695
- message: "File deleted successfully"
10696
- });
10697
- return;
10698
- }
10699
- if (options.raw) {
10700
- logger.raw(`Deleted ${key}`);
10701
- return;
11352
+ return;
11353
+ }
11354
+ if (options.raw) {
11355
+ logger.raw(`Deleted ${key}`);
11356
+ return;
11357
+ }
11358
+ logger.newLine();
11359
+ logger.success(`Deleted '${key}' from ${environment}`);
11360
+ logger.newLine();
11361
+ }
11362
+ async function runBucketDeleteLocal(key, options) {
11363
+ if (!key) {
11364
+ if (!options.raw && !options.json) {
11365
+ logger.error("File key is required");
11366
+ logger.newLine();
11367
+ logger.admonition("tip", "Usage", ["`playcademy bucket delete <key>`"]);
11368
+ logger.newLine();
11369
+ }
11370
+ process.exit(1);
11371
+ }
11372
+ const config = await loadConfig();
11373
+ if (!hasBucketSetup(config)) {
11374
+ if (!options.raw && !options.json) {
11375
+ logger.error("Bucket storage is not configured");
11376
+ logger.newLine();
11377
+ logger.admonition("tip", "Getting Started", [
11378
+ "Run `playcademy bucket init` to enable bucket storage"
11379
+ ]);
11380
+ logger.newLine();
11381
+ }
11382
+ process.exit(1);
11383
+ }
11384
+ const bucketDir = join36(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11385
+ const mf = new Miniflare13({
11386
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11387
+ r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
11388
+ r2Persist: bucketDir,
11389
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11390
+ });
11391
+ try {
11392
+ const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET);
11393
+ const object = await bucket.get(key);
11394
+ if (!object) {
11395
+ if (!options.raw && !options.json) {
11396
+ logger.warn(`File '${key}' not found`);
11397
+ logger.newLine();
11398
+ logger.admonition("tip", "Hint", [
11399
+ "Use `playcademy bucket list` to see all available files"
11400
+ ]);
11401
+ logger.newLine();
10702
11402
  }
10703
- logger.success(`Deleted '${key}'`);
11403
+ process.exit(1);
11404
+ }
11405
+ await bucket.delete(key);
11406
+ if (options.json) {
11407
+ logger.json({
11408
+ success: true,
11409
+ key,
11410
+ message: "File deleted successfully"
11411
+ });
11412
+ return;
11413
+ }
11414
+ if (options.raw) {
11415
+ logger.raw(`Deleted ${key}`);
11416
+ return;
11417
+ }
11418
+ logger.success(`Deleted '${key}'`);
11419
+ logger.newLine();
11420
+ } finally {
11421
+ await mf.dispose();
11422
+ }
11423
+ }
11424
+ async function runBucketDelete(key, options = {}) {
11425
+ try {
11426
+ if (!options.raw && !options.json) {
10704
11427
  logger.newLine();
10705
- } finally {
10706
- await mf.dispose();
11428
+ }
11429
+ if (options.remote) {
11430
+ await runBucketDeleteRemote(key, options);
11431
+ } else {
11432
+ await runBucketDeleteLocal(key, options);
10707
11433
  }
10708
11434
  } catch (error) {
10709
11435
  if (!options.raw && !options.json) {
@@ -10719,108 +11445,171 @@ async function runBucketDelete(key, options = {}) {
10719
11445
  // src/commands/bucket/get.ts
10720
11446
  init_constants2();
10721
11447
  import { writeFileSync as writeFileSync10 } from "fs";
10722
- import { join as join33 } from "path";
10723
- import { Miniflare as Miniflare12 } from "miniflare";
10724
- async function runBucketGet(key, options = {}) {
10725
- try {
11448
+ import { join as join37 } from "path";
11449
+ import { Miniflare as Miniflare14 } from "miniflare";
11450
+ async function runBucketGetRemote(key, options) {
11451
+ const environment = ensureEnvironment(options.env);
11452
+ const client = await requireAuthenticatedClient();
11453
+ const workspace = getWorkspace();
11454
+ const deployedGame = await getDeployedGame(workspace);
11455
+ if (!deployedGame) {
10726
11456
  if (!options.raw && !options.json) {
11457
+ logger.admonition("warning", "Deploy First", [
11458
+ `Deploy your game before accessing remote bucket: \`playcademy deploy\``
11459
+ ]);
10727
11460
  logger.newLine();
10728
11461
  }
10729
- if (options.remote) {
11462
+ process.exit(1);
11463
+ }
11464
+ const game = await client.games.fetch(deployedGame.gameId);
11465
+ let arrayBuffer;
11466
+ try {
11467
+ arrayBuffer = await client.dev.games.bucket.get(game.slug, key);
11468
+ } catch (error) {
11469
+ const errorMessage = error instanceof Error ? error.message : String(error);
11470
+ if (errorMessage.includes("not found")) {
10730
11471
  if (!options.raw && !options.json) {
11472
+ logger.warn(`File '${key}' not found in ${environment}`);
10731
11473
  logger.newLine();
10732
- logger.warn("Remote bucket operations are not yet implemented");
10733
- logger.newLine();
10734
- logger.admonition("info", "Coming Soon", [
10735
- "Remote bucket support is on the roadmap and will be available soon.",
10736
- "For now, bucket commands only work with local development storage."
11474
+ logger.admonition("note", "Hint", [
11475
+ `Use \`playcademy bucket list --remote\` to see all available files`
10737
11476
  ]);
10738
11477
  logger.newLine();
10739
11478
  }
10740
11479
  process.exit(1);
10741
11480
  }
10742
- if (!key) {
11481
+ throw error;
11482
+ }
11483
+ if (options.json) {
11484
+ logger.json({
11485
+ key,
11486
+ size: arrayBuffer.byteLength,
11487
+ message: "Use --output to save file"
11488
+ });
11489
+ return;
11490
+ }
11491
+ if (options.output) {
11492
+ writeFileSync10(options.output, Buffer.from(arrayBuffer));
11493
+ if (!options.raw) {
11494
+ logger.newLine();
11495
+ logger.success(`Downloaded '${key}' from ${environment} to '${options.output}'`);
11496
+ logger.newLine();
11497
+ logger.data("Size", `${arrayBuffer.byteLength} bytes`, 1);
11498
+ logger.newLine();
11499
+ }
11500
+ return;
11501
+ }
11502
+ if (options.raw) {
11503
+ process.stdout.write(Buffer.from(arrayBuffer));
11504
+ return;
11505
+ }
11506
+ logger.newLine();
11507
+ logger.success(`File: ${key}`);
11508
+ logger.newLine();
11509
+ logger.data("Size", `${arrayBuffer.byteLength} bytes`, 1);
11510
+ logger.data("Environment", environment, 1);
11511
+ logger.newLine();
11512
+ logger.admonition("note", "Download File", [
11513
+ `Use \`playcademy bucket get ${key} --output path/to/file --remote\` to download`
11514
+ ]);
11515
+ logger.newLine();
11516
+ }
11517
+ async function runBucketGetLocal(key, options) {
11518
+ if (!key) {
11519
+ if (!options.raw && !options.json) {
11520
+ logger.error("File key is required");
11521
+ logger.newLine();
11522
+ logger.admonition("note", "Usage", [
11523
+ "`playcademy bucket get <key> --output path/to/file`"
11524
+ ]);
11525
+ logger.newLine();
11526
+ }
11527
+ process.exit(1);
11528
+ }
11529
+ const config = await loadConfig();
11530
+ if (!hasBucketSetup(config)) {
11531
+ if (!options.raw && !options.json) {
11532
+ logger.error("Bucket storage is not configured");
11533
+ logger.newLine();
11534
+ logger.admonition("tip", "Getting Started", [
11535
+ "Run `playcademy bucket init` to enable bucket storage"
11536
+ ]);
11537
+ logger.newLine();
11538
+ }
11539
+ process.exit(1);
11540
+ }
11541
+ const bucketDir = join37(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11542
+ const mf = new Miniflare14({
11543
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11544
+ r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
11545
+ r2Persist: bucketDir,
11546
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11547
+ });
11548
+ try {
11549
+ const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET);
11550
+ const object = await bucket.get(key);
11551
+ if (!object) {
10743
11552
  if (!options.raw && !options.json) {
10744
- logger.error("File key is required");
11553
+ logger.warn(`Failed to get object: File '${key}' not found`);
10745
11554
  logger.newLine();
10746
- logger.admonition("tip", "Usage", ["`playcademy bucket get <key> --output <file>`"]);
11555
+ logger.admonition("tip", "Hint", [
11556
+ "Use `playcademy bucket list` to see all available files"
11557
+ ]);
10747
11558
  logger.newLine();
10748
11559
  }
10749
11560
  process.exit(1);
10750
11561
  }
10751
- const config = await loadConfig();
10752
- if (!hasBucketSetup(config)) {
10753
- if (!options.raw && !options.json) {
10754
- logger.error("Bucket storage is not configured");
11562
+ if (options.json) {
11563
+ const metadata = {
11564
+ key: object.key,
11565
+ size: object.size,
11566
+ uploaded: object.uploaded.toISOString(),
11567
+ httpMetadata: object.httpMetadata,
11568
+ customMetadata: object.customMetadata
11569
+ };
11570
+ logger.json(metadata);
11571
+ return;
11572
+ }
11573
+ if (options.output) {
11574
+ const buffer = await object.arrayBuffer();
11575
+ writeFileSync10(options.output, Buffer.from(buffer));
11576
+ if (!options.raw) {
11577
+ logger.success(`Downloaded '${key}' to '${options.output}'`);
10755
11578
  logger.newLine();
10756
- logger.admonition("tip", "Getting Started", [
10757
- "Run `playcademy bucket init` to enable bucket storage"
10758
- ]);
11579
+ logger.data("Size", `${object.size} bytes`, 1);
11580
+ logger.data("Content-Type", object.httpMetadata?.contentType || "unknown", 1);
10759
11581
  logger.newLine();
10760
11582
  }
10761
- process.exit(1);
11583
+ return;
10762
11584
  }
10763
- const bucketDir = join33(getWorkspace(), CLI_DIRECTORIES.BUCKET);
10764
- const mf = new Miniflare12({
10765
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10766
- r2Buckets: ["BUCKET"],
10767
- r2Persist: bucketDir,
10768
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10769
- });
10770
- try {
10771
- const bucket = await mf.getR2Bucket("BUCKET");
10772
- const object = await bucket.get(key);
10773
- if (!object) {
10774
- if (!options.raw && !options.json) {
10775
- logger.warn(`File '${key}' not found`);
10776
- logger.newLine();
10777
- logger.admonition("tip", "Hint", [
10778
- "Use `playcademy bucket list` to see all available files"
10779
- ]);
10780
- logger.newLine();
10781
- }
10782
- process.exit(1);
10783
- }
10784
- if (options.json) {
10785
- const metadata = {
10786
- key: object.key,
10787
- size: object.size,
10788
- uploaded: object.uploaded.toISOString(),
10789
- httpMetadata: object.httpMetadata,
10790
- customMetadata: object.customMetadata
10791
- };
10792
- logger.json(metadata);
10793
- return;
10794
- }
10795
- if (options.output) {
10796
- const buffer = await object.arrayBuffer();
10797
- writeFileSync10(options.output, Buffer.from(buffer));
10798
- if (!options.raw) {
10799
- logger.success(`Downloaded '${key}' to '${options.output}'`);
10800
- logger.newLine();
10801
- logger.data("Size", `${object.size} bytes`, 1);
10802
- logger.data("Content-Type", object.httpMetadata?.contentType || "unknown", 1);
10803
- logger.newLine();
10804
- }
10805
- return;
10806
- }
10807
- if (options.raw) {
10808
- const text5 = await object.text();
10809
- logger.raw(text5);
10810
- return;
10811
- }
10812
- logger.success(`File: ${key}`);
10813
- logger.newLine();
10814
- logger.data("Size", `${object.size} bytes`, 1);
10815
- logger.data("Content-Type", object.httpMetadata?.contentType || "unknown", 1);
10816
- logger.data("Uploaded", new Date(object.uploaded).toLocaleString(), 1);
10817
- logger.newLine();
10818
- logger.admonition("tip", "Download File", [
10819
- `Use \`playcademy bucket get ${key} --output <file>\` to download`
10820
- ]);
11585
+ if (options.raw) {
11586
+ const text5 = await object.text();
11587
+ logger.raw(text5);
11588
+ return;
11589
+ }
11590
+ logger.success(`File: ${key}`);
11591
+ logger.newLine();
11592
+ logger.data("Size", `${object.size} bytes`, 1);
11593
+ logger.data("Content-Type", object.httpMetadata?.contentType || "unknown", 1);
11594
+ logger.data("Uploaded", new Date(object.uploaded).toLocaleString(), 1);
11595
+ logger.newLine();
11596
+ logger.admonition("note", "Download File", [
11597
+ `Use \`playcademy bucket get ${key} --output path/to/file\` to download`
11598
+ ]);
11599
+ logger.newLine();
11600
+ } finally {
11601
+ await mf.dispose();
11602
+ }
11603
+ }
11604
+ async function runBucketGet(key, options = {}) {
11605
+ try {
11606
+ if (!options.raw && !options.json) {
10821
11607
  logger.newLine();
10822
- } finally {
10823
- await mf.dispose();
11608
+ }
11609
+ if (options.remote) {
11610
+ await runBucketGetRemote(key, options);
11611
+ } else {
11612
+ await runBucketGetLocal(key, options);
10824
11613
  }
10825
11614
  } catch (error) {
10826
11615
  if (!options.raw && !options.json) {
@@ -10835,7 +11624,7 @@ async function runBucketGet(key, options = {}) {
10835
11624
 
10836
11625
  // src/commands/bucket/init.ts
10837
11626
  init_constants2();
10838
- import { input as input8 } from "@inquirer/prompts";
11627
+ import { input as input10 } from "@inquirer/prompts";
10839
11628
  init_writer();
10840
11629
  init_loader2();
10841
11630
  var sampleCustomRouteTemplate4 = loadTemplateString("api/sample-custom.ts");
@@ -10868,7 +11657,7 @@ async function runBucketInit() {
10868
11657
  logger.success("Added bucket integration to config");
10869
11658
  logger.newLine();
10870
11659
  if (!hasLocalCustomRoutes(getWorkspace(), config)) {
10871
- const apiDirectory = await input8({
11660
+ const apiDirectory = await input10({
10872
11661
  message: " \u2514\u2500 API routes directory:",
10873
11662
  default: DEFAULT_API_ROUTES_DIRECTORY,
10874
11663
  validate: (value) => {
@@ -10909,94 +11698,140 @@ async function runBucketInit() {
10909
11698
 
10910
11699
  // src/commands/bucket/list.ts
10911
11700
  init_constants2();
10912
- import { join as join34 } from "path";
10913
- import { Miniflare as Miniflare13 } from "miniflare";
10914
- async function runBucketList(options = {}) {
10915
- try {
11701
+ import { join as join38 } from "path";
11702
+ import { Miniflare as Miniflare15 } from "miniflare";
11703
+ async function runBucketListRemote(options) {
11704
+ const environment = ensureEnvironment(options.env);
11705
+ const client = await requireAuthenticatedClient();
11706
+ const workspace = getWorkspace();
11707
+ const deployedGame = await getDeployedGame(workspace);
11708
+ if (!deployedGame) {
10916
11709
  if (!options.raw && !options.json) {
11710
+ logger.admonition("warning", "Deploy First", [
11711
+ `Deploy your game before accessing remote bucket: \`playcademy deploy\``
11712
+ ]);
10917
11713
  logger.newLine();
10918
11714
  }
10919
- if (options.remote) {
10920
- if (!options.raw && !options.json) {
10921
- logger.newLine();
10922
- logger.warn("Remote bucket operations are not yet implemented");
10923
- logger.newLine();
10924
- logger.admonition("info", "Coming Soon", [
10925
- "Remote bucket support is on the roadmap and will be available soon.",
10926
- "For now, bucket commands only work with local development storage."
10927
- ]);
10928
- logger.newLine();
10929
- }
10930
- process.exit(1);
11715
+ process.exit(1);
11716
+ }
11717
+ const game = await client.games.fetch(deployedGame.gameId);
11718
+ const files = await client.dev.games.bucket.list(game.slug, options.prefix);
11719
+ if (options.json) {
11720
+ logger.json(files);
11721
+ return;
11722
+ }
11723
+ if (options.raw) {
11724
+ for (const file of files) {
11725
+ logger.raw(file.key);
10931
11726
  }
10932
- const config = await loadConfig();
10933
- if (!hasBucketSetup(config)) {
10934
- if (!options.raw && !options.json) {
10935
- logger.error("Bucket storage is not configured");
10936
- logger.newLine();
10937
- logger.admonition("tip", "Getting Started", [
10938
- "Run `playcademy bucket init` to enable bucket storage"
10939
- ]);
10940
- logger.newLine();
10941
- }
10942
- process.exit(1);
11727
+ return;
11728
+ }
11729
+ if (!options.raw && !options.json) {
11730
+ logger.newLine();
11731
+ }
11732
+ if (files.length === 0) {
11733
+ logger.remark("No files found in remote bucket");
11734
+ if (options.prefix) {
11735
+ logger.newLine();
11736
+ logger.data("Prefix", options.prefix, 1);
10943
11737
  }
10944
- const bucketDir = join34(getWorkspace(), CLI_DIRECTORIES.BUCKET);
10945
- const mf = new Miniflare13({
10946
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
10947
- r2Buckets: ["BUCKET"],
10948
- r2Persist: bucketDir,
10949
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
10950
- });
10951
- try {
10952
- const bucket = await mf.getR2Bucket("BUCKET");
10953
- const listed = await bucket.list({ prefix: options.prefix });
10954
- const files = listed.objects;
10955
- if (options.json) {
10956
- const fileData = files.map((obj) => ({
10957
- key: obj.key,
10958
- size: obj.size,
10959
- uploaded: obj.uploaded.toISOString()
10960
- }));
10961
- logger.json(fileData);
10962
- return;
10963
- }
10964
- if (options.raw) {
10965
- for (const file of files) {
10966
- logger.raw(file.key);
10967
- }
10968
- return;
10969
- }
10970
- if (files.length === 0) {
10971
- logger.remark("No files found in bucket");
10972
- if (options.prefix) {
10973
- logger.newLine();
10974
- logger.data("Prefix", options.prefix, 1);
10975
- }
10976
- logger.newLine();
10977
- return;
11738
+ logger.newLine();
11739
+ return;
11740
+ }
11741
+ logger.success(`Found ${files.length} file${files.length === 1 ? "" : "s"} in ${environment}`);
11742
+ if (options.prefix) {
11743
+ logger.data("Prefix", options.prefix, 1);
11744
+ }
11745
+ logger.newLine();
11746
+ logger.table(
11747
+ files.map((file) => ({
11748
+ Key: file.key,
11749
+ Size: formatBytes2(file.size),
11750
+ Uploaded: new Date(file.uploaded).toLocaleString()
11751
+ }))
11752
+ );
11753
+ logger.newLine();
11754
+ }
11755
+ async function runBucketListLocal(options) {
11756
+ const config = await loadConfig();
11757
+ if (!hasBucketSetup(config)) {
11758
+ if (!options.raw && !options.json) {
11759
+ logger.error("Bucket storage is not configured");
11760
+ logger.newLine();
11761
+ logger.admonition("tip", "Getting Started", [
11762
+ "Run `playcademy bucket init` to enable bucket storage"
11763
+ ]);
11764
+ logger.newLine();
11765
+ }
11766
+ process.exit(1);
11767
+ }
11768
+ const bucketDir = join38(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11769
+ const mf = new Miniflare15({
11770
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11771
+ r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
11772
+ r2Persist: bucketDir,
11773
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11774
+ });
11775
+ try {
11776
+ const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET);
11777
+ const listed = await bucket.list({ prefix: options.prefix });
11778
+ const files = listed.objects;
11779
+ if (options.json) {
11780
+ const fileData = files.map((obj) => ({
11781
+ key: obj.key,
11782
+ size: obj.size,
11783
+ uploaded: obj.uploaded.toISOString()
11784
+ }));
11785
+ logger.json(fileData);
11786
+ return;
11787
+ }
11788
+ if (options.raw) {
11789
+ for (const file of files) {
11790
+ logger.raw(file.key);
10978
11791
  }
10979
- logger.success(`Found ${files.length} file${files.length === 1 ? "" : "s"}`);
11792
+ return;
11793
+ }
11794
+ if (files.length === 0) {
11795
+ logger.remark("No files found in bucket");
10980
11796
  if (options.prefix) {
11797
+ logger.newLine();
10981
11798
  logger.data("Prefix", options.prefix, 1);
10982
11799
  }
10983
11800
  logger.newLine();
10984
- logger.table(
10985
- files.map((obj) => ({
10986
- Key: obj.key,
10987
- Size: formatBytes(obj.size),
10988
- Uploaded: new Date(obj.uploaded).toLocaleString()
10989
- }))
10990
- );
11801
+ return;
11802
+ }
11803
+ logger.success(`Found ${files.length} file${files.length === 1 ? "" : "s"}`);
11804
+ if (options.prefix) {
11805
+ logger.data("Prefix", options.prefix, 1);
11806
+ }
11807
+ logger.newLine();
11808
+ logger.table(
11809
+ files.map((obj) => ({
11810
+ Key: obj.key,
11811
+ Size: formatBytes2(obj.size),
11812
+ Uploaded: new Date(obj.uploaded).toLocaleString()
11813
+ }))
11814
+ );
11815
+ logger.newLine();
11816
+ if (listed.truncated) {
11817
+ logger.admonition("info", "Truncated Results", [
11818
+ "The list was truncated. Use --prefix to narrow down results."
11819
+ ]);
10991
11820
  logger.newLine();
10992
- if (listed.truncated) {
10993
- logger.admonition("info", "Truncated Results", [
10994
- "The list was truncated. Use --prefix to narrow down results."
10995
- ]);
10996
- logger.newLine();
10997
- }
10998
- } finally {
10999
- await mf.dispose();
11821
+ }
11822
+ } finally {
11823
+ await mf.dispose();
11824
+ }
11825
+ }
11826
+ async function runBucketList(options = {}) {
11827
+ try {
11828
+ if (!options.raw && !options.json) {
11829
+ logger.newLine();
11830
+ }
11831
+ if (options.remote) {
11832
+ await runBucketListRemote(options);
11833
+ } else {
11834
+ await runBucketListLocal(options);
11000
11835
  }
11001
11836
  } catch (error) {
11002
11837
  if (!options.raw && !options.json) {
@@ -11008,7 +11843,7 @@ async function runBucketList(options = {}) {
11008
11843
  process.exit(1);
11009
11844
  }
11010
11845
  }
11011
- function formatBytes(bytes) {
11846
+ function formatBytes2(bytes) {
11012
11847
  if (bytes === 0) return "0 B";
11013
11848
  const k = 1024;
11014
11849
  const sizes = ["B", "KB", "MB", "GB"];
@@ -11018,94 +11853,137 @@ function formatBytes(bytes) {
11018
11853
 
11019
11854
  // src/commands/bucket/put.ts
11020
11855
  init_constants2();
11021
- import { readFileSync as readFileSync8, statSync as statSync2 } from "fs";
11022
- import { join as join35 } from "path";
11023
- import { Miniflare as Miniflare14 } from "miniflare";
11024
- async function runBucketPut(key, filePath, options = {}) {
11856
+ import { readFileSync as readFileSync10, statSync as statSync4 } from "fs";
11857
+ import { join as join39 } from "path";
11858
+ import { Miniflare as Miniflare16 } from "miniflare";
11859
+ async function runBucketPutRemote(key, filePath, options) {
11860
+ const environment = ensureEnvironment(options.env);
11861
+ const client = await requireAuthenticatedClient();
11862
+ const workspace = getWorkspace();
11863
+ const deployedGame = await getDeployedGame(workspace);
11864
+ if (!deployedGame) {
11865
+ if (!options.raw && !options.json) {
11866
+ logger.admonition("warning", "Deploy First", [
11867
+ `Deploy your game before accessing remote bucket: \`playcademy deploy\``
11868
+ ]);
11869
+ logger.newLine();
11870
+ }
11871
+ process.exit(1);
11872
+ }
11873
+ let fileBuffer;
11874
+ let fileSize;
11025
11875
  try {
11876
+ fileBuffer = readFileSync10(filePath);
11877
+ fileSize = statSync4(filePath).size;
11878
+ } catch {
11026
11879
  if (!options.raw && !options.json) {
11880
+ logger.error(`File not found: ${filePath}`);
11027
11881
  logger.newLine();
11028
11882
  }
11029
- if (options.remote) {
11030
- if (!options.raw && !options.json) {
11031
- logger.newLine();
11032
- logger.warn("Remote bucket operations are not yet implemented");
11033
- logger.newLine();
11034
- logger.admonition("info", "Coming Soon", [
11035
- "Remote bucket support is on the roadmap and will be available soon.",
11036
- "For now, bucket commands only work with local development storage."
11037
- ]);
11038
- logger.newLine();
11039
- }
11040
- process.exit(1);
11883
+ process.exit(1);
11884
+ }
11885
+ const game = await client.games.fetch(deployedGame.gameId);
11886
+ const contentType = getContentType(filePath);
11887
+ await client.dev.games.bucket.put(game.slug, key, fileBuffer, contentType);
11888
+ if (options.json) {
11889
+ logger.json({
11890
+ success: true,
11891
+ key,
11892
+ size: fileSize,
11893
+ uploaded: (/* @__PURE__ */ new Date()).toISOString()
11894
+ });
11895
+ return;
11896
+ }
11897
+ if (options.raw) {
11898
+ logger.raw(`Uploaded ${key}`);
11899
+ return;
11900
+ }
11901
+ logger.newLine();
11902
+ logger.success(`Uploaded '${filePath}' to '${key}' in ${environment}`);
11903
+ logger.newLine();
11904
+ logger.data("Size", `${fileSize} bytes`, 1);
11905
+ logger.data("Content-Type", contentType, 1);
11906
+ logger.newLine();
11907
+ }
11908
+ async function runBucketPutLocal(key, filePath, options) {
11909
+ if (!key || !filePath) {
11910
+ if (!options.raw && !options.json) {
11911
+ logger.error("File key and path are required");
11912
+ logger.newLine();
11913
+ logger.admonition("tip", "Usage", ["`playcademy bucket put <key> <file>`"]);
11914
+ logger.newLine();
11041
11915
  }
11042
- if (!key || !filePath) {
11043
- if (!options.raw && !options.json) {
11044
- logger.error("File key and path are required");
11045
- logger.newLine();
11046
- logger.admonition("tip", "Usage", ["`playcademy bucket put <key> <file>`"]);
11047
- logger.newLine();
11048
- }
11049
- process.exit(1);
11916
+ process.exit(1);
11917
+ }
11918
+ const config = await loadConfig();
11919
+ if (!hasBucketSetup(config)) {
11920
+ if (!options.raw && !options.json) {
11921
+ logger.error("Bucket storage is not configured");
11922
+ logger.newLine();
11923
+ logger.admonition("tip", "Getting Started", [
11924
+ "Run `playcademy bucket init` to enable bucket storage"
11925
+ ]);
11926
+ logger.newLine();
11050
11927
  }
11051
- const config = await loadConfig();
11052
- if (!hasBucketSetup(config)) {
11053
- if (!options.raw && !options.json) {
11054
- logger.error("Bucket storage is not configured");
11055
- logger.newLine();
11056
- logger.admonition("tip", "Getting Started", [
11057
- "Run `playcademy bucket init` to enable bucket storage"
11058
- ]);
11059
- logger.newLine();
11060
- }
11061
- process.exit(1);
11928
+ process.exit(1);
11929
+ }
11930
+ let fileBuffer;
11931
+ let fileSize;
11932
+ try {
11933
+ fileBuffer = readFileSync10(filePath);
11934
+ fileSize = statSync4(filePath).size;
11935
+ } catch {
11936
+ if (!options.raw && !options.json) {
11937
+ logger.error(`File not found: ${filePath}`);
11938
+ logger.newLine();
11062
11939
  }
11063
- let fileBuffer;
11064
- let fileSize;
11065
- try {
11066
- fileBuffer = readFileSync8(filePath);
11067
- fileSize = statSync2(filePath).size;
11068
- } catch {
11069
- if (!options.raw && !options.json) {
11070
- logger.error(`File not found: ${filePath}`);
11071
- logger.newLine();
11940
+ process.exit(1);
11941
+ }
11942
+ const bucketDir = join39(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11943
+ const mf = new Miniflare16({
11944
+ modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11945
+ r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
11946
+ r2Persist: bucketDir,
11947
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11948
+ });
11949
+ try {
11950
+ const bucket = await mf.getR2Bucket(CLOUDFLARE_BINDINGS.BUCKET);
11951
+ await bucket.put(key, new Uint8Array(fileBuffer).buffer, {
11952
+ httpMetadata: {
11953
+ contentType: getContentType(filePath)
11072
11954
  }
11073
- process.exit(1);
11074
- }
11075
- const bucketDir = join35(getWorkspace(), CLI_DIRECTORIES.BUCKET);
11076
- const mf = new Miniflare14({
11077
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
11078
- r2Buckets: ["BUCKET"],
11079
- r2Persist: bucketDir,
11080
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
11081
11955
  });
11082
- try {
11083
- const bucket = await mf.getR2Bucket("BUCKET");
11084
- await bucket.put(key, new Uint8Array(fileBuffer).buffer, {
11085
- httpMetadata: {
11086
- contentType: getContentType(filePath)
11087
- }
11956
+ if (options.json) {
11957
+ logger.json({
11958
+ success: true,
11959
+ key,
11960
+ size: fileSize,
11961
+ uploaded: (/* @__PURE__ */ new Date()).toISOString()
11088
11962
  });
11089
- if (options.json) {
11090
- logger.json({
11091
- success: true,
11092
- key,
11093
- size: fileSize,
11094
- uploaded: (/* @__PURE__ */ new Date()).toISOString()
11095
- });
11096
- return;
11097
- }
11098
- if (options.raw) {
11099
- logger.raw(`Uploaded ${key}`);
11100
- return;
11101
- }
11102
- logger.success(`Uploaded '${filePath}' to '${key}'`);
11103
- logger.newLine();
11104
- logger.data("Size", `${fileSize} bytes`, 1);
11105
- logger.data("Content-Type", getContentType(filePath), 1);
11963
+ return;
11964
+ }
11965
+ if (options.raw) {
11966
+ logger.raw(`Uploaded ${key}`);
11967
+ return;
11968
+ }
11969
+ logger.success(`Uploaded '${filePath}' to '${key}'`);
11970
+ logger.newLine();
11971
+ logger.data("Size", `${fileSize} bytes`, 1);
11972
+ logger.data("Content-Type", getContentType(filePath), 1);
11973
+ logger.newLine();
11974
+ } finally {
11975
+ await mf.dispose();
11976
+ }
11977
+ }
11978
+ async function runBucketPut(key, filePath, options = {}) {
11979
+ try {
11980
+ if (!options.raw && !options.json) {
11106
11981
  logger.newLine();
11107
- } finally {
11108
- await mf.dispose();
11982
+ }
11983
+ if (options.remote) {
11984
+ await runBucketPutRemote(key, filePath, options);
11985
+ } else {
11986
+ await runBucketPutLocal(key, filePath, options);
11109
11987
  }
11110
11988
  } catch (error) {
11111
11989
  if (!options.raw && !options.json) {
@@ -11117,58 +11995,44 @@ async function runBucketPut(key, filePath, options = {}) {
11117
11995
  process.exit(1);
11118
11996
  }
11119
11997
  }
11120
- function getContentType(filePath) {
11121
- const ext = filePath.split(".").pop()?.toLowerCase();
11122
- const types = {
11123
- jpg: "image/jpeg",
11124
- jpeg: "image/jpeg",
11125
- png: "image/png",
11126
- gif: "image/gif",
11127
- webp: "image/webp",
11128
- svg: "image/svg+xml",
11129
- mp3: "audio/mpeg",
11130
- wav: "audio/wav",
11131
- ogg: "audio/ogg",
11132
- mp4: "video/mp4",
11133
- webm: "video/webm",
11134
- pdf: "application/pdf",
11135
- json: "application/json",
11136
- txt: "text/plain",
11137
- html: "text/html",
11138
- css: "text/css",
11139
- js: "application/javascript"
11140
- };
11141
- return types[ext || ""] || "application/octet-stream";
11142
- }
11143
11998
 
11144
11999
  // src/commands/bucket/index.ts
11145
12000
  var bucketCommand = new Command16("bucket").description("Manage bucket storage integration").action(() => {
11146
12001
  bucketCommand.help();
11147
12002
  });
11148
12003
  bucketCommand.command("init").description("Add bucket storage integration to your project").action(runBucketInit);
11149
- bucketCommand.command("list").alias("ls").description("List files in local bucket").option("--prefix <prefix>", "Filter files by key prefix").option("--raw", "Output file keys one per line, no formatting").option("--json", "Output as JSON array").option("--remote", "Use remote bucket (not yet implemented)").option(
12004
+ bucketCommand.command("list").alias("ls").description("List files in bucket").option("--prefix <prefix>", "Filter files by key prefix").option("--raw", "Output file keys one per line, no formatting").option("--json", "Output as JSON array").option("--remote", "Use remote bucket instead of local").option(
11150
12005
  "--env <environment>",
11151
12006
  "Environment to use with --remote: staging (default) or production"
11152
12007
  ).action(runBucketList);
11153
- bucketCommand.command("get <key>").description("Download a file from local bucket").option("-o, --output <path>", "Output file path (required for binary files)").option("--raw", "Output file content to stdout").option("--json", "Output metadata as JSON").option("--remote", "Use remote bucket (not yet implemented)").option(
12008
+ bucketCommand.command("get <key>").description("Download a file from bucket").option("-o, --output <path>", "Output file path (required for binary files)").option("--raw", "Output file content to stdout").option("--json", "Output metadata as JSON").option("--remote", "Use remote bucket instead of local").option(
11154
12009
  "--env <environment>",
11155
12010
  "Environment to use with --remote: staging (default) or production"
11156
12011
  ).action(runBucketGet);
11157
- bucketCommand.command("put <key> <file>").description("Upload a file to local bucket").option("--raw", "Output minimal confirmation").option("--json", "Output result as JSON").option("--remote", "Use remote bucket (not yet implemented)").option(
12012
+ bucketCommand.command("put <key> <file>").description("Upload a file to bucket").option("--raw", "Output minimal confirmation").option("--json", "Output result as JSON").option("--remote", "Use remote bucket instead of local").option(
11158
12013
  "--env <environment>",
11159
12014
  "Environment to use with --remote: staging (default) or production"
11160
12015
  ).action(runBucketPut);
11161
- bucketCommand.command("delete <key>").alias("del").alias("rm").description("Delete a file from local bucket").option("--raw", "Output minimal confirmation").option("--json", "Output result as JSON").option("--remote", "Use remote bucket (not yet implemented)").option(
12016
+ bucketCommand.command("delete <key>").alias("del").alias("rm").description("Delete a file from bucket").option("--raw", "Output minimal confirmation").option("--json", "Output result as JSON").option("--remote", "Use remote bucket instead of local").option(
11162
12017
  "--env <environment>",
11163
12018
  "Environment to use with --remote: staging (default) or production"
11164
12019
  ).action(runBucketDelete);
12020
+ bucketCommand.command("bulk <directory>").description(
12021
+ "Upload directory contents to bucket (preserves subdirectory structure, strips source directory name)"
12022
+ ).option(
12023
+ "--prefix <prefix>",
12024
+ "Prefix for all uploaded keys (use to include source dir: --prefix assets)"
12025
+ ).option("--dry-run", "Show what would be uploaded without uploading").option("--raw", "Output minimal confirmation").option("--json", "Output result as JSON").option("--remote", "Use remote bucket instead of local").option(
12026
+ "--env <environment>",
12027
+ "Environment to use with --remote: staging (default) or production"
12028
+ ).action(runBucketBulk);
11165
12029
 
11166
12030
  // src/commands/secret/index.ts
11167
12031
  import { Command as Command20 } from "commander";
11168
12032
 
11169
12033
  // src/commands/secret/delete.ts
11170
12034
  init_src();
11171
- import { confirm as confirm9 } from "@inquirer/prompts";
12035
+ import { confirm as confirm10 } from "@inquirer/prompts";
11172
12036
  import { Command as Command17 } from "commander";
11173
12037
  var deleteCommand2 = new Command17("delete").description("Delete a game secret").argument("<key>", "Secret key to delete").option("--env <environment>", "Environment (staging or production)").option("-f, --force", "Skip confirmation").action(async (key, options) => {
11174
12038
  const { env } = options;
@@ -11188,7 +12052,7 @@ var deleteCommand2 = new Command17("delete").description("Delete a game secret")
11188
12052
  }
11189
12053
  const game = await client.games.fetch(deployedGame.gameId);
11190
12054
  if (!options.force) {
11191
- const confirmed = await confirm9({
12055
+ const confirmed = await confirm10({
11192
12056
  message: `Delete secret "${key}" from "${game.slug}" in ${environment}?`,
11193
12057
  default: false
11194
12058
  });
@@ -11354,7 +12218,7 @@ async function listProfilesAction() {
11354
12218
  var listCommand3 = new Command22("list").alias("ls").description("List all stored authentication profiles").action(listProfilesAction);
11355
12219
 
11356
12220
  // src/commands/profiles/remove.ts
11357
- import { bold as bold6 } from "colorette";
12221
+ import { bold as bold8 } from "colorette";
11358
12222
  import { Command as Command23 } from "commander";
11359
12223
  var removeCommand = new Command23("remove").alias("rm").description('Remove an authentication profile (defaults to "default")').argument("[name]", "Profile name to remove", "default").option("--env <environment>", "Environment to remove profile from (staging or production)").action(async (name, options) => {
11360
12224
  const { env } = options;
@@ -11370,7 +12234,7 @@ var removeCommand = new Command23("remove").alias("rm").description('Remove an a
11370
12234
  }
11371
12235
  await removeProfile(environment, name);
11372
12236
  logger.admonition("note", "Removed!", [
11373
- `Profile ${bold6(name)} removed from ${environment}`,
12237
+ `Profile ${bold8(name)} removed from ${environment}`,
11374
12238
  environment === "production" ? `To re-authenticate run \`playcademy login --env ${environment}\`` : "To re-authenticate run `playcademy login`"
11375
12239
  ]);
11376
12240
  logger.newLine();
@@ -11381,7 +12245,7 @@ var removeCommand = new Command23("remove").alias("rm").description('Remove an a
11381
12245
 
11382
12246
  // src/commands/profiles/reset.ts
11383
12247
  init_string();
11384
- import { confirm as confirm10 } from "@inquirer/prompts";
12248
+ import { confirm as confirm11 } from "@inquirer/prompts";
11385
12249
  import { Command as Command24 } from "commander";
11386
12250
  var resetCommand = new Command24("reset").description(
11387
12251
  "Remove all authentication profiles across all environments (requires confirmation)"
@@ -11419,7 +12283,7 @@ var resetCommand = new Command24("reset").description(
11419
12283
  logger.newLine();
11420
12284
  }
11421
12285
  }
11422
- const confirmed = await confirm10({
12286
+ const confirmed = await confirm11({
11423
12287
  message: "Are you sure you want to remove all profiles?",
11424
12288
  default: false
11425
12289
  });
@@ -11469,7 +12333,7 @@ import { Command as Command31 } from "commander";
11469
12333
 
11470
12334
  // src/commands/timeback/cleanup.ts
11471
12335
  init_src();
11472
- import { confirm as confirm11 } from "@inquirer/prompts";
12336
+ import { confirm as confirm12 } from "@inquirer/prompts";
11473
12337
  import { Command as Command26 } from "commander";
11474
12338
  var cleanupCommand = new Command26("cleanup").description("Remove TimeBack integration for your game").option(
11475
12339
  "--env <environment>",
@@ -11493,7 +12357,7 @@ var cleanupCommand = new Command26("cleanup").description("Remove TimeBack integ
11493
12357
  return;
11494
12358
  }
11495
12359
  displayCleanupWarning(integration, game.displayName, logger);
11496
- const confirmed = await confirm11({
12360
+ const confirmed = await confirm12({
11497
12361
  message: "Are you sure you want to remove TimeBack integration?",
11498
12362
  default: false
11499
12363
  });
@@ -11669,7 +12533,7 @@ var setupCommand = new Command28("setup").description("Set up TimeBack integrati
11669
12533
  // src/commands/timeback/update.ts
11670
12534
  init_src();
11671
12535
  init_string();
11672
- import { confirm as confirm12 } from "@inquirer/prompts";
12536
+ import { confirm as confirm13 } from "@inquirer/prompts";
11673
12537
  import { green as green4, red as red3 } from "colorette";
11674
12538
  import { Command as Command29 } from "commander";
11675
12539
  var updateCommand = new Command29("update").description("Update TimeBack integration configuration for your game").option("--verbose, -v", "Output detailed information").option(
@@ -11750,7 +12614,7 @@ var updateCommand = new Command29("update").description("Update TimeBack integra
11750
12614
  logger.data(change.label, `${red3(change.current)} \u2192 ${green4(change.next)}`, 1);
11751
12615
  }
11752
12616
  logger.newLine();
11753
- const confirmed = await confirm12({
12617
+ const confirmed = await confirm13({
11754
12618
  message: `Update ${changeDetails.length} ${pluralize(changeDetails.length, "field")} in TimeBack?`,
11755
12619
  default: false
11756
12620
  });
@@ -11857,7 +12721,7 @@ import { Command as Command33 } from "commander";
11857
12721
  init_src();
11858
12722
  init_constants2();
11859
12723
  import { writeFileSync as writeFileSync11 } from "fs";
11860
- import { join as join36 } from "path";
12724
+ import { join as join40 } from "path";
11861
12725
  import { Command as Command32 } from "commander";
11862
12726
  var bundleCommand = new Command32("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) => {
11863
12727
  try {
@@ -11887,7 +12751,7 @@ var bundleCommand = new Command32("bundle").description("Bundle and inspect the
11887
12751
  }),
11888
12752
  (result) => `Bundled ${formatSize(result.code.length)}`
11889
12753
  );
11890
- const outputPath = join36(workspace, options.output);
12754
+ const outputPath = join40(workspace, options.output);
11891
12755
  writeFileSync11(outputPath, bundle.code, "utf-8");
11892
12756
  logger.success(`Bundle saved to ${options.output}`);
11893
12757
  logger.newLine();
@@ -11963,9 +12827,12 @@ export {
11963
12827
  REQUIRED_FIELDS,
11964
12828
  analyzeChanges,
11965
12829
  bundleBackend,
12830
+ bundleSeedWorker,
11966
12831
  calculateConfigDiff,
11967
12832
  calculateDeploymentPlan,
11968
12833
  checkTimebackSetup,
12834
+ collectBulkFiles,
12835
+ collectFiles,
11969
12836
  compareIntegrationKeys,
11970
12837
  confirmDeploymentPlan,
11971
12838
  createClient,
@@ -11990,7 +12857,9 @@ export {
11990
12857
  ensurePlaycademyGitignore,
11991
12858
  ensurePlaycademyTypes,
11992
12859
  ensureRootGitignore,
12860
+ executeSeedFile,
11993
12861
  findConfigPath,
12862
+ formatBytes,
11994
12863
  formatSize,
11995
12864
  generateEntryCode,
11996
12865
  generateJsConfig,
@@ -12001,8 +12870,10 @@ export {
12001
12870
  getBackendSize,
12002
12871
  getBaseUrl,
12003
12872
  getBestUnit,
12873
+ getBucketKey,
12004
12874
  getCallbackUrl,
12005
12875
  getCliContext,
12876
+ getContentType,
12006
12877
  getCurrentProfile,
12007
12878
  getCustomRoutesDirectory,
12008
12879
  getCustomRoutesHash,
@@ -12040,18 +12911,25 @@ export {
12040
12911
  hashContent,
12041
12912
  hashDirectory,
12042
12913
  hashFile,
12914
+ importSeedModule,
12043
12915
  importTypescriptDefault,
12044
12916
  importTypescriptFile,
12045
12917
  integrationChangeDetectors,
12918
+ isIgnoredByGitignore,
12046
12919
  listProfiles,
12047
12920
  loadAuthStore,
12048
12921
  loadConfig,
12049
12922
  loadDeployConfig,
12050
12923
  loadGameStore,
12924
+ loadGitignorePatterns,
12051
12925
  logAndExit,
12052
12926
  logger,
12927
+ matchesGitignorePattern,
12053
12928
  needsBackend,
12054
12929
  normalizeEnvironment,
12930
+ normalizeGitignoreEntry,
12931
+ outputDryRunResults,
12932
+ outputUploadResults,
12055
12933
  prepareDeploymentContext,
12056
12934
  processConfigVariables,
12057
12935
  promptForGameInfo,
@@ -12078,11 +12956,14 @@ export {
12078
12956
  selectConfigFormat,
12079
12957
  selectEnvironment,
12080
12958
  setCliContext,
12959
+ shouldSkipFile,
12081
12960
  startCallbackServer,
12082
12961
  startDevServer,
12083
12962
  startHotReload,
12084
12963
  timebackChangeDetector,
12085
12964
  updateConfigFile,
12965
+ uploadFilesLocal,
12966
+ uploadFilesRemote,
12086
12967
  validateApiDirectoryDoesNotExist,
12087
12968
  validateBuildPath,
12088
12969
  validateConfig,