vela 0.11.4 → 0.11.5

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/bin.js CHANGED
@@ -21,7 +21,7 @@ import pc38 from "picocolors";
21
21
  // package.json
22
22
  var package_default = {
23
23
  name: "vela",
24
- version: "0.11.4",
24
+ version: "0.11.5",
25
25
  type: "module",
26
26
  description: "A CLI for creating and updating SvelteKit projects",
27
27
  license: "MIT",
@@ -57,7 +57,7 @@ var package_default = {
57
57
  dependencies: {
58
58
  "@clack/prompts": "^1.7.0",
59
59
  "@faker-js/faker": "^10.6.0",
60
- "@velastack/patterns": "^0.2.3",
60
+ "@velastack/patterns": "^0.2.4",
61
61
  "@velastack/pocketbase-codegen": "^0.1.0",
62
62
  "annotate-json-schema": "^0.1.0",
63
63
  commander: "^13.1.0",
@@ -83,10 +83,10 @@ var package_default = {
83
83
  "@types/node": "^22.0.0",
84
84
  esbuild: "^0.28.2",
85
85
  prettier: "^3.9.6",
86
+ shellcheck: "^4.1.0",
86
87
  typescript: "^5.6.0",
87
88
  vite: "^8.2.2",
88
- vitest: "^4.1.11",
89
- shellcheck: "^4.1.0"
89
+ vitest: "^4.1.11"
90
90
  },
91
91
  peerDependencies: {
92
92
  "@sveltejs/kit": "^2.57.1",
@@ -100,6 +100,18 @@ var package_default = {
100
100
  optional: true
101
101
  }
102
102
  },
103
+ overrides: {
104
+ "global-agent": "^4.1.3",
105
+ "@xhmikosr/decompress-unzip": {
106
+ "file-type": "^21.3.1"
107
+ },
108
+ "@xhmikosr/decompress-tar": {
109
+ "file-type": "^21.3.1"
110
+ },
111
+ "@felipecrs/decompress-tarxz": {
112
+ "file-type": "^21.3.1"
113
+ }
114
+ },
103
115
  keywords: [
104
116
  "svelte",
105
117
  "sveltekit",
@@ -291,8 +303,20 @@ function mergePackageJson(user, template) {
291
303
  }
292
304
  return { merged, added, conflicts, replaced };
293
305
  }
294
- function readPackageJson(path46) {
295
- return JSON.parse(fs.readFileSync(path46, "utf8"));
306
+ function dropTemplateAdapters(user, template) {
307
+ const isAdapter = (name) => name.startsWith("@sveltejs/adapter-");
308
+ const userHasOne = DEP_KINDS.some((kind) => Object.keys(user[kind] ?? {}).some(isAdapter));
309
+ if (!userHasOne) return template;
310
+ const copy = { ...template };
311
+ for (const kind of DEP_KINDS) {
312
+ const deps = template[kind];
313
+ if (!deps) continue;
314
+ copy[kind] = Object.fromEntries(Object.entries(deps).filter(([name]) => !isAdapter(name)));
315
+ }
316
+ return copy;
317
+ }
318
+ function readPackageJson(path47) {
319
+ return JSON.parse(fs.readFileSync(path47, "utf8"));
296
320
  }
297
321
  var PACKAGE_NAME_PLACEHOLDER = /~TODO~/g;
298
322
  var APP_NAME_PLACEHOLDER = /~APP_NAME~/g;
@@ -305,12 +329,12 @@ function fillTemplatePlaceholders(raw, values) {
305
329
  function escapeSingleQuoted(value) {
306
330
  return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
307
331
  }
308
- function readTemplatePackageJson(path46, values) {
309
- const raw = fillTemplatePlaceholders(fs.readFileSync(path46, "utf8"), values);
332
+ function readTemplatePackageJson(path47, values) {
333
+ const raw = fillTemplatePlaceholders(fs.readFileSync(path47, "utf8"), values);
310
334
  return JSON.parse(raw);
311
335
  }
312
- function writePackageJson(path46, pkg) {
313
- fs.writeFileSync(path46, JSON.stringify(pkg, null, " ") + "\n");
336
+ function writePackageJson(path47, pkg) {
337
+ fs.writeFileSync(path47, JSON.stringify(pkg, null, " ") + "\n");
314
338
  }
315
339
  function toValidPackageName(name) {
316
340
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z0-9~.-]+/g, "-");
@@ -866,7 +890,7 @@ async function packageManagerPrompt(cwd) {
866
890
  }
867
891
  return pm;
868
892
  }
869
- async function installDependencies(agent, cwd) {
893
+ async function installDependencies(agent, cwd, { exitOnFailure = true } = {}) {
870
894
  const task = p2.taskLog({
871
895
  title: `Installing dependencies with ${agent}...`,
872
896
  limit: Math.ceil(process5.stdout.rows / 2),
@@ -883,14 +907,16 @@ async function installDependencies(agent, cwd) {
883
907
  proc.process?.stderr?.on("data", (data) => task.message(data.toString(), { raw: true }));
884
908
  await proc;
885
909
  task.success("Successfully installed dependencies");
910
+ return true;
886
911
  } catch {
887
912
  task.error("Failed to install dependencies");
913
+ if (!exitOnFailure) return false;
888
914
  p2.cancel("Operation failed.");
889
915
  process5.exit(2);
890
916
  }
891
917
  }
892
- function addPnpmBuildDependencies(cwd, packageManager, allowedPackages) {
893
- if (!packageManager || packageManager !== "pnpm") return;
918
+ function addPnpmBuildDependencies(cwd, packageManager2, allowedPackages) {
919
+ if (!packageManager2 || packageManager2 !== "pnpm") return;
894
920
  const pkgPath = path6.join(cwd, "package.json");
895
921
  if (!fs7.existsSync(pkgPath)) return;
896
922
  const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
@@ -1027,11 +1053,11 @@ function getPocketbaseMetadata(cwd) {
1027
1053
  return null;
1028
1054
  }
1029
1055
  async function execPackageBin(cwd, args, stdio = "pipe") {
1030
- const packageManager = (await detect2({ cwd }))?.name ?? "npm";
1031
- const resolved = resolveCommand(packageManager, "execute", args);
1032
- if (!resolved) throw new Error(`Could not resolve command for ${packageManager}`);
1056
+ const packageManager2 = (await detect2({ cwd }))?.name ?? "npm";
1057
+ const resolved = resolveCommand(packageManager2, "execute", args);
1058
+ if (!resolved) throw new Error(`Could not resolve command for ${packageManager2}`);
1033
1059
  const { command, args: resolvedArgs } = resolved;
1034
- if (packageManager === "npm") resolvedArgs.unshift("--yes");
1060
+ if (packageManager2 === "npm") resolvedArgs.unshift("--yes");
1035
1061
  return x(command, resolvedArgs, { nodeOptions: { cwd, stdio }, throwOnError: true });
1036
1062
  }
1037
1063
  async function withPocketbase(cwd, fn, creds) {
@@ -1479,7 +1505,14 @@ var GITIGNORE_ENTRIES = [
1479
1505
  "!.env.example",
1480
1506
  "!.env.test",
1481
1507
  "vite.config.js.timestamp-*",
1482
- "vite.config.ts.timestamp-*"
1508
+ "vite.config.ts.timestamp-*",
1509
+ // The local database, minus the parts that are source. Same block as the
1510
+ // template's _gitignore; without it a blessed project commits its SQLite.
1511
+ "/data/*",
1512
+ "!/data/fixtures",
1513
+ "!/data/seeds",
1514
+ "!/data/hooks",
1515
+ "/backups"
1483
1516
  ];
1484
1517
  function mergeGitignore(filePath) {
1485
1518
  const existing = fs13.existsSync(filePath) ? fs13.readFileSync(filePath, "utf8") : "";
@@ -1647,13 +1680,13 @@ async function blessProject(cwdArg, options) {
1647
1680
  mergeAppDts(templateDir, projectPath);
1648
1681
  maybeReplaceRoutes(projectPath, templateDir, options);
1649
1682
  p4.log.success("Vela files in place");
1650
- let packageManager;
1683
+ let packageManager2;
1651
1684
  if (options.install !== false) {
1652
1685
  const pm = typeof options.install === "string" ? options.install : await packageManagerPrompt(projectPath);
1653
1686
  if (pm) {
1654
1687
  addPnpmBuildDependencies(projectPath, pm, ["esbuild", "pocketbase-server"]);
1655
1688
  await installDependencies(pm, projectPath);
1656
- packageManager = pm;
1689
+ packageManager2 = pm;
1657
1690
  }
1658
1691
  }
1659
1692
  p4.log.step("Initializing PocketBase...");
@@ -1667,7 +1700,7 @@ async function blessProject(cwdArg, options) {
1667
1700
  ["PocketBase superuser credentials \u2014 used by `vela` commands"]
1668
1701
  );
1669
1702
  p4.log.success("PocketBase initialized");
1670
- printNextSteps(projectPath, packageManager);
1703
+ printNextSteps(projectPath, packageManager2);
1671
1704
  }
1672
1705
  function ensureShadcnCss(projectPath) {
1673
1706
  if (ensureShadcnImport(path13.join(projectPath, "src", "app.css"))) {
@@ -1713,7 +1746,10 @@ function mergeDependencies(projectPath, templateDir) {
1713
1746
  appName,
1714
1747
  cliVersion: package_default.version
1715
1748
  });
1716
- const { merged, added, conflicts, replaced } = mergePackageJson(userPkg, templatePkg);
1749
+ const { merged, added, conflicts, replaced } = mergePackageJson(
1750
+ userPkg,
1751
+ dropTemplateAdapters(userPkg, templatePkg)
1752
+ );
1717
1753
  writePackageJson(userPkgPath, merged);
1718
1754
  if (added.length > 0) {
1719
1755
  p4.log.info(
@@ -1851,9 +1887,9 @@ function summarize(names) {
1851
1887
  if (names.length <= 4) return names.join(", ");
1852
1888
  return `${names.slice(0, 3).join(", ")}, and ${names.length - 3} more`;
1853
1889
  }
1854
- function printNextSteps(projectPath, packageManager) {
1890
+ function printNextSteps(projectPath, packageManager2) {
1855
1891
  const relative = path13.relative(process7.cwd(), projectPath);
1856
- const pm = packageManager ?? getUserAgent() ?? "npm";
1892
+ const pm = packageManager2 ?? getUserAgent() ?? "npm";
1857
1893
  const nextSteps = [];
1858
1894
  if (relative !== "") {
1859
1895
  const hasSpaces = relative.includes(" ");
@@ -1901,19 +1937,19 @@ var create = new Command3("create").description("scaffold a new velastack projec
1901
1937
  return runCommand(async () => {
1902
1938
  const listing = await listAllTemplates();
1903
1939
  const options = parseOptions(optionsSchema2(listing), rawOpts);
1904
- const { directory, packageManager, name, template } = await createProject(
1940
+ const { directory, packageManager: packageManager2, name, template } = await createProject(
1905
1941
  projectPath,
1906
1942
  options,
1907
1943
  listing
1908
1944
  );
1909
1945
  const relative = path14.relative(process8.cwd(), directory);
1910
- const pm = packageManager ?? (await detect3({ cwd: directory }))?.name ?? getUserAgent() ?? "npm";
1946
+ const pm = packageManager2 ?? (await detect3({ cwd: directory }))?.name ?? getUserAgent() ?? "npm";
1911
1947
  const nextSteps = [];
1912
1948
  if (relative !== "") {
1913
1949
  const hasSpaces = relative.includes(" ");
1914
1950
  nextSteps.push(`\`cd ${hasSpaces ? `"${relative}"` : relative}\``);
1915
1951
  }
1916
- if (!packageManager) {
1952
+ if (!packageManager2) {
1917
1953
  const resolved = resolveCommand3(pm, "install", []);
1918
1954
  if (resolved) {
1919
1955
  nextSteps.push(
@@ -2002,14 +2038,14 @@ async function createProject(cwdArg, options, listing) {
2002
2038
  throw new Error(`Template ${template.name} is missing package.template.json`);
2003
2039
  }
2004
2040
  p5.log.success("Project created");
2005
- let packageManager;
2041
+ let packageManager2;
2006
2042
  if (options.install !== false) {
2007
2043
  const pm = typeof options.install === "string" ? options.install : await packageManagerPrompt(projectPath);
2008
2044
  if (pm) {
2009
2045
  const builds = template.backend ? ["esbuild", "pocketbase-server"] : ["esbuild"];
2010
2046
  addPnpmBuildDependencies(projectPath, pm, builds);
2011
2047
  await installDependencies(pm, projectPath);
2012
- packageManager = pm;
2048
+ packageManager2 = pm;
2013
2049
  }
2014
2050
  }
2015
2051
  if (credentials) {
@@ -2035,7 +2071,7 @@ async function createProject(cwdArg, options, listing) {
2035
2071
  );
2036
2072
  p5.log.success("PocketBase initialized");
2037
2073
  }
2038
- return { directory: projectPath, packageManager, name, template };
2074
+ return { directory: projectPath, packageManager: packageManager2, name, template };
2039
2075
  }
2040
2076
  function promptCredentials(options, onCancel2) {
2041
2077
  return p5.group(
@@ -3523,10 +3559,11 @@ function collectArtifact(cwd, config = {}) {
3523
3559
  const buildPath = path20.join(cwd, outputDir);
3524
3560
  if (!fs21.existsSync(path20.join(buildPath, "index.js"))) {
3525
3561
  throw new BuildError(
3526
- `No ${outputDir}/index.js after the build.
3562
+ `No ${outputDir}/index.js to deploy.
3527
3563
 
3528
- Deploying to a server needs @sveltejs/adapter-node. Install it and set it as
3529
- the adapter in your Vite or Svelte config, then build again.`
3564
+ Deploying to a server needs the output of @sveltejs/adapter-node. Check that the
3565
+ build ran with it as the adapter (and that \`outputDir\` in velastack.config
3566
+ matches where it writes), then deploy again.`
3530
3567
  );
3531
3568
  }
3532
3569
  entries.push({ localPath: buildPath, remoteDir: "" });
@@ -3585,6 +3622,10 @@ var VELA_USER = "vela";
3585
3622
  var SCRIPT_VERSIONS_DIR = `${VELA_ROOT}/script-versions`;
3586
3623
  var PROVISIONED_MARKER = `${VELA_ETC}/provisioned`;
3587
3624
  var ORIGIN_FILE = `${VELA_ETC}/origin.json`;
3625
+ function instanceHasBackend(state) {
3626
+ if (!state) return false;
3627
+ return state.backend ?? Boolean(state.pbPort);
3628
+ }
3588
3629
  function serverTemplatesDir() {
3589
3630
  return path21.join(templatesDir(), "server");
3590
3631
  }
@@ -4138,12 +4179,13 @@ Set both ${pc6.cyan("POCKETBASE_SUPERUSER_EMAIL")} and ${pc6.cyan("POCKETBASE_SU
4138
4179
  import PocketBase2 from "pocketbase";
4139
4180
  async function openRemoteDatabase(session, instance) {
4140
4181
  const [state] = await readInstanceStates(session, instance);
4141
- const pbPort = state?.pbPort;
4182
+ const pbPort = instanceHasBackend(state) ? state?.pbPort : void 0;
4142
4183
  if (!pbPort) {
4143
4184
  throw new Error(
4144
4185
  `${instance} has no deployed database yet.
4145
4186
 
4146
- Run \`vela deploy\` first \u2014 the database is created by the first deploy.`
4187
+ Run \`vela deploy\` first \u2014 the database is created by the first deploy of a
4188
+ project with a backend (\`vela bless\` adds one).`
4147
4189
  );
4148
4190
  }
4149
4191
  const env2 = await readRemoteEnv(session, instance);
@@ -8306,8 +8348,8 @@ var provision = addSshOptions(
8306
8348
  );
8307
8349
 
8308
8350
  // src/commands/deploy.ts
8309
- import path37 from "node:path";
8310
- import fs36 from "node:fs";
8351
+ import path38 from "node:path";
8352
+ import fs37 from "node:fs";
8311
8353
  import { Command as Command76, Option as Option2 } from "commander";
8312
8354
  import * as p42 from "@clack/prompts";
8313
8355
  import pc18 from "picocolors";
@@ -8371,6 +8413,272 @@ async function seedRemoteMeta(session, instance, local, appURL) {
8371
8413
  return Object.keys(patch);
8372
8414
  }
8373
8415
 
8416
+ // src/lib/adapter.ts
8417
+ import fs36 from "node:fs";
8418
+ import path37 from "node:path";
8419
+ import {
8420
+ Project as Project2,
8421
+ QuoteKind as QuoteKind2,
8422
+ SyntaxKind as SyntaxKind2
8423
+ } from "ts-morph";
8424
+ import { detect as detect7 } from "package-manager-detector";
8425
+ var ADAPTER_NODE = "@sveltejs/adapter-node";
8426
+ var ADAPTER_AUTO = "@sveltejs/adapter-auto";
8427
+ var ADAPTER_STATIC = "@sveltejs/adapter-static";
8428
+ var ADAPTER_NODE_RANGE = "^5.5.7";
8429
+ var ADAPTER_SNIPPET = `import adapter from '${ADAPTER_NODE}';
8430
+
8431
+ // ...
8432
+ adapter: adapter()`;
8433
+ var AdapterError = class extends Error {
8434
+ constructor(message, snippet = ADAPTER_SNIPPET) {
8435
+ super(message);
8436
+ this.snippet = snippet;
8437
+ this.name = "AdapterError";
8438
+ }
8439
+ snippet;
8440
+ };
8441
+ function newProject() {
8442
+ return new Project2({
8443
+ compilerOptions: { allowJs: true },
8444
+ manipulationSettings: { quoteKind: QuoteKind2.Single }
8445
+ });
8446
+ }
8447
+ function getDefaultExportObject(sourceFile) {
8448
+ const exported = sourceFile.getExportAssignment((ea) => !ea.isExportEquals())?.getExpression();
8449
+ if (exported?.getKind() === SyntaxKind2.ObjectLiteralExpression) {
8450
+ return exported;
8451
+ }
8452
+ if (exported?.getKind() === SyntaxKind2.Identifier) {
8453
+ const init = sourceFile.getVariableDeclaration(exported.getText())?.getInitializer();
8454
+ if (init?.getKind() === SyntaxKind2.ObjectLiteralExpression) {
8455
+ return init;
8456
+ }
8457
+ }
8458
+ return null;
8459
+ }
8460
+ function resolveKitTarget(root) {
8461
+ const vite = inspectViteSveltekit(root);
8462
+ if (vite?.inlineArg) {
8463
+ return {
8464
+ sourceFile: vite.sourceFile,
8465
+ filePath: vite.filePath,
8466
+ container: "vite-inline",
8467
+ kit: vite.inlineArg,
8468
+ created: false,
8469
+ originalText: vite.sourceFile.getFullText()
8470
+ };
8471
+ }
8472
+ const sveltePath = probeFirstExisting(root, SVELTE_CONFIG_CANDIDATES);
8473
+ if (sveltePath) {
8474
+ const name = path37.basename(sveltePath);
8475
+ if (sveltePath.endsWith(".cjs")) {
8476
+ throw new AdapterError(`${name} is CommonJS, which vela does not edit.`);
8477
+ }
8478
+ const sourceFile = newProject().addSourceFileAtPath(sveltePath);
8479
+ const config = getDefaultExportObject(sourceFile);
8480
+ const kit = config && getOrCreateObjectLiteralProperty(config, "kit", "{}");
8481
+ if (!kit) {
8482
+ throw new AdapterError(`${name} has a shape vela does not understand.`);
8483
+ }
8484
+ return {
8485
+ sourceFile,
8486
+ filePath: sveltePath,
8487
+ container: "svelte-config",
8488
+ kit,
8489
+ created: false,
8490
+ originalText: sourceFile.getFullText()
8491
+ };
8492
+ }
8493
+ if (vite?.sveltekitCall) {
8494
+ const name = path37.basename(vite.filePath);
8495
+ if (vite.nonObjectArg) {
8496
+ throw new AdapterError(`${name} passes sveltekit() something other than an object literal.`);
8497
+ }
8498
+ const originalText = vite.sourceFile.getFullText();
8499
+ const kit = createSveltekitArg(vite);
8500
+ if (!kit)
8501
+ throw new AdapterError(`${name} has a sveltekit() call vela cannot add an argument to.`);
8502
+ return {
8503
+ sourceFile: vite.sourceFile,
8504
+ filePath: vite.filePath,
8505
+ container: "vite-inline",
8506
+ kit,
8507
+ created: true,
8508
+ originalText
8509
+ };
8510
+ }
8511
+ throw new AdapterError(
8512
+ `No svelte.config or vite.config with a sveltekit() plugin found in ${root}.`
8513
+ );
8514
+ }
8515
+ function classify(specifier) {
8516
+ if (specifier === ADAPTER_NODE) return "node";
8517
+ if (specifier === ADAPTER_AUTO) return "auto";
8518
+ if (specifier === ADAPTER_STATIC) return "static";
8519
+ return "other";
8520
+ }
8521
+ function importOf(sourceFile, identifier) {
8522
+ return sourceFile.getImportDeclarations().find((decl) => decl.getDefaultImport()?.getText() === identifier);
8523
+ }
8524
+ function inspectAdapter(target) {
8525
+ const base2 = { file: target.filePath, container: target.container };
8526
+ const prop = target.kit.getProperty("adapter");
8527
+ if (!prop) return { info: { ...base2, kind: "none" } };
8528
+ if (prop.getKind() !== SyntaxKind2.PropertyAssignment) {
8529
+ return { info: { ...base2, kind: "other" } };
8530
+ }
8531
+ const init = prop.asKindOrThrow(SyntaxKind2.PropertyAssignment).getInitializer();
8532
+ if (!init || init.getKind() !== SyntaxKind2.CallExpression) {
8533
+ return { info: { ...base2, kind: "other" } };
8534
+ }
8535
+ const callee = init.asKindOrThrow(SyntaxKind2.CallExpression).getExpression();
8536
+ if (callee.getKind() !== SyntaxKind2.Identifier) {
8537
+ return { info: { ...base2, kind: "other" } };
8538
+ }
8539
+ const importDecl = importOf(target.sourceFile, callee.getText());
8540
+ if (!importDecl) return { info: { ...base2, kind: "other" } };
8541
+ const specifier = importDecl.getModuleSpecifierValue();
8542
+ return { info: { ...base2, kind: classify(specifier), specifier }, importDecl };
8543
+ }
8544
+ async function ensureNodeAdapter(root, { install = true } = {}) {
8545
+ const target = resolveKitTarget(root);
8546
+ const { info, importDecl } = inspectAdapter(target);
8547
+ const name = path37.basename(target.filePath);
8548
+ const outcome = {
8549
+ previous: info.kind,
8550
+ removedDeps: [],
8551
+ packageJsonChanged: false
8552
+ };
8553
+ switch (info.kind) {
8554
+ case "node":
8555
+ break;
8556
+ case "static":
8557
+ throw new AdapterError(
8558
+ `This project builds a static site with ${ADAPTER_STATIC} (${name}).
8559
+
8560
+ vela deploy runs the app as a Node server, which needs ${ADAPTER_NODE}. Switch
8561
+ the adapter to deploy it here, or host the static output elsewhere.`
8562
+ );
8563
+ case "other":
8564
+ throw new AdapterError(
8565
+ `This project's adapter${info.specifier ? ` (${info.specifier})` : ""} in ${name} is not one
8566
+ vela will change for you.
8567
+
8568
+ vela deploy runs the app as a Node server, which needs ${ADAPTER_NODE}:`
8569
+ );
8570
+ case "auto":
8571
+ switchImport(target, importDecl);
8572
+ outcome.configFile = name;
8573
+ break;
8574
+ case "none":
8575
+ addAdapter(target);
8576
+ outcome.configFile = name;
8577
+ break;
8578
+ }
8579
+ if (outcome.configFile) saveTarget(target);
8580
+ const pkgPath = path37.join(root, "package.json");
8581
+ if (fs36.existsSync(pkgPath)) {
8582
+ const pkg = readPackageJson(pkgPath);
8583
+ const { changed, removed } = adoptNodeAdapter(pkg);
8584
+ if (changed) {
8585
+ writePackageJson(pkgPath, pkg);
8586
+ outcome.packageJsonChanged = true;
8587
+ outcome.removedDeps = removed;
8588
+ }
8589
+ }
8590
+ if (outcome.packageJsonChanged && install) {
8591
+ outcome.installedWith = await installAdapterDependencies(root);
8592
+ }
8593
+ return outcome;
8594
+ }
8595
+ async function installAdapterDependencies(root) {
8596
+ const agent = await packageManager(root);
8597
+ const ok = await installDependencies(agent, root, { exitOnFailure: false });
8598
+ if (!ok) {
8599
+ throw new AdapterError(
8600
+ `Installing ${ADAPTER_NODE} with ${agent} failed.
8601
+
8602
+ The config and package.json are already updated: run \`${agent} install\`, then deploy again.`,
8603
+ ""
8604
+ );
8605
+ }
8606
+ return agent;
8607
+ }
8608
+ function switchImport(target, importDecl) {
8609
+ importDecl.setModuleSpecifier(ADAPTER_NODE);
8610
+ const prop = target.kit.getPropertyOrThrow("adapter").asKindOrThrow(SyntaxKind2.PropertyAssignment);
8611
+ const call = prop.getInitializerIfKindOrThrow(SyntaxKind2.CallExpression);
8612
+ for (let i = call.getArguments().length - 1; i >= 0; i--) call.removeArgument(i);
8613
+ dropAdapterAutoComments(target);
8614
+ }
8615
+ var ADAPTER_AUTO_COMMENT = /adapter-auto|svelte\.dev\/docs\/kit\/adapters|switch out the adapter/;
8616
+ function dropAdapterAutoComments(target) {
8617
+ const prop = target.kit.getPropertyOrThrow("adapter");
8618
+ const ranges = prop.getLeadingCommentRanges();
8619
+ if (ranges.length === 0 || !ranges.every((r) => ADAPTER_AUTO_COMMENT.test(r.getText()))) return;
8620
+ const fullStart = prop.getFullStart();
8621
+ const start = prop.getStart();
8622
+ const trivia = target.sourceFile.getFullText().slice(fullStart, start);
8623
+ const lastNewline = trivia.lastIndexOf("\n");
8624
+ if (lastNewline === -1) return;
8625
+ const indent = trivia.slice(lastNewline + 1);
8626
+ target.sourceFile.replaceText([fullStart, start], `
8627
+ ${indent}`);
8628
+ }
8629
+ function addAdapter(target) {
8630
+ const taken = importOf(target.sourceFile, "adapter") ?? target.sourceFile.getVariableDeclaration("adapter");
8631
+ if (taken) {
8632
+ throw new AdapterError(
8633
+ `${path37.basename(target.filePath)} already binds the name \`adapter\` to something that is not the SvelteKit adapter.`
8634
+ );
8635
+ }
8636
+ target.sourceFile.addImportDeclaration({
8637
+ defaultImport: "adapter",
8638
+ moduleSpecifier: ADAPTER_NODE
8639
+ });
8640
+ target.kit.addPropertyAssignment({ name: "adapter", initializer: "adapter()" });
8641
+ }
8642
+ function saveTarget(target) {
8643
+ if (target.created) target.sourceFile.formatText();
8644
+ if (target.sourceFile.getFullText() === target.originalText) return;
8645
+ target.sourceFile.saveSync();
8646
+ }
8647
+ function adoptNodeAdapter(pkg) {
8648
+ const removed = [];
8649
+ let changed = false;
8650
+ for (const kind of ["dependencies", "devDependencies"]) {
8651
+ const deps = pkg[kind];
8652
+ if (deps && ADAPTER_AUTO in deps) {
8653
+ delete deps[ADAPTER_AUTO];
8654
+ removed.push(ADAPTER_AUTO);
8655
+ changed = true;
8656
+ }
8657
+ }
8658
+ if (!pkg.dependencies?.[ADAPTER_NODE] && !pkg.devDependencies?.[ADAPTER_NODE]) {
8659
+ pkg.devDependencies = sortKeys({ ...pkg.devDependencies, [ADAPTER_NODE]: ADAPTER_NODE_RANGE });
8660
+ changed = true;
8661
+ }
8662
+ return { changed, removed: [...new Set(removed)] };
8663
+ }
8664
+ function lockfileFor(agent) {
8665
+ switch (agent) {
8666
+ case "pnpm":
8667
+ return "pnpm-lock.yaml";
8668
+ case "yarn":
8669
+ return "yarn.lock";
8670
+ case "bun":
8671
+ return "bun.lock";
8672
+ case "deno":
8673
+ return "deno.lock";
8674
+ default:
8675
+ return "package-lock.json";
8676
+ }
8677
+ }
8678
+ async function packageManager(root) {
8679
+ return (await detect7({ cwd: root }))?.name ?? getUserAgent() ?? "npm";
8680
+ }
8681
+
8374
8682
  // src/commands/deploy.ts
8375
8683
  var OptionsSchema3 = v8.object({
8376
8684
  ...SSH_OPTION_SCHEMA,
@@ -8403,6 +8711,10 @@ var deploy = addLockWaitOption(
8403
8711
  const options = parseOptions(OptionsSchema3, raw);
8404
8712
  const backend3 = hasBackend();
8405
8713
  p42.intro(pc18.bgCyan(pc18.black(" vela deploy ")));
8714
+ if (options.build !== false) {
8715
+ const { workspaceRootDir } = await getWorkspace();
8716
+ await prepareAdapter(workspaceRootDir);
8717
+ }
8406
8718
  await withTarget(
8407
8719
  raw,
8408
8720
  {
@@ -8416,7 +8728,12 @@ var deploy = addLockWaitOption(
8416
8728
  const isPreview2 = ctx.target.kind === "preview";
8417
8729
  const configured = options.domain ?? (isPreview2 ? existing?.domain : ctx.binding.domain ?? config.deploy?.domain ?? existing?.domain) ?? "";
8418
8730
  const askedForRemoteDb = options.remoteDb ?? config.deploy?.buildAgainstRemote;
8419
- const remoteDb = askedForRemoteDb ?? Boolean(existing?.pbPort);
8731
+ const remoteDb = askedForRemoteDb ?? instanceHasBackend(existing);
8732
+ if (existing && instanceHasBackend(existing) !== backend3) {
8733
+ p42.log.info(
8734
+ backend3 ? `${pc18.cyan(ctx.targetName)} was deployed without a backend before. This deploy adds PocketBase.` : `${pc18.cyan(ctx.targetName)} was deployed with a backend before. This deploy removes PocketBase; its database stays on the server.`
8735
+ );
8736
+ }
8420
8737
  const sha = await gitSha(workspaceRootDir);
8421
8738
  const reporter = createDeployReporter(workspaceRootDir);
8422
8739
  const server = await reporter.identifyServer(session);
@@ -8572,6 +8889,38 @@ or ${pc18.cyan("vela link")} it to get a free velastack.app hostname.`) + `.`
8572
8889
  p42.outro(`${pc18.cyan("vela status")} to see what is running`);
8573
8890
  }, "Failed to deploy.")
8574
8891
  );
8892
+ async function prepareAdapter(workspaceRootDir) {
8893
+ const rethrow = (err) => {
8894
+ if (err instanceof AdapterError) {
8895
+ throw new Error(err.snippet ? `${err.message}
8896
+
8897
+ ${pc18.cyan(err.snippet)}` : err.message);
8898
+ }
8899
+ throw err;
8900
+ };
8901
+ const outcome = await ensureNodeAdapter(workspaceRootDir, { install: false }).catch(rethrow);
8902
+ if (!outcome.configFile && !outcome.packageJsonChanged) return;
8903
+ const changed = [
8904
+ outcome.configFile,
8905
+ outcome.packageJsonChanged ? "package.json" : void 0
8906
+ ].filter((f) => Boolean(f));
8907
+ const why = outcome.previous === "auto" ? `${ADAPTER_AUTO} builds nothing for a server of your own` : outcome.previous === "none" ? "No adapter was configured" : `${ADAPTER_NODE} was configured but not in package.json`;
8908
+ p42.log.step(`Switching the adapter to ${pc18.cyan(ADAPTER_NODE)}`);
8909
+ p42.log.info(
8910
+ `${why}, and vela deploy runs the app as a Node server.
8911
+
8912
+ Changed ${changed.join(", ")}` + (outcome.removedDeps.length ? `
8913
+ Removed ${outcome.removedDeps.join(", ")}` : "")
8914
+ );
8915
+ if (outcome.packageJsonChanged) {
8916
+ try {
8917
+ changed.push(lockfileFor(await installAdapterDependencies(workspaceRootDir)));
8918
+ } catch (err) {
8919
+ rethrow(err);
8920
+ }
8921
+ }
8922
+ p42.log.warn(`Commit ${changed.join(", ")} so every deploy builds the same way.`);
8923
+ }
8575
8924
  async function reportAppURLDrift(session, instance, domain) {
8576
8925
  const expected = normalizeOrigin(domain);
8577
8926
  if (!expected) return;
@@ -8607,10 +8956,10 @@ Set them in the admin panel instead. ${pc18.dim(String(err))}`
8607
8956
  }
8608
8957
  }
8609
8958
  async function openDatabaseTunnel(session, instance, state) {
8610
- const pbPort = state?.pbPort;
8959
+ const pbPort = instanceHasBackend(state) ? state?.pbPort : void 0;
8611
8960
  if (!pbPort) {
8612
8961
  throw new Error(
8613
- `--remote-db needs an existing deployment to build against, and ${instance} has not been deployed yet.
8962
+ `--remote-db needs a deployed database to build against, and ${instance} ${state ? "has no backend" : "has not been deployed yet"}.
8614
8963
 
8615
8964
  Deploy once without it, then turn it on.`
8616
8965
  );
@@ -8684,7 +9033,7 @@ async function uploadRelease(session, instance, release, entries) {
8684
9033
  }
8685
9034
  function isDirectory(target) {
8686
9035
  try {
8687
- return fs36.statSync(target).isDirectory();
9036
+ return fs37.statSync(target).isDirectory();
8688
9037
  } catch {
8689
9038
  return false;
8690
9039
  }
@@ -8692,7 +9041,7 @@ function isDirectory(target) {
8692
9041
  async function reportEmptyEnvironment(session, instance, workspaceRootDir) {
8693
9042
  const remote = await readRemoteEnv(session, instance);
8694
9043
  if (Object.keys(remote).length > 0) return;
8695
- if (!fs36.existsSync(path37.join(workspaceRootDir, ".env"))) return;
9044
+ if (!fs37.existsSync(path38.join(workspaceRootDir, ".env"))) return;
8696
9045
  p42.log.warn(
8697
9046
  `This app has no production environment variables yet.
8698
9047
 
@@ -8710,7 +9059,7 @@ async function serverTimeOrLocal(session) {
8710
9059
  }
8711
9060
 
8712
9061
  // src/commands/link.ts
8713
- import path38 from "node:path";
9062
+ import path39 from "node:path";
8714
9063
  import process29 from "node:process";
8715
9064
  import { Command as Command77 } from "commander";
8716
9065
  import * as p43 from "@clack/prompts";
@@ -8798,12 +9147,12 @@ async function promptProjectName(workspaceRootDir) {
8798
9147
  }
8799
9148
  function defaultProjectName2(workspaceRootDir) {
8800
9149
  try {
8801
- const pkg = readPackageJson(path38.join(workspaceRootDir, "package.json"));
9150
+ const pkg = readPackageJson(path39.join(workspaceRootDir, "package.json"));
8802
9151
  const name = pkg.name;
8803
9152
  if (typeof name === "string" && name.trim()) return name.trim();
8804
9153
  } catch {
8805
9154
  }
8806
- return path38.basename(workspaceRootDir);
9155
+ return path39.basename(workspaceRootDir);
8807
9156
  }
8808
9157
 
8809
9158
  // src/commands/env.ts
@@ -8934,8 +9283,8 @@ var envUnset = addTargetOptions(
8934
9283
  );
8935
9284
 
8936
9285
  // src/commands/env/import.ts
8937
- import fs37 from "node:fs";
8938
- import path39 from "node:path";
9286
+ import fs38 from "node:fs";
9287
+ import path40 from "node:path";
8939
9288
  import process31 from "node:process";
8940
9289
  import { Command as Command81 } from "commander";
8941
9290
  import * as p47 from "@clack/prompts";
@@ -8981,10 +9330,10 @@ var envImport = addTargetOptions(
8981
9330
  )
8982
9331
  );
8983
9332
  function resolve(file) {
8984
- return path39.resolve(process31.cwd(), file);
9333
+ return path40.resolve(process31.cwd(), file);
8985
9334
  }
8986
9335
  function read(resolved, shown) {
8987
- if (!fs37.existsSync(resolved)) throw new Error(`${shown} does not exist.`);
9336
+ if (!fs38.existsSync(resolved)) throw new Error(`${shown} does not exist.`);
8988
9337
  const incoming = readLocalEnvFile(resolved);
8989
9338
  if (Object.keys(incoming).length === 0) p47.log.info(`${shown} has no variables to import.`);
8990
9339
  return incoming;
@@ -9262,8 +9611,8 @@ var admin = new Command87("admin").description("manage admin panel logins").conf
9262
9611
  import { Command as Command93 } from "commander";
9263
9612
 
9264
9613
  // src/commands/backup/create.ts
9265
- import fs38 from "node:fs";
9266
- import path40 from "node:path";
9614
+ import fs39 from "node:fs";
9615
+ import path41 from "node:path";
9267
9616
  import { Command as Command88 } from "commander";
9268
9617
  import * as p51 from "@clack/prompts";
9269
9618
  import pc26 from "picocolors";
@@ -9417,12 +9766,12 @@ Your bucket's own versioning is what protects the uploaded files.`
9417
9766
  }, "Failed to create the backup.")
9418
9767
  );
9419
9768
  async function download(ctx, key, outputDir) {
9420
- const dir = path40.resolve(ctx.workspaceRootDir, outputDir);
9421
- fs38.mkdirSync(dir, { recursive: true });
9422
- const destination = path40.join(dir, key);
9769
+ const dir = path41.resolve(ctx.workspaceRootDir, outputDir);
9770
+ fs39.mkdirSync(dir, { recursive: true });
9771
+ const destination = path41.join(dir, key);
9423
9772
  if (!ctx.session) {
9424
- fs38.copyFileSync(path40.join(ctx.workspaceRootDir, "data", "backups", key), destination);
9425
- return path40.relative(ctx.workspaceRootDir, destination);
9773
+ fs39.copyFileSync(path41.join(ctx.workspaceRootDir, "data", "backups", key), destination);
9774
+ return path41.relative(ctx.workspaceRootDir, destination);
9426
9775
  }
9427
9776
  const spinner7 = p51.spinner();
9428
9777
  spinner7.start(`Downloading ${key}`);
@@ -9433,7 +9782,7 @@ async function download(ctx, key, outputDir) {
9433
9782
  throw error;
9434
9783
  }
9435
9784
  spinner7.stop(`Downloaded ${key}`);
9436
- return path40.relative(ctx.workspaceRootDir, destination);
9785
+ return path41.relative(ctx.workspaceRootDir, destination);
9437
9786
  }
9438
9787
 
9439
9788
  // src/commands/backup/list.ts
@@ -9467,8 +9816,8 @@ Take one with ${pc27.cyan("vela backup create")}.`
9467
9816
  );
9468
9817
 
9469
9818
  // src/commands/backup/download.ts
9470
- import fs39 from "node:fs";
9471
- import path41 from "node:path";
9819
+ import fs40 from "node:fs";
9820
+ import path42 from "node:path";
9472
9821
  import { Command as Command90 } from "commander";
9473
9822
  import * as p53 from "@clack/prompts";
9474
9823
  import pc28 from "picocolors";
@@ -9495,11 +9844,11 @@ Run ${pc28.cyan("vela backup list")} to see what it does have.`
9495
9844
  Fetch it from the bucket directly \u2014 vela does not hold its credentials.`
9496
9845
  );
9497
9846
  }
9498
- const dir = path41.resolve(ctx.workspaceRootDir, options.output);
9499
- fs39.mkdirSync(dir, { recursive: true });
9500
- const destination = path41.join(dir, key);
9847
+ const dir = path42.resolve(ctx.workspaceRootDir, options.output);
9848
+ fs40.mkdirSync(dir, { recursive: true });
9849
+ const destination = path42.join(dir, key);
9501
9850
  if (!ctx.session) {
9502
- fs39.copyFileSync(path41.join(ctx.workspaceRootDir, "data", "backups", key), destination);
9851
+ fs40.copyFileSync(path42.join(ctx.workspaceRootDir, "data", "backups", key), destination);
9503
9852
  } else {
9504
9853
  const spinner7 = p53.spinner();
9505
9854
  spinner7.start(`Downloading ${key} (${formatBytes(found.size)})`);
@@ -9513,7 +9862,7 @@ Fetch it from the bucket directly \u2014 vela does not hold its credentials.`
9513
9862
  }
9514
9863
  reportResult({
9515
9864
  summary: `Saved ${key} from ${ctx.targetName}.`,
9516
- filesCreated: [path41.relative(ctx.workspaceRootDir, destination)]
9865
+ filesCreated: [path42.relative(ctx.workspaceRootDir, destination)]
9517
9866
  });
9518
9867
  });
9519
9868
  }, "Failed to download the backup.")
@@ -9608,8 +9957,8 @@ Set one with ${pc30.cyan('vela backup schedule "0 3 * * *"')}.`
9608
9957
  var backup = new Command93("backup").description("back up the database and uploads, locally or on a target").configureHelp(helpConfig).addCommand(backupCreate).addCommand(backupList).addCommand(backupDownload).addCommand(backupDelete).addCommand(backupSchedule);
9609
9958
 
9610
9959
  // src/commands/restore.ts
9611
- import fs40 from "node:fs";
9612
- import path42 from "node:path";
9960
+ import fs41 from "node:fs";
9961
+ import path43 from "node:path";
9613
9962
  import process34 from "node:process";
9614
9963
  import { Command as Command94 } from "commander";
9615
9964
  import * as p56 from "@clack/prompts";
@@ -9632,7 +9981,7 @@ var restore = addLockWaitOption(
9632
9981
  `${ctx.targetName} was deployed without a database, so there is nothing to restore.`
9633
9982
  );
9634
9983
  }
9635
- const local = source && fs40.existsSync(source) ? source : void 0;
9984
+ const local = source && fs41.existsSync(source) ? source : void 0;
9636
9985
  const key = local ? void 0 : await resolveKey(ctx, source);
9637
9986
  if (!options.yes) {
9638
9987
  await confirm13(ctx.appName, ctx.targetName, ctx.envTag, local ?? key);
@@ -9655,7 +10004,7 @@ var restore = addLockWaitOption(
9655
10004
  });
9656
10005
  p56.log.success(
9657
10006
  `Restored ${pc31.cyan(`${ctx.appName} (${ctx.targetName})`)} from ${pc31.cyan(
9658
- local ? path42.basename(local) : key
10007
+ local ? path43.basename(local) : key
9659
10008
  )}.`
9660
10009
  );
9661
10010
  if (result?.storageCarriedOver) {
@@ -9721,21 +10070,21 @@ Take one with ${pc31.cyan("vela backup create")}, or pass the path to an archive
9721
10070
  }
9722
10071
  async function stage(ctx, file) {
9723
10072
  const dir = remotePaths.restoreStage(ctx.instance);
9724
- const remote = `${dir}/${path42.basename(file)}`;
10073
+ const remote = `${dir}/${path43.basename(file)}`;
9725
10074
  const spinner7 = p56.spinner();
9726
- spinner7.start(`Uploading ${path42.basename(file)}`);
10075
+ spinner7.start(`Uploading ${path43.basename(file)}`);
9727
10076
  try {
9728
10077
  await ctx.session.script(`mkdir -p "$1"`, { args: [dir] });
9729
10078
  await ctx.session.upload([file], dir);
9730
10079
  } catch (error) {
9731
- spinner7.stop(`Could not upload ${path42.basename(file)}.`);
10080
+ spinner7.stop(`Could not upload ${path43.basename(file)}.`);
9732
10081
  throw error;
9733
10082
  }
9734
- spinner7.stop(`Uploaded ${path42.basename(file)}`);
10083
+ spinner7.stop(`Uploaded ${path43.basename(file)}`);
9735
10084
  return remote;
9736
10085
  }
9737
10086
  async function confirm13(appName, targetName, envTag, from) {
9738
- const what = `${pc31.cyan(`${appName} (${targetName})`)} from ${pc31.cyan(path42.basename(from))}`;
10087
+ const what = `${pc31.cyan(`${appName} (${targetName})`)} from ${pc31.cyan(path43.basename(from))}`;
9739
10088
  if (isProd(envTag)) {
9740
10089
  const answer = await p56.text({
9741
10090
  message: `This replaces the database and uploads of ${what}. Type the app name to confirm`,
@@ -9846,28 +10195,28 @@ Release and domain are shown from what this project recorded.`
9846
10195
  }
9847
10196
 
9848
10197
  // src/commands/test.ts
9849
- import path43 from "node:path";
10198
+ import path44 from "node:path";
9850
10199
  import process35 from "node:process";
9851
10200
  import { Command as Command96 } from "commander";
9852
10201
  import PocketBase6 from "pocketbase";
9853
10202
  import pc33 from "picocolors";
9854
10203
  import { x as x5 } from "tinyexec";
9855
- import { detect as detect7 } from "package-manager-detector";
10204
+ import { detect as detect8 } from "package-manager-detector";
9856
10205
  import { resolveCommand as resolveCommand7 } from "package-manager-detector/commands";
9857
- import fs41 from "node:fs";
10206
+ import fs42 from "node:fs";
9858
10207
  var testServer = new Command96("test:server").description("run server tests").allowUnknownOption(true).allowExcessArguments(true).configureHelp(helpConfig).action(async (_opts, cmd) => {
9859
10208
  const cwd = process35.cwd();
9860
10209
  const email3 = `test-${Math.random().toString(36).slice(2)}@example.com`;
9861
10210
  const password11 = "password";
9862
- const testDataDir = path43.join(cwd, "test-data");
9863
- fs41.rmSync(testDataDir, { recursive: true, force: true });
10211
+ const testDataDir = path44.join(cwd, "test-data");
10212
+ fs42.rmSync(testDataDir, { recursive: true, force: true });
9864
10213
  const { stop, url } = await launchPocketbase(cwd, {
9865
10214
  dir: testDataDir,
9866
- migrationsDir: path43.join(cwd, MIGRATIONS_DIR),
10215
+ migrationsDir: path44.join(cwd, MIGRATIONS_DIR),
9867
10216
  // The app's PocketBase hooks (slug generation, personal teams, …) are part
9868
10217
  // of its behaviour; the suite runs against the same server dev and build
9869
10218
  // start, so it loads them from the same place.
9870
- hooksDir: path43.join(cwd, DATA_DIR, "hooks"),
10219
+ hooksDir: path44.join(cwd, DATA_DIR, "hooks"),
9871
10220
  email: email3,
9872
10221
  password: password11
9873
10222
  });
@@ -9883,7 +10232,7 @@ var testServer = new Command96("test:server").description("run server tests").al
9883
10232
  if (cleanedUp) return;
9884
10233
  cleanedUp = true;
9885
10234
  stop();
9886
- fs41.rmSync(testDataDir, { recursive: true, force: true });
10235
+ fs42.rmSync(testDataDir, { recursive: true, force: true });
9887
10236
  };
9888
10237
  const cleanup = async () => {
9889
10238
  if (cleanedUp) return;
@@ -9935,7 +10284,7 @@ var testServer = new Command96("test:server").description("run server tests").al
9935
10284
  const passthrough = filter ? extraArgs.filter((a) => a !== filter) : extraArgs;
9936
10285
  if (!filter) filter = "server";
9937
10286
  try {
9938
- const pm = (await detect7({ cwd }))?.name ?? "npm";
10287
+ const pm = (await detect8({ cwd }))?.name ?? "npm";
9939
10288
  const resolved = resolveCommand7(pm, "execute", [
9940
10289
  "vitest",
9941
10290
  "run",
@@ -9982,8 +10331,8 @@ function stubPagesPlugin() {
9982
10331
  }
9983
10332
 
9984
10333
  // src/commands/routes.ts
9985
- import fs42 from "node:fs";
9986
- import path44 from "node:path";
10334
+ import fs43 from "node:fs";
10335
+ import path45 from "node:path";
9987
10336
  import { Command as Command97 } from "commander";
9988
10337
  var HTTP_METHODS = /* @__PURE__ */ new Set([
9989
10338
  "GET",
@@ -9997,30 +10346,30 @@ var HTTP_METHODS = /* @__PURE__ */ new Set([
9997
10346
  ]);
9998
10347
  var routes = new Command97("routes").description("list routes").configureHelp(helpConfig).action(async () => {
9999
10348
  const { workspaceRootDir, routesDir } = await getWorkspace();
10000
- const routesRoot = path44.join(workspaceRootDir, routesDir);
10349
+ const routesRoot = path45.join(workspaceRootDir, routesDir);
10001
10350
  const found = walk(routesRoot, routesRoot).filter((r) => r.methods.length > 0);
10002
10351
  found.sort((a, b) => a.urlPattern.localeCompare(b.urlPattern));
10003
10352
  printTable(found);
10004
10353
  });
10005
10354
  function walk(root, dir) {
10006
- const entries = fs42.readdirSync(dir, { withFileTypes: true });
10355
+ const entries = fs43.readdirSync(dir, { withFileTypes: true });
10007
10356
  const routes2 = [];
10008
10357
  const hasLeaf = entries.some((e) => e.isFile() && isRouteFile(e.name));
10009
10358
  if (hasLeaf) {
10010
- const id = "/" + path44.relative(root, dir).split(path44.sep).filter(Boolean).join("/");
10359
+ const id = "/" + path45.relative(root, dir).split(path45.sep).filter(Boolean).join("/");
10011
10360
  const urlPattern = id.replace(/\([^)]+\)\/?/g, "").replace(/\/$/, "") || "/";
10012
10361
  const methods = /* @__PURE__ */ new Set();
10013
10362
  for (const entry of entries) {
10014
10363
  if (!entry.isFile()) continue;
10015
10364
  if (entry.name.endsWith("+page.svelte")) methods.add("GET");
10016
10365
  if (entry.name.endsWith("+server.ts") || entry.name.endsWith("+server.js") || entry.name.endsWith("+page.server.ts") || entry.name.endsWith("+page.server.js")) {
10017
- extractMethods(path44.join(dir, entry.name)).forEach((m) => methods.add(m));
10366
+ extractMethods(path45.join(dir, entry.name)).forEach((m) => methods.add(m));
10018
10367
  }
10019
10368
  }
10020
10369
  routes2.push({ id: id || "/", urlPattern, methods: [...methods] });
10021
10370
  }
10022
10371
  for (const entry of entries) {
10023
- if (entry.isDirectory()) routes2.push(...walk(root, path44.join(dir, entry.name)));
10372
+ if (entry.isDirectory()) routes2.push(...walk(root, path45.join(dir, entry.name)));
10024
10373
  }
10025
10374
  return routes2;
10026
10375
  }
@@ -10029,7 +10378,7 @@ function isRouteFile(name) {
10029
10378
  }
10030
10379
  function extractMethods(file) {
10031
10380
  try {
10032
- const content = fs42.readFileSync(file, "utf8");
10381
+ const content = fs43.readFileSync(file, "utf8");
10033
10382
  const methods = [];
10034
10383
  const exportRegex = /export\s+(?:const|async\s+function|function)\s+(\w+)/g;
10035
10384
  let match;
@@ -10073,11 +10422,11 @@ function printTable(routes2) {
10073
10422
  import process36 from "node:process";
10074
10423
  import { Command as Command98 } from "commander";
10075
10424
  import { x as x6 } from "tinyexec";
10076
- import { detect as detect8 } from "package-manager-detector";
10425
+ import { detect as detect9 } from "package-manager-detector";
10077
10426
  import { resolveCommand as resolveCommand8 } from "package-manager-detector/commands";
10078
10427
  async function runWuchale(extraArgs) {
10079
10428
  const cwd = process36.cwd();
10080
- const pm = (await detect8({ cwd }))?.name ?? "npm";
10429
+ const pm = (await detect9({ cwd }))?.name ?? "npm";
10081
10430
  const resolved = resolveCommand8(pm, "execute", ["wuchale", ...extraArgs]);
10082
10431
  const args = resolved.args.slice();
10083
10432
  if (pm === "npm") args.unshift("--yes");
@@ -10112,7 +10461,7 @@ import pc35 from "picocolors";
10112
10461
 
10113
10462
  // src/lib/cms-backend.ts
10114
10463
  import { createRequire as createRequire3 } from "node:module";
10115
- import path45 from "node:path";
10464
+ import path46 from "node:path";
10116
10465
  import process37 from "node:process";
10117
10466
  import { pathToFileURL as pathToFileURL3 } from "node:url";
10118
10467
  import pc34 from "picocolors";
@@ -10120,7 +10469,7 @@ var DEFAULT_PROJECT = "default";
10120
10469
  async function loadBackendModule(root) {
10121
10470
  let entry;
10122
10471
  try {
10123
- entry = createRequire3(path45.join(root, "package.json")).resolve("@velastack/cms/backend");
10472
+ entry = createRequire3(path46.join(root, "package.json")).resolve("@velastack/cms/backend");
10124
10473
  } catch {
10125
10474
  throw new Error(
10126
10475
  `@velastack/cms is not installed in this project.
@@ -10138,8 +10487,8 @@ async function withCmsBackend(fn, cwd = process37.cwd()) {
10138
10487
  const { createCmsBackend } = await loadBackendModule(root);
10139
10488
  const dataDir2 = localDataDir(root);
10140
10489
  const backend3 = createCmsBackend({
10141
- dbPath: path45.join(dataDir2, "cms.sqlite"),
10142
- uploadDir: path45.join(dataDir2, "uploads")
10490
+ dbPath: path46.join(dataDir2, "cms.sqlite"),
10491
+ uploadDir: path46.join(dataDir2, "uploads")
10143
10492
  });
10144
10493
  try {
10145
10494
  return await fn(backend3);
@@ -10242,7 +10591,10 @@ var NO_BACKEND_COMMMANDS = /* @__PURE__ */ new Set([
10242
10591
  // Acts on whichever target `-t` names; the local path reads the project's
10243
10592
  // own credentials rather than requiring them in this process.
10244
10593
  "backup",
10245
- "restore"
10594
+ "restore",
10595
+ // Removes a copy from its server; the other `destroy` subcommands edit the
10596
+ // local schema and stay gated.
10597
+ "destroy deployment"
10246
10598
  ]);
10247
10599
  var BACKEND_OPTIONAL_COMMANDS = /* @__PURE__ */ new Set(["dev", "build", "preview", "deploy"]);
10248
10600
  var SELF_CREDENTIALED_COMMANDS = /* @__PURE__ */ new Set(["test:server"]);
@@ -10251,14 +10603,14 @@ program.hook("preAction", (_thisCommand, actionCommand) => {
10251
10603
  if (isStub(actionCommand)) return;
10252
10604
  const envRoot = findWorkspaceRoot() ?? process38.cwd();
10253
10605
  dotenv2.config({ path: nodePath.join(envRoot, ".env"), quiet: true });
10254
- const path46 = getCommandPath(actionCommand);
10255
- if (NO_BACKEND_COMMMANDS.has(path46)) return;
10256
- const top = path46.split(" ", 1)[0];
10606
+ const path47 = getCommandPath(actionCommand);
10607
+ if (NO_BACKEND_COMMMANDS.has(path47)) return;
10608
+ const top = path47.split(" ", 1)[0];
10257
10609
  if (NO_BACKEND_COMMMANDS.has(top)) return;
10258
10610
  if (!hasBackend()) {
10259
10611
  if (BACKEND_OPTIONAL_COMMANDS.has(top)) return;
10260
10612
  p61.log.error(
10261
- `${pc38.cyan(`vela ${path46}`)} needs a backend, and this project does not have one.
10613
+ `${pc38.cyan(`vela ${path47}`)} needs a backend, and this project does not have one.
10262
10614
 
10263
10615
  Static projects have no database to talk to.
10264
10616
 
@@ -10268,7 +10620,7 @@ To add a backend to this project, run ${pc38.cyan("vela bless")}.`
10268
10620
  p61.cancel("Operation failed.");
10269
10621
  process38.exit(1);
10270
10622
  }
10271
- if (SELF_CREDENTIALED_COMMANDS.has(path46)) return;
10623
+ if (SELF_CREDENTIALED_COMMANDS.has(path47)) return;
10272
10624
  if (!process38.env.POCKETBASE_SUPERUSER_EMAIL || !process38.env.POCKETBASE_SUPERUSER_PASSWORD) {
10273
10625
  p61.log.error(
10274
10626
  `PocketBase superuser credentials are required.