naro-cli 0.6.1 → 0.8.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.
@@ -6,10 +6,10 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/deploy.ts
9
- import { existsSync as existsSync6 } from "fs";
10
- import { cp, mkdtemp as mkdtemp2, readFile as readFile7, rm as rm2 } from "fs/promises";
9
+ import { existsSync as existsSync7 } from "fs";
10
+ import { cp as cp2, mkdtemp as mkdtemp2, readFile as readFile13, rm as rm3 } from "fs/promises";
11
11
  import { tmpdir as tmpdir2 } from "os";
12
- import { basename, dirname, join as join15, resolve as resolve4 } from "path";
12
+ import { basename as basename3, dirname as dirname4, join as join23, resolve as resolve9 } from "path";
13
13
  import { fileURLToPath } from "url";
14
14
 
15
15
  // ../../packages/build/src/artifact-upload.ts
@@ -142,8 +142,8 @@ async function restoreCache(args) {
142
142
  async function saveCache(args) {
143
143
  const { scope, key, sourceDir, ports, observer } = args;
144
144
  try {
145
- const info2 = await stat(sourceDir);
146
- if (!info2.isDirectory()) throw new Error("not a directory");
145
+ const info3 = await stat(sourceDir);
146
+ if (!info3.isDirectory()) throw new Error("not a directory");
147
147
  } catch {
148
148
  await log(
149
149
  observer,
@@ -226,9 +226,9 @@ function hashAsset(data, contentType2) {
226
226
  }
227
227
 
228
228
  // ../../packages/build/src/local-build-executor.ts
229
- import { mkdtemp, rm } from "fs/promises";
229
+ import { access as access8, mkdtemp, readFile as readFile12, rm as rm2 } from "fs/promises";
230
230
  import { tmpdir } from "os";
231
- import { join as join8 } from "path";
231
+ import { dirname as dirname3, join as join16, resolve as resolve7 } from "path";
232
232
 
233
233
  // ../../packages/core/src/artifact-manifest.ts
234
234
  var MAX_FILE_BYTES = 25 * 1024 * 1024;
@@ -244,6 +244,9 @@ var PHASES = [
244
244
  ];
245
245
  var ACTIVE_STATUSES = new Set(PHASES);
246
246
 
247
+ // ../../packages/core/src/deployment-strategy.ts
248
+ var MIN_SUPPORTED_NEXT_MAJOR = 15;
249
+
247
250
  // ../../packages/core/src/errors.ts
248
251
  var DeploymentError = class extends Error {
249
252
  code;
@@ -278,6 +281,9 @@ function pathnameOf(url2) {
278
281
  }
279
282
  }
280
283
 
284
+ // ../../packages/core/src/internal-auth.ts
285
+ import { Buffer as Buffer2 } from "buffer";
286
+
281
287
  // ../../packages/core/src/paths.ts
282
288
  function projectPath(teamSlug, projectSlug) {
283
289
  return `/${teamSlug}/projects/${projectSlug}`;
@@ -2468,9 +2474,9 @@ var $ZodCheckEndsWith = /* @__PURE__ */ $constructor("$ZodCheckEndsWith", (inst,
2468
2474
  });
2469
2475
  };
2470
2476
  });
2471
- function handleCheckPropertyResult(result, payload, property) {
2477
+ function handleCheckPropertyResult(result, payload, property2) {
2472
2478
  if (result.issues.length) {
2473
- payload.issues.push(...prefixIssues(property, result.issues));
2479
+ payload.issues.push(...prefixIssues(property2, result.issues));
2474
2480
  }
2475
2481
  }
2476
2482
  var $ZodCheckProperty = /* @__PURE__ */ $constructor("$ZodCheckProperty", (inst, def) => {
@@ -11297,10 +11303,10 @@ function _endsWith(suffix, params) {
11297
11303
  });
11298
11304
  }
11299
11305
  // @__NO_SIDE_EFFECTS__
11300
- function _property(property, schema, params) {
11306
+ function _property(property2, schema, params) {
11301
11307
  return new $ZodCheckProperty({
11302
11308
  check: "property",
11303
- property,
11309
+ property: property2,
11304
11310
  schema,
11305
11311
  ...normalizeParams(params)
11306
11312
  });
@@ -11397,28 +11403,28 @@ function _tuple(Class2, items, _paramsOrRest, _params) {
11397
11403
  });
11398
11404
  }
11399
11405
  // @__NO_SIDE_EFFECTS__
11400
- function _record(Class2, keyType, valueType, params) {
11406
+ function _record(Class2, keyType, valueType2, params) {
11401
11407
  return new Class2({
11402
11408
  type: "record",
11403
11409
  keyType,
11404
- valueType,
11410
+ valueType: valueType2,
11405
11411
  ...normalizeParams(params)
11406
11412
  });
11407
11413
  }
11408
11414
  // @__NO_SIDE_EFFECTS__
11409
- function _map(Class2, keyType, valueType, params) {
11415
+ function _map(Class2, keyType, valueType2, params) {
11410
11416
  return new Class2({
11411
11417
  type: "map",
11412
11418
  keyType,
11413
- valueType,
11419
+ valueType: valueType2,
11414
11420
  ...normalizeParams(params)
11415
11421
  });
11416
11422
  }
11417
11423
  // @__NO_SIDE_EFFECTS__
11418
- function _set(Class2, valueType, params) {
11424
+ function _set(Class2, valueType2, params) {
11419
11425
  return new Class2({
11420
11426
  type: "set",
11421
- valueType,
11427
+ valueType: valueType2,
11422
11428
  ...normalizeParams(params)
11423
11429
  });
11424
11430
  }
@@ -13784,37 +13790,37 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
13784
13790
  inst.keyType = def.keyType;
13785
13791
  inst.valueType = def.valueType;
13786
13792
  });
13787
- function record(keyType, valueType, params) {
13788
- if (!valueType || !valueType._zod) {
13793
+ function record(keyType, valueType2, params) {
13794
+ if (!valueType2 || !valueType2._zod) {
13789
13795
  return new ZodRecord({
13790
13796
  type: "record",
13791
13797
  keyType: string2(),
13792
13798
  valueType: keyType,
13793
- ...util_exports.normalizeParams(valueType)
13799
+ ...util_exports.normalizeParams(valueType2)
13794
13800
  });
13795
13801
  }
13796
13802
  return new ZodRecord({
13797
13803
  type: "record",
13798
13804
  keyType,
13799
- valueType,
13805
+ valueType: valueType2,
13800
13806
  ...util_exports.normalizeParams(params)
13801
13807
  });
13802
13808
  }
13803
- function partialRecord(keyType, valueType, params) {
13809
+ function partialRecord(keyType, valueType2, params) {
13804
13810
  const k = clone(keyType);
13805
13811
  k._zod.values = void 0;
13806
13812
  return new ZodRecord({
13807
13813
  type: "record",
13808
13814
  keyType: k,
13809
- valueType,
13815
+ valueType: valueType2,
13810
13816
  ...util_exports.normalizeParams(params)
13811
13817
  });
13812
13818
  }
13813
- function looseRecord(keyType, valueType, params) {
13819
+ function looseRecord(keyType, valueType2, params) {
13814
13820
  return new ZodRecord({
13815
13821
  type: "record",
13816
13822
  keyType,
13817
- valueType,
13823
+ valueType: valueType2,
13818
13824
  mode: "loose",
13819
13825
  ...util_exports.normalizeParams(params)
13820
13826
  });
@@ -13830,11 +13836,11 @@ var ZodMap = /* @__PURE__ */ $constructor("ZodMap", (inst, def) => {
13830
13836
  inst.max = (...args) => inst.check(_maxSize(...args));
13831
13837
  inst.size = (...args) => inst.check(_size(...args));
13832
13838
  });
13833
- function map(keyType, valueType, params) {
13839
+ function map(keyType, valueType2, params) {
13834
13840
  return new ZodMap({
13835
13841
  type: "map",
13836
13842
  keyType,
13837
- valueType,
13843
+ valueType: valueType2,
13838
13844
  ...util_exports.normalizeParams(params)
13839
13845
  });
13840
13846
  }
@@ -13847,10 +13853,10 @@ var ZodSet = /* @__PURE__ */ $constructor("ZodSet", (inst, def) => {
13847
13853
  inst.max = (...args) => inst.check(_maxSize(...args));
13848
13854
  inst.size = (...args) => inst.check(_size(...args));
13849
13855
  });
13850
- function set(valueType, params) {
13856
+ function set(valueType2, params) {
13851
13857
  return new ZodSet({
13852
13858
  type: "set",
13853
- valueType,
13859
+ valueType: valueType2,
13854
13860
  ...util_exports.normalizeParams(params)
13855
13861
  });
13856
13862
  }
@@ -14809,6 +14815,7 @@ var RESERVED_SUBDOMAINS = [
14809
14815
  "docs",
14810
14816
  "help",
14811
14817
  "imap",
14818
+ "ingest",
14812
14819
  "mail",
14813
14820
  "mx",
14814
14821
  "naro",
@@ -14888,6 +14895,87 @@ var connectRepositorySchema = external_exports.object({
14888
14895
  })
14889
14896
  });
14890
14897
 
14898
+ // ../../packages/core/src/sql-classifier.ts
14899
+ var NUL = String.fromCharCode(0);
14900
+
14901
+ // ../../packages/build/src/next-version.ts
14902
+ var NEXT_RELEASE_DATES = {
14903
+ 16: "2025-10-21",
14904
+ 15: "2024-10-21",
14905
+ 14: "2023-10-26",
14906
+ 13: "2022-10-26",
14907
+ 12: "2021-10-26"
14908
+ };
14909
+ var SUPPORT_WINDOW_YEARS = 2;
14910
+ var PEER_LOWER = { major: 15, minor: 5, patch: 21 };
14911
+ var PEER_16_FLOOR = { major: 16, minor: 2, patch: 11 };
14912
+ function parseNextVersion(raw) {
14913
+ const match = /^\s*(\d+)\.(\d+)\.(\d+)/.exec(raw);
14914
+ if (!match) return null;
14915
+ return {
14916
+ major: Number(match[1]),
14917
+ minor: Number(match[2]),
14918
+ patch: Number(match[3])
14919
+ };
14920
+ }
14921
+ function declaredNextMajor(range) {
14922
+ const trimmed = range.trim();
14923
+ if (trimmed === "") return null;
14924
+ const match = /^[\^~]?(\d+)(?:\.\d+)*$/.exec(trimmed);
14925
+ return match ? Number(match[1]) : null;
14926
+ }
14927
+ function supportEndsAt(major) {
14928
+ const released = NEXT_RELEASE_DATES[major];
14929
+ if (!released) return null;
14930
+ const end = new Date(released);
14931
+ end.setUTCFullYear(end.getUTCFullYear() + SUPPORT_WINDOW_YEARS);
14932
+ return end;
14933
+ }
14934
+ function compare(a, b) {
14935
+ return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
14936
+ }
14937
+ function withinPeerRange(version2) {
14938
+ if (version2.major === 15) return compare(version2, PEER_LOWER) >= 0;
14939
+ if (version2.major === 16) return compare(version2, PEER_16_FLOOR) >= 0;
14940
+ return version2.major > 16;
14941
+ }
14942
+ function formatDate(date5) {
14943
+ return date5.toISOString().slice(0, 10);
14944
+ }
14945
+ function classifyNextVersion(raw, now) {
14946
+ const version2 = parseNextVersion(raw);
14947
+ if (!version2) {
14948
+ return {
14949
+ status: "unknown",
14950
+ version: null,
14951
+ reason: `Could not read a Next.js version from "${raw}".`
14952
+ };
14953
+ }
14954
+ const windowEnd = supportEndsAt(version2.major);
14955
+ if (!windowEnd || version2.major < PEER_LOWER.major) {
14956
+ return {
14957
+ status: "too-old",
14958
+ version: version2,
14959
+ reason: `Next.js ${raw} is too old to deploy. @opennextjs/cloudflare supports Next.js 15.5.21 and newer. Upgrade to Next.js 16.`
14960
+ };
14961
+ }
14962
+ if (now > windowEnd) {
14963
+ return {
14964
+ status: "needs-unsupported-flag",
14965
+ version: version2,
14966
+ reason: `Next.js ${version2.major} left its support window on ${formatDate(windowEnd)}. The build continues, but unsupported releases stop receiving security patches \u2014 upgrade to Next.js 16. See https://nextjs.org/support-policy`
14967
+ };
14968
+ }
14969
+ if (!withinPeerRange(version2)) {
14970
+ return {
14971
+ status: "outside-peer-range",
14972
+ version: version2,
14973
+ reason: `Next.js ${raw} is outside the range @opennextjs/cloudflare declares support for (>=15.5.21 <16 || >=16.2.11). The build may fail in ways naro cannot explain.`
14974
+ };
14975
+ }
14976
+ return { status: "supported", version: version2, reason: "" };
14977
+ }
14978
+
14891
14979
  // ../../packages/build/src/package-manager.ts
14892
14980
  import { access, readFile as readFile2 } from "fs/promises";
14893
14981
  import { join as join2 } from "path";
@@ -14915,14 +15003,20 @@ async function detectPackageManager(root) {
14915
15003
  return {
14916
15004
  name: "pnpm",
14917
15005
  install: ["pnpm", "install", "--frozen-lockfile"],
14918
- cacheArgs: (dir) => ["--store-dir", dir]
15006
+ cacheArgs: (dir) => ["--store-dir", dir],
15007
+ // pnpm only warns about skipped esbuild/workerd build scripts here (it is
15008
+ // `pnpm install` that exits 1 over them), and a vinext build succeeds
15009
+ // without those postinstalls.
15010
+ addArgs: (specs) => ["pnpm", "add", ...specs]
14919
15011
  };
14920
15012
  }
14921
15013
  if (await exists(join2(root, "package-lock.json"))) {
14922
15014
  return {
14923
15015
  name: "npm",
14924
15016
  install: ["npm", "ci"],
14925
- cacheArgs: (dir) => ["--cache", dir]
15017
+ cacheArgs: (dir) => ["--cache", dir],
15018
+ // --no-save keeps package.json and the lockfile untouched.
15019
+ addArgs: (specs) => ["npm", "install", "--no-save", ...specs]
14926
15020
  };
14927
15021
  }
14928
15022
  if (await exists(join2(root, "yarn.lock"))) {
@@ -14935,7 +15029,8 @@ async function detectPackageManager(root) {
14935
15029
  modern ? "--immutable" : "--frozen-lockfile"
14936
15030
  ],
14937
15031
  // Berry takes no install-time cache flag; YARN_CACHE_FOLDER covers it.
14938
- cacheArgs: (dir) => modern ? [] : ["--cache-folder", dir]
15032
+ cacheArgs: (dir) => modern ? [] : ["--cache-folder", dir],
15033
+ addArgs: (specs) => ["yarn", "add", ...specs]
14939
15034
  };
14940
15035
  }
14941
15036
  throw new DeploymentError(
@@ -14980,215 +15075,22 @@ async function convertProxyToMiddleware(appRoot) {
14980
15075
  return { converted: false };
14981
15076
  }
14982
15077
 
14983
- // ../../packages/build/src/platform-config.ts
14984
- import { access as access3, writeFile as writeFile3 } from "fs/promises";
14985
- import { createRequire } from "module";
14986
- import { join as join4 } from "path";
14987
- var COMPATIBILITY_DATE = "2026-08-19";
14988
- var NEXT_CONFIG_FILES = [
14989
- "next.config.js",
14990
- "next.config.mjs",
14991
- "next.config.cjs",
14992
- "next.config.ts"
14993
- ];
14994
- async function pathExists(path) {
14995
- try {
14996
- await access3(path);
14997
- return true;
14998
- } catch {
14999
- return false;
15000
- }
15001
- }
15002
- async function createPackageManagerConfig(appRoot, writer = async (path, contents) => {
15003
- await writeFile3(path, contents, "utf8");
15004
- }) {
15005
- const packageManagerConfigPath = join4(appRoot, ".next-deploy-user.npmrc");
15006
- await writer(
15007
- packageManagerConfigPath,
15008
- "# Host package-manager credentials are intentionally unavailable.\n"
15009
- );
15010
- return packageManagerConfigPath;
15011
- }
15012
- async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
15013
- await writeFile3(path, contents, "utf8");
15014
- }, cloudflareModulePath = createRequire(import.meta.url).resolve(
15015
- "@opennextjs/cloudflare"
15016
- ), exists3 = pathExists) {
15017
- const openNextConfigPath = join4(appRoot, ".next-deploy-open-next.config.mjs");
15018
- const wranglerConfigPath = join4(appRoot, ".next-deploy-wrangler.json");
15019
- const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
15020
- let hasNextConfig = false;
15021
- for (const name of NEXT_CONFIG_FILES) {
15022
- if (await exists3(join4(appRoot, name))) {
15023
- hasNextConfig = true;
15024
- break;
15025
- }
15026
- }
15027
- if (!hasNextConfig) {
15028
- await writer(
15029
- join4(appRoot, "next.config.mjs"),
15030
- 'export default { output: "standalone" };\n'
15031
- );
15032
- }
15033
- await writer(
15034
- openNextConfigPath,
15035
- `import { defineCloudflareConfig } from ${JSON.stringify(cloudflareModulePath)};
15036
-
15037
- export default defineCloudflareConfig({});
15038
- `
15039
- );
15040
- await writer(
15041
- wranglerConfigPath,
15042
- `${JSON.stringify(
15043
- {
15044
- name: `next-deploy-${safeDeploymentId}`,
15045
- main: ".open-next/worker.js",
15046
- compatibility_date: COMPATIBILITY_DATE,
15047
- compatibility_flags: ["nodejs_compat"],
15048
- assets: {
15049
- directory: ".open-next/assets",
15050
- binding: "ASSETS"
15051
- }
15052
- },
15053
- null,
15054
- 2
15055
- )}
15056
- `
15057
- );
15058
- const packageManagerConfigPath = await createPackageManagerConfig(
15059
- appRoot,
15060
- writer
15061
- );
15062
- return {
15063
- openNextConfigPath,
15064
- wranglerConfigPath,
15065
- packageManagerConfigPath
15066
- };
15067
- }
15068
-
15069
- // ../../packages/build/src/frameworks.ts
15070
- import { readFile as readFile4 } from "fs/promises";
15071
- import { join as join5 } from "path";
15072
- var FRAMEWORK_PRESETS = {
15073
- next: {
15074
- id: "next",
15075
- detectDependency: "next",
15076
- defaultBuildScript: "build",
15077
- defaultOutputDirectory: ".open-next",
15078
- needsServerRuntime: true,
15079
- spaFallback: false,
15080
- cachePath: ".next/cache"
15081
- },
15082
- vite: {
15083
- id: "vite",
15084
- detectDependency: "vite",
15085
- defaultBuildScript: "build",
15086
- defaultOutputDirectory: "dist",
15087
- needsServerRuntime: false,
15088
- spaFallback: true,
15089
- cachePath: "node_modules/.vite"
15090
- },
15091
- cra: {
15092
- id: "cra",
15093
- detectDependency: "react-scripts",
15094
- defaultBuildScript: "build",
15095
- defaultOutputDirectory: "build",
15096
- needsServerRuntime: false,
15097
- spaFallback: true,
15098
- cachePath: "node_modules/.cache"
15099
- },
15100
- static: {
15101
- id: "static",
15102
- detectDependency: null,
15103
- defaultBuildScript: null,
15104
- defaultOutputDirectory: ".",
15105
- needsServerRuntime: false,
15106
- spaFallback: false,
15107
- cachePath: null
15108
- }
15109
- };
15110
- var DETECTION_ORDER = ["next", "vite", "cra"];
15111
- var FRAMEWORK_LABELS = {
15112
- next: "Next.js",
15113
- vite: "Vite",
15114
- cra: "Create React App",
15115
- static: "static files"
15116
- };
15117
- function dependencyVersion(packageJson, name) {
15118
- const version2 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
15119
- return typeof version2 === "string" && version2.trim() !== "" ? version2 : null;
15120
- }
15121
- async function readPackageJson(root) {
15122
- let source;
15123
- try {
15124
- source = await readFile4(join5(root, "package.json"), "utf8");
15125
- } catch (error51) {
15126
- if (error51.code === "ENOENT") return null;
15127
- throw new DeploymentError(
15128
- "FRAMEWORK_DETECTION_FAILED",
15129
- "Could not read package.json.",
15130
- { cause: error51 }
15131
- );
15132
- }
15133
- try {
15134
- return JSON.parse(source);
15135
- } catch (error51) {
15136
- throw new DeploymentError(
15137
- "FRAMEWORK_DETECTION_FAILED",
15138
- "The selected root does not contain a valid package.json.",
15139
- { cause: error51 }
15140
- );
15141
- }
15142
- }
15143
- async function detectFramework(root, overrides = {}) {
15144
- const packageJson = await readPackageJson(root);
15145
- let preset;
15146
- if (overrides.framework) {
15147
- const candidate = FRAMEWORK_PRESETS[overrides.framework] ?? null;
15148
- if (!candidate || candidate.id !== overrides.framework)
15149
- throw new DeploymentError(
15150
- "FRAMEWORK_DETECTION_FAILED",
15151
- `Unknown framework "${overrides.framework}".`
15152
- );
15153
- preset = candidate;
15154
- } else if (!packageJson) {
15155
- preset = FRAMEWORK_PRESETS.static;
15156
- } else {
15157
- const detected = DETECTION_ORDER.map((id) => FRAMEWORK_PRESETS[id]).find(
15158
- (candidate) => candidate.detectDependency !== null && dependencyVersion(packageJson, candidate.detectDependency) !== null
15159
- );
15160
- if (!detected)
15161
- throw new DeploymentError(
15162
- "FRAMEWORK_DETECTION_FAILED",
15163
- "Could not detect the project framework from package.json. Set a framework (and build command / output directory) in the project settings."
15164
- );
15165
- preset = detected;
15166
- }
15167
- const buildScript = overrides.buildCommand ?? preset.defaultBuildScript;
15168
- if (buildScript !== null) {
15169
- if (!packageJson)
15170
- throw new DeploymentError(
15171
- "FRAMEWORK_DETECTION_FAILED",
15172
- "A build command requires a package.json."
15173
- );
15174
- if (typeof packageJson.scripts?.[buildScript] !== "string")
15175
- throw new DeploymentError(
15176
- "BUILD_FAILED",
15177
- `The project must declare a "${buildScript}" script.`
15178
- );
15179
- }
15180
- const version2 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
15181
- return {
15182
- preset,
15183
- buildScript,
15184
- outputDirectory: overrides.outputDirectory ?? preset.defaultOutputDirectory,
15185
- description: version2 ? `${FRAMEWORK_LABELS[preset.id]} ${version2}` : FRAMEWORK_LABELS[preset.id]
15186
- };
15187
- }
15078
+ // ../../packages/build/src/vinext.ts
15079
+ import { access as access5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
15080
+ import { join as join6, resolve as resolve2 } from "path";
15188
15081
 
15189
15082
  // ../../packages/build/src/open-next.ts
15190
- import { access as access4, readdir, readFile as readFile5 } from "fs/promises";
15191
- import { extname, isAbsolute, join as join6, relative, resolve, sep } from "path";
15083
+ import { access as access3, readdir, readFile as readFile4 } from "fs/promises";
15084
+ import {
15085
+ basename,
15086
+ dirname,
15087
+ extname,
15088
+ isAbsolute,
15089
+ join as join4,
15090
+ relative,
15091
+ resolve,
15092
+ sep
15093
+ } from "path";
15192
15094
  function parseJsonc(source) {
15193
15095
  let output = "";
15194
15096
  let index = 0;
@@ -15228,19 +15130,19 @@ function parseJsonc(source) {
15228
15130
  }
15229
15131
  return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
15230
15132
  }
15231
- function confinedPath(root, configuredPath, label) {
15133
+ function confinedPath(root, configuredPath, label2, base2 = root) {
15232
15134
  if (isAbsolute(configuredPath)) {
15233
15135
  throw new DeploymentError(
15234
15136
  "ARTIFACT_INVALID",
15235
- `${label} must be relative to the application root.`
15137
+ `${label2} must be relative to the application root.`
15236
15138
  );
15237
15139
  }
15238
- const resolved = resolve(root, configuredPath);
15140
+ const resolved = resolve(base2, configuredPath);
15239
15141
  const relation = relative(root, resolved);
15240
15142
  if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) {
15241
15143
  throw new DeploymentError(
15242
15144
  "ARTIFACT_INVALID",
15243
- `${label} escapes the application root.`
15145
+ `${label2} escapes the application root.`
15244
15146
  );
15245
15147
  }
15246
15148
  return resolved;
@@ -15294,25 +15196,25 @@ function contentType(path) {
15294
15196
  return "application/octet-stream";
15295
15197
  }
15296
15198
  }
15297
- async function listFiles(root) {
15199
+ async function listArtifactFiles(root, label2 = "OpenNext") {
15298
15200
  const files = [];
15299
15201
  async function visit(directory) {
15300
15202
  const entries = await readdir(directory, { withFileTypes: true });
15301
15203
  for (const entry of entries.sort(
15302
15204
  (left, right) => left.name.localeCompare(right.name)
15303
15205
  )) {
15304
- const path = join6(directory, entry.name);
15206
+ const path = join4(directory, entry.name);
15305
15207
  if (entry.isSymbolicLink()) {
15306
15208
  throw new DeploymentError(
15307
15209
  "ARTIFACT_INVALID",
15308
- "OpenNext output cannot contain symbolic links."
15210
+ `${label2} output cannot contain symbolic links.`
15309
15211
  );
15310
15212
  }
15311
15213
  if (entry.isDirectory()) await visit(path);
15312
15214
  else if (entry.isFile()) {
15313
15215
  files.push({
15314
15216
  name: relative(root, path).split(sep).join("/"),
15315
- data: await readFile5(path),
15217
+ data: await readFile4(path),
15316
15218
  contentType: contentType(path)
15317
15219
  });
15318
15220
  }
@@ -15331,13 +15233,13 @@ async function findWranglerConfig(root, configuredPath) {
15331
15233
  "Wrangler config escapes the application root."
15332
15234
  );
15333
15235
  }
15334
- await access4(resolved);
15236
+ await access3(resolved);
15335
15237
  return resolved;
15336
15238
  }
15337
15239
  for (const name of ["wrangler.jsonc", "wrangler.json"]) {
15338
- const path = join6(root, name);
15240
+ const path = join4(root, name);
15339
15241
  try {
15340
- await access4(path);
15242
+ await access3(path);
15341
15243
  return path;
15342
15244
  } catch {
15343
15245
  }
@@ -15347,11 +15249,26 @@ async function findWranglerConfig(root, configuredPath) {
15347
15249
  "A wrangler.jsonc or wrangler.json file is required."
15348
15250
  );
15349
15251
  }
15252
+ var OPEN_NEXT_LAYOUT = {
15253
+ outputRoot: ".open-next",
15254
+ bundleDir: ".open-next/worker-bundle"
15255
+ };
15256
+ var listFiles = (root) => listArtifactFiles(root);
15350
15257
  async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
15258
+ const configPath = await findWranglerConfig(appRoot, wranglerConfigPath);
15259
+ return readWorkerArtifact(appRoot, identity, {
15260
+ wranglerConfigPath: configPath,
15261
+ ...OPEN_NEXT_LAYOUT
15262
+ });
15263
+ }
15264
+ async function readWorkerArtifact(appRoot, identity, layout) {
15351
15265
  try {
15352
- const configPath = await findWranglerConfig(appRoot, wranglerConfigPath);
15266
+ const configPath = await findWranglerConfig(
15267
+ appRoot,
15268
+ layout.wranglerConfigPath
15269
+ );
15353
15270
  const metadata = parseJsonc(
15354
- await readFile5(configPath, "utf8")
15271
+ await readFile4(configPath, "utf8")
15355
15272
  );
15356
15273
  if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
15357
15274
  throw new DeploymentError(
@@ -15359,43 +15276,50 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
15359
15276
  "Wrangler metadata must declare main and compatibility_date."
15360
15277
  );
15361
15278
  }
15362
- const outputRoot = resolve(appRoot, ".open-next");
15279
+ const configDir = dirname(configPath);
15280
+ const outputRoot = confinedPath(appRoot, layout.outputRoot, "Output root");
15363
15281
  const entrypointPath = confinedPath(
15364
15282
  appRoot,
15365
15283
  metadata.main,
15366
- "Worker entrypoint"
15284
+ "Worker entrypoint",
15285
+ configDir
15367
15286
  );
15368
15287
  const entryRelation = relative(outputRoot, entrypointPath);
15369
15288
  if (entryRelation === ".." || entryRelation.startsWith(`..${sep}`) || isAbsolute(entryRelation)) {
15370
15289
  throw new DeploymentError(
15371
15290
  "ARTIFACT_INVALID",
15372
- "Worker entrypoint must be inside .open-next."
15291
+ `Worker entrypoint must be inside ${layout.outputRoot}.`
15373
15292
  );
15374
15293
  }
15375
- await access4(entrypointPath);
15376
- const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(appRoot, metadata.assets.directory, "Assets directory") : join6(outputRoot, "assets");
15294
+ await access3(entrypointPath);
15295
+ const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(
15296
+ appRoot,
15297
+ metadata.assets.directory,
15298
+ "Assets directory",
15299
+ configDir
15300
+ ) : join4(outputRoot, "assets");
15377
15301
  const assetsRelation = relative(outputRoot, configuredAssets);
15378
15302
  if (assetsRelation === ".." || assetsRelation.startsWith(`..${sep}`) || isAbsolute(assetsRelation)) {
15379
15303
  throw new DeploymentError(
15380
15304
  "ARTIFACT_INVALID",
15381
- "Assets directory must be inside .open-next."
15305
+ `Assets directory must be inside ${layout.outputRoot}.`
15382
15306
  );
15383
15307
  }
15384
- const bundleRoot = join6(outputRoot, "worker-bundle");
15308
+ const bundleRoot = confinedPath(appRoot, layout.bundleDir, "Bundle dir");
15385
15309
  let bundleFiles = null;
15386
15310
  try {
15387
- await access4(bundleRoot);
15311
+ await access3(bundleRoot);
15388
15312
  bundleFiles = await listFiles(bundleRoot);
15389
15313
  } catch {
15390
15314
  }
15391
15315
  const outputFiles = bundleFiles ?? await listFiles(outputRoot);
15392
- const entrypointName = bundleFiles ? "worker.js" : relative(outputRoot, entrypointPath).split(sep).join("/");
15316
+ const entrypointName = bundleFiles ? basename(entrypointPath) : relative(outputRoot, entrypointPath).split(sep).join("/");
15393
15317
  const assetsPrefix = `${relative(outputRoot, configuredAssets).split(sep).join("/")}/`;
15394
15318
  const entrypoint = outputFiles.find((file2) => file2.name === entrypointName);
15395
15319
  if (!entrypoint)
15396
15320
  throw new DeploymentError(
15397
15321
  "ARTIFACT_INVALID",
15398
- "OpenNext worker entrypoint is missing."
15322
+ "Worker entrypoint is missing from the build output."
15399
15323
  );
15400
15324
  return {
15401
15325
  ...identity,
@@ -15414,28 +15338,483 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
15414
15338
  if (error51 instanceof DeploymentError) throw error51;
15415
15339
  throw new DeploymentError(
15416
15340
  "ARTIFACT_INVALID",
15417
- "Could not read the OpenNext deployment output.",
15341
+ "Could not read the Worker build output.",
15418
15342
  { cause: error51 }
15419
15343
  );
15420
15344
  }
15421
15345
  }
15422
15346
 
15423
- // ../../packages/build/src/process.ts
15424
- import { spawn } from "child_process";
15425
- import { createInterface } from "readline";
15426
-
15427
- // ../../packages/build/src/redact.ts
15428
- var SECRET_ASSIGNMENT = /\b(CLOUDFLARE_API_TOKEN|SUPABASE_SERVICE_ROLE_KEY|BUILD_CALLBACK_SECRET)=([^\s]+)/gi;
15429
- function redactLog(message) {
15430
- return message.replace(/\bBearer\s+[^\s]+/gi, "Bearer [REDACTED]").replace(/(https?:\/\/)[^/@\s]+@/gi, "$1[REDACTED]@").replace(SECRET_ASSIGNMENT, "$1=[REDACTED]");
15347
+ // ../../packages/build/src/platform-config.ts
15348
+ import { access as access4, writeFile as writeFile3 } from "fs/promises";
15349
+ import { createRequire } from "module";
15350
+ import { join as join5 } from "path";
15351
+ var COMPATIBILITY_DATE = "2026-08-19";
15352
+ var NEXT_CONFIG_FILES = [
15353
+ "next.config.js",
15354
+ "next.config.mjs",
15355
+ "next.config.cjs",
15356
+ "next.config.ts"
15357
+ ];
15358
+ async function pathExists(path) {
15359
+ try {
15360
+ await access4(path);
15361
+ return true;
15362
+ } catch {
15363
+ return false;
15364
+ }
15431
15365
  }
15432
-
15433
- // ../../packages/build/src/process.ts
15434
- var CommandExecutionError = class extends DeploymentError {
15435
- exitCode;
15436
- timedOut;
15437
- constructor(code, message, options) {
15438
- super(
15366
+ async function createPackageManagerConfig(appRoot, writer = async (path, contents) => {
15367
+ await writeFile3(path, contents, "utf8");
15368
+ }) {
15369
+ const packageManagerConfigPath = join5(appRoot, ".next-deploy-user.npmrc");
15370
+ await writer(
15371
+ packageManagerConfigPath,
15372
+ "# Host package-manager credentials are intentionally unavailable.\n"
15373
+ );
15374
+ return packageManagerConfigPath;
15375
+ }
15376
+ async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
15377
+ await writeFile3(path, contents, "utf8");
15378
+ }, cloudflareModulePath = createRequire(import.meta.url).resolve(
15379
+ "@opennextjs/cloudflare"
15380
+ ), exists5 = pathExists) {
15381
+ const openNextConfigPath = join5(appRoot, ".next-deploy-open-next.config.mjs");
15382
+ const wranglerConfigPath = join5(appRoot, ".next-deploy-wrangler.json");
15383
+ const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
15384
+ let hasNextConfig = false;
15385
+ for (const name of NEXT_CONFIG_FILES) {
15386
+ if (await exists5(join5(appRoot, name))) {
15387
+ hasNextConfig = true;
15388
+ break;
15389
+ }
15390
+ }
15391
+ if (!hasNextConfig) {
15392
+ await writer(
15393
+ join5(appRoot, "next.config.mjs"),
15394
+ 'export default { output: "standalone" };\n'
15395
+ );
15396
+ }
15397
+ await writer(
15398
+ openNextConfigPath,
15399
+ `import { defineCloudflareConfig } from ${JSON.stringify(cloudflareModulePath)};
15400
+
15401
+ export default defineCloudflareConfig({});
15402
+ `
15403
+ );
15404
+ await writer(
15405
+ wranglerConfigPath,
15406
+ `${JSON.stringify(
15407
+ {
15408
+ name: `next-deploy-${safeDeploymentId}`,
15409
+ main: ".open-next/worker.js",
15410
+ compatibility_date: COMPATIBILITY_DATE,
15411
+ compatibility_flags: ["nodejs_compat"],
15412
+ assets: {
15413
+ directory: ".open-next/assets",
15414
+ binding: "ASSETS"
15415
+ }
15416
+ },
15417
+ null,
15418
+ 2
15419
+ )}
15420
+ `
15421
+ );
15422
+ const packageManagerConfigPath = await createPackageManagerConfig(
15423
+ appRoot,
15424
+ writer
15425
+ );
15426
+ return {
15427
+ openNextConfigPath,
15428
+ wranglerConfigPath,
15429
+ packageManagerConfigPath
15430
+ };
15431
+ }
15432
+
15433
+ // ../../packages/build/src/vinext.ts
15434
+ var VINEXT_TOOLCHAIN = [
15435
+ "vinext@1.0.0-beta.9",
15436
+ "@cloudflare/vite-plugin@1.54.7",
15437
+ "@vitejs/plugin-react@6.1.1",
15438
+ "@vitejs/plugin-rsc@0.5.34",
15439
+ "react-server-dom-webpack@19.2.8",
15440
+ "vite@8.2.2"
15441
+ ];
15442
+ var VINEXT_COMPATIBILITY_DATE = COMPATIBILITY_DATE;
15443
+ var VITE_CONFIG_NAMES = [
15444
+ "vite.config.js",
15445
+ "vite.config.mjs",
15446
+ "vite.config.cjs",
15447
+ "vite.config.ts",
15448
+ "vite.config.mts",
15449
+ "vite.config.cts"
15450
+ ];
15451
+ var GENERATED_VITE_CONFIG = "vite.config.mjs";
15452
+ var OUTPUT_DIR = "dist";
15453
+ var SERVER_DIR = "server";
15454
+ var CLIENT_DIR = "client";
15455
+ var WRANGLER_OUTPUT_NAME = "wrangler.json";
15456
+ async function exists3(path) {
15457
+ try {
15458
+ await access5(path);
15459
+ return true;
15460
+ } catch {
15461
+ return false;
15462
+ }
15463
+ }
15464
+ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
15465
+ await writeFile4(path, contents, "utf8");
15466
+ }) {
15467
+ for (const name of VITE_CONFIG_NAMES) {
15468
+ if (await exists3(join6(appRoot, name))) {
15469
+ throw new DeploymentError(
15470
+ "ARTIFACT_INVALID",
15471
+ `The project already has a ${name}. vinext offers no way to point at a different config, and overwriting it would change how the project's own tooling builds.`
15472
+ );
15473
+ }
15474
+ }
15475
+ const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
15476
+ const viteConfigPath = join6(appRoot, GENERATED_VITE_CONFIG);
15477
+ const wranglerConfigPath = join6(appRoot, "wrangler.jsonc");
15478
+ await writer(
15479
+ viteConfigPath,
15480
+ [
15481
+ 'import { defineConfig } from "vite";',
15482
+ 'import vinext from "vinext";',
15483
+ 'import { cloudflare } from "@cloudflare/vite-plugin";',
15484
+ "",
15485
+ "export default defineConfig({",
15486
+ " plugins: [",
15487
+ " vinext(),",
15488
+ ' cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] } }),',
15489
+ " ],",
15490
+ "});",
15491
+ ""
15492
+ ].join("\n")
15493
+ );
15494
+ await writer(
15495
+ wranglerConfigPath,
15496
+ `${JSON.stringify(
15497
+ {
15498
+ name: `naro-vinext-${safeDeploymentId}`,
15499
+ compatibility_date: VINEXT_COMPATIBILITY_DATE,
15500
+ compatibility_flags: ["nodejs_compat"],
15501
+ main: "vinext/server/fetch-handler",
15502
+ assets: {
15503
+ directory: `${OUTPUT_DIR}/${CLIENT_DIR}`,
15504
+ binding: "ASSETS"
15505
+ }
15506
+ },
15507
+ null,
15508
+ 2
15509
+ )}
15510
+ `
15511
+ );
15512
+ return { viteConfigPath, wranglerConfigPath };
15513
+ }
15514
+ var AUTO_FIXED_ISSUES = [/Missing "type": "module"/i];
15515
+ var ISSUE_LINE = /^\s*✗\s+(.+)$/;
15516
+ var ANSI_ESCAPE = /\u001B\[[0-9;]*m/g;
15517
+ var SUMMARY_LINE = /Overall:\s*(\d+)%\s*compatible\s*\(\s*(\d+)\s+supported,\s*(\d+)\s+partial,\s*(\d+)\s+issues?\s*\)/i;
15518
+ function parseVinextCheckReport(output) {
15519
+ const plain = output.replace(ANSI_ESCAPE, "");
15520
+ const match = SUMMARY_LINE.exec(plain);
15521
+ if (!match) return null;
15522
+ const issueLines = [
15523
+ ...new Set(
15524
+ plain.split("\n").map((line) => ISSUE_LINE.exec(line)?.[1]?.trim()).filter((line) => Boolean(line))
15525
+ )
15526
+ ];
15527
+ return {
15528
+ percent: Number(match[1]),
15529
+ supported: Number(match[2]),
15530
+ partial: Number(match[3]),
15531
+ issues: Number(match[4]),
15532
+ issueLines
15533
+ };
15534
+ }
15535
+ function vinextRouteFromCheck(report) {
15536
+ if (!report)
15537
+ return {
15538
+ eligible: false,
15539
+ reason: "vinext check output could not be read; staying on OpenNext. Expected if vinext changed its report format."
15540
+ };
15541
+ const autoFixed = report.issueLines.filter(
15542
+ (line) => AUTO_FIXED_ISSUES.some((pattern) => pattern.test(line))
15543
+ );
15544
+ const blocking = Math.max(0, report.issues - autoFixed.length);
15545
+ if (blocking > 0)
15546
+ return {
15547
+ eligible: false,
15548
+ reason: `vinext check reported ${blocking} unsupported item(s) (${report.percent}% compatible); staying on OpenNext.`
15549
+ };
15550
+ if (autoFixed.length > 0)
15551
+ return {
15552
+ eligible: true,
15553
+ reason: `vinext check found only issues vinext build fixes itself (${autoFixed.join("; ")}); attempting a vinext build.`
15554
+ };
15555
+ return {
15556
+ eligible: true,
15557
+ reason: report.partial > 0 ? `vinext check found no blocking issues (${report.partial} partially supported); attempting a vinext build.` : "vinext check found no issues; attempting a vinext build."
15558
+ };
15559
+ }
15560
+ function globToRegExp(glob) {
15561
+ let source = "";
15562
+ for (let index = 0; index < glob.length; index += 1) {
15563
+ const char = glob[index];
15564
+ if (char !== "*") {
15565
+ source += char?.replace(/[.+?^${}()|[\]\\]/g, String.raw`\$&`) ?? "";
15566
+ continue;
15567
+ }
15568
+ if (glob[index + 1] !== "*") {
15569
+ source += "[^/]*";
15570
+ continue;
15571
+ }
15572
+ if (glob[index + 2] === "/") {
15573
+ source += "(?:[^/]*/)*";
15574
+ index += 2;
15575
+ } else {
15576
+ source += ".*";
15577
+ index += 1;
15578
+ }
15579
+ }
15580
+ return new RegExp(`^${source}$`);
15581
+ }
15582
+ function moduleMatchers(rules) {
15583
+ if (!Array.isArray(rules)) return [];
15584
+ return rules.flatMap((rule) => {
15585
+ const globs = rule.globs;
15586
+ return Array.isArray(globs) ? globs : [];
15587
+ }).filter((glob) => typeof glob === "string").map(globToRegExp);
15588
+ }
15589
+ async function readVinextArtifact(appRoot, identity) {
15590
+ try {
15591
+ const outputRoot = resolve2(appRoot, OUTPUT_DIR);
15592
+ const serverRoot = join6(outputRoot, SERVER_DIR);
15593
+ const clientRoot = join6(outputRoot, CLIENT_DIR);
15594
+ const configPath = join6(serverRoot, WRANGLER_OUTPUT_NAME);
15595
+ if (!await exists3(configPath)) {
15596
+ throw new DeploymentError(
15597
+ "ARTIFACT_INVALID",
15598
+ `vinext build produced no ${OUTPUT_DIR}/${SERVER_DIR}/${WRANGLER_OUTPUT_NAME}.`
15599
+ );
15600
+ }
15601
+ const metadata = JSON.parse(
15602
+ await readFile5(configPath, "utf8")
15603
+ );
15604
+ if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
15605
+ throw new DeploymentError(
15606
+ "ARTIFACT_INVALID",
15607
+ "vinext wrangler output must declare main and compatibility_date."
15608
+ );
15609
+ }
15610
+ const entrypointName = metadata.main.split(/[\\/]/).join("/");
15611
+ const matchers = moduleMatchers(metadata.rules);
15612
+ const isModule = (name) => matchers.length === 0 || matchers.some((matcher) => matcher.test(name));
15613
+ const serverFiles = await listArtifactFiles(serverRoot, "vinext");
15614
+ const entrypoint = serverFiles.find((file2) => file2.name === entrypointName);
15615
+ if (!entrypoint) {
15616
+ throw new DeploymentError(
15617
+ "ARTIFACT_INVALID",
15618
+ `vinext worker entrypoint "${entrypointName}" is missing from the build output.`
15619
+ );
15620
+ }
15621
+ return {
15622
+ ...identity,
15623
+ entrypoint,
15624
+ modules: serverFiles.filter(
15625
+ (file2) => file2.name !== entrypointName && file2.name !== WRANGLER_OUTPUT_NAME && !file2.name.endsWith(".map") && isModule(file2.name)
15626
+ ),
15627
+ assets: await listArtifactFiles(clientRoot, "vinext"),
15628
+ compatibilityDate: metadata.compatibility_date,
15629
+ compatibilityFlags: Array.isArray(metadata.compatibility_flags) ? metadata.compatibility_flags.filter(
15630
+ (flag) => typeof flag === "string"
15631
+ ) : [],
15632
+ assetsBinding: typeof metadata.assets?.binding === "string" ? metadata.assets.binding : "ASSETS"
15633
+ };
15634
+ } catch (error51) {
15635
+ if (error51 instanceof DeploymentError) throw error51;
15636
+ throw new DeploymentError(
15637
+ "ARTIFACT_INVALID",
15638
+ "Could not read the vinext deployment output.",
15639
+ { cause: error51 }
15640
+ );
15641
+ }
15642
+ }
15643
+
15644
+ // ../../packages/build/src/frameworks.ts
15645
+ import { readFile as readFile6 } from "fs/promises";
15646
+ import { join as join7 } from "path";
15647
+ var FRAMEWORK_PRESETS = {
15648
+ next: {
15649
+ id: "next",
15650
+ detectDependency: "next",
15651
+ defaultBuildScript: "build",
15652
+ defaultOutputDirectory: ".open-next",
15653
+ needsServerRuntime: true,
15654
+ spaFallback: false,
15655
+ cachePath: ".next/cache"
15656
+ },
15657
+ "next-export": {
15658
+ id: "next-export",
15659
+ detectDependency: "next",
15660
+ defaultBuildScript: "build",
15661
+ defaultOutputDirectory: "out",
15662
+ needsServerRuntime: false,
15663
+ spaFallback: false,
15664
+ cachePath: ".next/cache"
15665
+ },
15666
+ vite: {
15667
+ id: "vite",
15668
+ detectDependency: "vite",
15669
+ defaultBuildScript: "build",
15670
+ defaultOutputDirectory: "dist",
15671
+ needsServerRuntime: false,
15672
+ spaFallback: true,
15673
+ cachePath: "node_modules/.vite"
15674
+ },
15675
+ cra: {
15676
+ id: "cra",
15677
+ detectDependency: "react-scripts",
15678
+ defaultBuildScript: "build",
15679
+ defaultOutputDirectory: "build",
15680
+ needsServerRuntime: false,
15681
+ spaFallback: true,
15682
+ cachePath: "node_modules/.cache"
15683
+ },
15684
+ static: {
15685
+ id: "static",
15686
+ detectDependency: null,
15687
+ defaultBuildScript: null,
15688
+ defaultOutputDirectory: ".",
15689
+ needsServerRuntime: false,
15690
+ spaFallback: false,
15691
+ cachePath: null
15692
+ }
15693
+ };
15694
+ var DETECTION_ORDER = ["next", "vite", "cra"];
15695
+ var FRAMEWORK_LABELS = {
15696
+ next: "Next.js",
15697
+ "next-export": "Next.js static export",
15698
+ vite: "Vite",
15699
+ cra: "Create React App",
15700
+ static: "static files"
15701
+ };
15702
+ function dependencyVersion(packageJson, name) {
15703
+ const version2 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
15704
+ return typeof version2 === "string" && version2.trim() !== "" ? version2 : null;
15705
+ }
15706
+ var STATIC_EXPORT = /(?:^|[\s,{;])output\s*:\s*(["'`])export\1/;
15707
+ function declaresStaticExport(source) {
15708
+ const code = source.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/(^|[^:])\/\/.*$/gm, "$1");
15709
+ return STATIC_EXPORT.test(code);
15710
+ }
15711
+ async function usesStaticExport(root) {
15712
+ for (const name of NEXT_CONFIG_FILES) {
15713
+ try {
15714
+ return declaresStaticExport(await readFile6(join7(root, name), "utf8"));
15715
+ } catch {
15716
+ continue;
15717
+ }
15718
+ }
15719
+ return false;
15720
+ }
15721
+ async function readPackageJson(root) {
15722
+ let source;
15723
+ try {
15724
+ source = await readFile6(join7(root, "package.json"), "utf8");
15725
+ } catch (error51) {
15726
+ if (error51.code === "ENOENT") return null;
15727
+ throw new DeploymentError(
15728
+ "FRAMEWORK_DETECTION_FAILED",
15729
+ "Could not read package.json.",
15730
+ { cause: error51 }
15731
+ );
15732
+ }
15733
+ try {
15734
+ return JSON.parse(source);
15735
+ } catch (error51) {
15736
+ throw new DeploymentError(
15737
+ "FRAMEWORK_DETECTION_FAILED",
15738
+ "The selected root does not contain a valid package.json.",
15739
+ { cause: error51 }
15740
+ );
15741
+ }
15742
+ }
15743
+ async function detectFramework(root, overrides = {}) {
15744
+ const packageJson = await readPackageJson(root);
15745
+ let preset;
15746
+ if (overrides.framework) {
15747
+ const candidate = FRAMEWORK_PRESETS[overrides.framework] ?? null;
15748
+ if (!candidate || candidate.id !== overrides.framework)
15749
+ throw new DeploymentError(
15750
+ "FRAMEWORK_DETECTION_FAILED",
15751
+ `Unknown framework "${overrides.framework}".`
15752
+ );
15753
+ preset = candidate;
15754
+ } else if (!packageJson) {
15755
+ preset = FRAMEWORK_PRESETS.static;
15756
+ } else {
15757
+ const detected = DETECTION_ORDER.map((id) => FRAMEWORK_PRESETS[id]).find(
15758
+ (candidate) => candidate.detectDependency !== null && dependencyVersion(packageJson, candidate.detectDependency) !== null
15759
+ );
15760
+ if (!detected)
15761
+ throw new DeploymentError(
15762
+ "FRAMEWORK_DETECTION_FAILED",
15763
+ "Could not detect the project framework from package.json. Set a framework (and build command / output directory) in the project settings."
15764
+ );
15765
+ preset = detected;
15766
+ }
15767
+ if (preset.id === "next" && await usesStaticExport(root))
15768
+ preset = FRAMEWORK_PRESETS["next-export"];
15769
+ const buildScript = overrides.buildCommand ?? preset.defaultBuildScript;
15770
+ if (buildScript !== null) {
15771
+ if (!packageJson)
15772
+ throw new DeploymentError(
15773
+ "FRAMEWORK_DETECTION_FAILED",
15774
+ "A build command requires a package.json."
15775
+ );
15776
+ if (typeof packageJson.scripts?.[buildScript] !== "string")
15777
+ throw new DeploymentError(
15778
+ "BUILD_FAILED",
15779
+ `The project must declare a "${buildScript}" script.`
15780
+ );
15781
+ }
15782
+ const version2 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
15783
+ return {
15784
+ preset,
15785
+ buildScript,
15786
+ outputDirectory: overrides.outputDirectory ?? preset.defaultOutputDirectory,
15787
+ description: version2 ? `${FRAMEWORK_LABELS[preset.id]} ${version2}` : FRAMEWORK_LABELS[preset.id],
15788
+ dependencyRange: version2
15789
+ };
15790
+ }
15791
+
15792
+ // ../../packages/build/src/process.ts
15793
+ import { spawn } from "child_process";
15794
+ import { createInterface } from "readline";
15795
+
15796
+ // ../../packages/build/src/log-format.ts
15797
+ var CSI_SEQUENCE = /\u001B\[[0-9;?]*[\u0020-\u002F]*[@-~]/g;
15798
+ var OSC_SEQUENCE = /\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)/g;
15799
+ function formatLogLine(raw) {
15800
+ const line = raw.replace(/\r+$/, "");
15801
+ const lastFrame = line.slice(line.lastIndexOf("\r") + 1);
15802
+ const plain = lastFrame.replace(OSC_SEQUENCE, "").replace(CSI_SEQUENCE, "").trimEnd();
15803
+ return plain.trim() === "" ? null : plain;
15804
+ }
15805
+
15806
+ // ../../packages/build/src/redact.ts
15807
+ var SECRET_ASSIGNMENT = /\b(CLOUDFLARE_API_TOKEN|SUPABASE_SERVICE_ROLE_KEY|BUILD_CALLBACK_SECRET)=([^\s]+)/gi;
15808
+ function redactLog(message) {
15809
+ return message.replace(/\bBearer\s+[^\s]+/gi, "Bearer [REDACTED]").replace(/(https?:\/\/)[^/@\s]+@/gi, "$1[REDACTED]@").replace(SECRET_ASSIGNMENT, "$1=[REDACTED]");
15810
+ }
15811
+
15812
+ // ../../packages/build/src/process.ts
15813
+ var CommandExecutionError = class extends DeploymentError {
15814
+ exitCode;
15815
+ timedOut;
15816
+ constructor(code, message, options) {
15817
+ super(
15439
15818
  code,
15440
15819
  message,
15441
15820
  options.cause === void 0 ? void 0 : { cause: options.cause }
@@ -15457,202 +15836,1537 @@ var PASSTHROUGH_ENVIRONMENT = [
15457
15836
  "COREPACK_HOME",
15458
15837
  "XDG_CACHE_HOME"
15459
15838
  ];
15460
- var MAX_CAPTURED_LINES = 2e3;
15461
- function commandEnvironment(overrides) {
15462
- const environment = {
15463
- NODE_ENV: process.env.NODE_ENV ?? "production"
15839
+ var MAX_CAPTURED_LINES = 2e3;
15840
+ function commandEnvironment(overrides) {
15841
+ const environment = {
15842
+ NODE_ENV: process.env.NODE_ENV ?? "production"
15843
+ };
15844
+ for (const name of PASSTHROUGH_ENVIRONMENT) {
15845
+ const value = process.env[name];
15846
+ if (value !== void 0) environment[name] = value;
15847
+ }
15848
+ return { ...environment, ...overrides };
15849
+ }
15850
+ var startProcess = (spec, observer) => {
15851
+ const detached = process.platform !== "win32";
15852
+ const child = spawn(spec.command, [...spec.args], {
15853
+ cwd: spec.cwd,
15854
+ shell: false,
15855
+ env: commandEnvironment(spec.env),
15856
+ detached,
15857
+ stdio: ["ignore", "pipe", "pipe"]
15858
+ });
15859
+ const consume = async (input, level) => {
15860
+ const lines = createInterface({ input });
15861
+ for await (const message of lines) {
15862
+ const formatted = formatLogLine(message);
15863
+ if (formatted === null) continue;
15864
+ await observer.onLog({
15865
+ level,
15866
+ stage: spec.stage,
15867
+ message: redactLog(formatted)
15868
+ });
15869
+ }
15870
+ };
15871
+ const outputComplete = Promise.all([
15872
+ consume(child.stdout, "INFO"),
15873
+ consume(child.stderr, "WARN")
15874
+ ]);
15875
+ const kill = (signal) => {
15876
+ if (detached && child.pid) {
15877
+ try {
15878
+ process.kill(-child.pid, signal);
15879
+ return;
15880
+ } catch {
15881
+ }
15882
+ }
15883
+ child.kill(signal);
15884
+ };
15885
+ let exitCode = null;
15886
+ let done = false;
15887
+ const exited = new Promise((resolveExit) => {
15888
+ child.once("error", (error51) => {
15889
+ done = true;
15890
+ void observer.onLog({
15891
+ level: "WARN",
15892
+ stage: spec.stage,
15893
+ message: `Could not start ${spec.command}: ${error51.message}`
15894
+ });
15895
+ resolveExit(null);
15896
+ });
15897
+ child.once("close", (code) => {
15898
+ done = true;
15899
+ exitCode = code;
15900
+ resolveExit(code);
15901
+ });
15902
+ });
15903
+ let stopping;
15904
+ return {
15905
+ exited,
15906
+ stop: () => {
15907
+ stopping ??= (async () => {
15908
+ if (!done) {
15909
+ kill("SIGTERM");
15910
+ const forceKill = setTimeout(() => kill("SIGKILL"), 2e3);
15911
+ forceKill.unref();
15912
+ await exited;
15913
+ clearTimeout(forceKill);
15914
+ }
15915
+ await outputComplete;
15916
+ void exitCode;
15917
+ })();
15918
+ return stopping;
15919
+ }
15920
+ };
15921
+ };
15922
+ async function runCommand(spec, observer) {
15923
+ const detached = process.platform !== "win32";
15924
+ const child = spawn(spec.command, [...spec.args], {
15925
+ cwd: spec.cwd,
15926
+ shell: false,
15927
+ env: commandEnvironment(spec.env),
15928
+ detached,
15929
+ stdio: ["ignore", "pipe", "pipe"]
15930
+ });
15931
+ let timedOut = false;
15932
+ const captured = { stdout: [], stderr: [] };
15933
+ const consume = async (input, level) => {
15934
+ const lines = createInterface({ input });
15935
+ for await (const message of lines) {
15936
+ const output = captured[level === "INFO" ? "stdout" : "stderr"];
15937
+ if (output.length < MAX_CAPTURED_LINES) output.push(message);
15938
+ const formatted = formatLogLine(message);
15939
+ if (formatted === null) continue;
15940
+ await observer.onLog({
15941
+ level,
15942
+ stage: spec.stage,
15943
+ message: redactLog(formatted)
15944
+ });
15945
+ }
15946
+ };
15947
+ const outputComplete = Promise.all([
15948
+ consume(child.stdout, "INFO"),
15949
+ consume(child.stderr, "ERROR")
15950
+ ]);
15951
+ const kill = (signal) => {
15952
+ if (detached && child.pid) {
15953
+ try {
15954
+ process.kill(-child.pid, signal);
15955
+ return;
15956
+ } catch {
15957
+ }
15958
+ }
15959
+ child.kill(signal);
15960
+ };
15961
+ let forceKill;
15962
+ const timeout = setTimeout(() => {
15963
+ timedOut = true;
15964
+ kill("SIGTERM");
15965
+ forceKill = setTimeout(() => kill("SIGKILL"), 2e3);
15966
+ forceKill.unref();
15967
+ }, spec.timeoutMs);
15968
+ const outcome = await new Promise((resolveOutcome) => {
15969
+ child.once("error", (error51) => resolveOutcome({ type: "error", error: error51 }));
15970
+ child.once(
15971
+ "close",
15972
+ (exitCode, signal) => resolveOutcome({ type: "close", exitCode, signal })
15973
+ );
15974
+ });
15975
+ clearTimeout(timeout);
15976
+ if (forceKill) clearTimeout(forceKill);
15977
+ await outputComplete;
15978
+ if (outcome.type === "error") {
15979
+ throw new CommandExecutionError(
15980
+ spec.errorCode,
15981
+ `Could not start ${spec.command}`,
15982
+ {
15983
+ exitCode: null,
15984
+ timedOut,
15985
+ cause: outcome.error
15986
+ }
15987
+ );
15988
+ }
15989
+ if (timedOut) {
15990
+ throw new CommandExecutionError(
15991
+ spec.errorCode,
15992
+ `${spec.command} exceeded its ${spec.timeoutMs}ms time limit`,
15993
+ { exitCode: outcome.exitCode, timedOut: true }
15994
+ );
15995
+ }
15996
+ if (outcome.exitCode !== 0) {
15997
+ throw new CommandExecutionError(
15998
+ spec.errorCode,
15999
+ `${spec.command} exited with code ${String(outcome.exitCode)}${outcome.signal ? ` (${outcome.signal})` : ""}`,
16000
+ { exitCode: outcome.exitCode, timedOut: false }
16001
+ );
16002
+ }
16003
+ return captured;
16004
+ }
16005
+
16006
+ // ../../packages/build/src/repository.ts
16007
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "path";
16008
+ function resolveAppRoot(checkout, configuredRoot) {
16009
+ const checkoutRoot = resolve3(checkout);
16010
+ if (!configuredRoot) return checkoutRoot;
16011
+ if (isAbsolute2(configuredRoot)) {
16012
+ throw new Error("Configured root directory escapes repository");
16013
+ }
16014
+ const appRoot = resolve3(checkoutRoot, configuredRoot);
16015
+ const relativeRoot = relative2(checkoutRoot, appRoot);
16016
+ if (relativeRoot === ".." || relativeRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(relativeRoot)) {
16017
+ throw new Error("Configured root directory escapes repository");
16018
+ }
16019
+ return appRoot;
16020
+ }
16021
+
16022
+ // ../../packages/build/src/static-artifact.ts
16023
+ import { readdir as readdir2, readFile as readFile7 } from "fs/promises";
16024
+ import { isAbsolute as isAbsolute3, join as join8, relative as relative3, resolve as resolve4, sep as sep2 } from "path";
16025
+ var SHIM_WORKER_SOURCE = "export default { fetch: (request, env) => env.ASSETS.fetch(request) };\n";
16026
+ var EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules"]);
16027
+ var ALLOWED_DOTFILE = ".well-known";
16028
+ function excluded(name) {
16029
+ if (name === ALLOWED_DOTFILE) return false;
16030
+ return name.startsWith(".") || EXCLUDED_DIRECTORIES.has(name);
16031
+ }
16032
+ async function readStaticArtifact(appRoot, outputDirectory, identity, options, onWarn) {
16033
+ if (isAbsolute3(outputDirectory))
16034
+ throw new DeploymentError(
16035
+ "ARTIFACT_INVALID",
16036
+ "Output directory must be relative to the application root."
16037
+ );
16038
+ const outputRoot = resolve4(appRoot, outputDirectory);
16039
+ const relation = relative3(appRoot, outputRoot);
16040
+ if (relation === ".." || relation.startsWith(`..${sep2}`) || isAbsolute3(relation))
16041
+ throw new DeploymentError(
16042
+ "ARTIFACT_INVALID",
16043
+ "Output directory escapes the application root."
16044
+ );
16045
+ const assets = [];
16046
+ async function visit(directory) {
16047
+ const entries = await readdir2(directory, { withFileTypes: true });
16048
+ for (const entry of entries.sort(
16049
+ (left, right) => left.name.localeCompare(right.name)
16050
+ )) {
16051
+ if (excluded(entry.name)) continue;
16052
+ const path = join8(directory, entry.name);
16053
+ if (entry.isSymbolicLink())
16054
+ throw new DeploymentError(
16055
+ "ARTIFACT_INVALID",
16056
+ "Static output cannot contain symbolic links."
16057
+ );
16058
+ if (entry.isDirectory()) await visit(path);
16059
+ else if (entry.isFile()) {
16060
+ assets.push({
16061
+ name: relative3(outputRoot, path).split(sep2).join("/"),
16062
+ data: await readFile7(path),
16063
+ contentType: contentType(path)
16064
+ });
16065
+ }
16066
+ }
16067
+ }
16068
+ try {
16069
+ await visit(outputRoot);
16070
+ } catch (error51) {
16071
+ if (error51 instanceof DeploymentError) throw error51;
16072
+ throw new DeploymentError(
16073
+ "ARTIFACT_INVALID",
16074
+ `Could not read the build output directory "${outputDirectory}".`,
16075
+ { cause: error51 }
16076
+ );
16077
+ }
16078
+ if (assets.length === 0)
16079
+ throw new DeploymentError(
16080
+ "ARTIFACT_INVALID",
16081
+ `The build output directory "${outputDirectory}" contains no files.`
16082
+ );
16083
+ if (!assets.some((file2) => file2.name === "index.html"))
16084
+ await onWarn?.(
16085
+ `No index.html in "${outputDirectory}" \u2014 the site root will not serve a page.`
16086
+ );
16087
+ return {
16088
+ ...identity,
16089
+ entrypoint: {
16090
+ name: "worker.js",
16091
+ data: new TextEncoder().encode(SHIM_WORKER_SOURCE),
16092
+ contentType: "application/javascript"
16093
+ },
16094
+ modules: [],
16095
+ assets,
16096
+ compatibilityDate: COMPATIBILITY_DATE,
16097
+ compatibilityFlags: [],
16098
+ assetsBinding: "ASSETS",
16099
+ assetsConfig: {
16100
+ notFoundHandling: options.spaFallback ? "single-page-application" : "404-page"
16101
+ }
16102
+ };
16103
+ }
16104
+
16105
+ // ../../packages/build/src/strategy/fingerprint.ts
16106
+ import { createHash as createHash3 } from "crypto";
16107
+ import { readFile as readFile8 } from "fs/promises";
16108
+ import { join as join9, relative as relative4, sep as sep3 } from "path";
16109
+ var APP_ROOT_FILES = [
16110
+ "package.json",
16111
+ "next.config.js",
16112
+ "next.config.mjs",
16113
+ "next.config.cjs",
16114
+ "next.config.ts",
16115
+ "vite.config.ts",
16116
+ "vite.config.mts",
16117
+ "vite.config.js",
16118
+ "vite.config.mjs",
16119
+ "open-next.config.ts",
16120
+ "open-next.config.js",
16121
+ "open-next.config.mjs",
16122
+ "wrangler.jsonc",
16123
+ "wrangler.json",
16124
+ "wrangler.toml",
16125
+ "vinext.config.ts",
16126
+ "vinext.config.js",
16127
+ "vinext.config.mjs",
16128
+ ".vinextrc"
16129
+ ];
16130
+ var SOURCE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mjs", "mts"];
16131
+ var SOURCE_FILES = ["middleware", "proxy", "src/middleware", "src/proxy"];
16132
+ var CHECKOUT_FILES = [
16133
+ "package.json",
16134
+ "pnpm-lock.yaml",
16135
+ "package-lock.json",
16136
+ "yarn.lock",
16137
+ "pnpm-workspace.yaml",
16138
+ ".npmrc"
16139
+ ];
16140
+ function sha256(data) {
16141
+ return createHash3("sha256").update(data).digest("hex");
16142
+ }
16143
+ async function hashFile(path) {
16144
+ try {
16145
+ return sha256(await readFile8(path));
16146
+ } catch {
16147
+ return null;
16148
+ }
16149
+ }
16150
+ async function hashStrategyInputs(checkout, appRoot) {
16151
+ const candidates = /* @__PURE__ */ new Set();
16152
+ for (const name of CHECKOUT_FILES) candidates.add(join9(checkout, name));
16153
+ for (const name of APP_ROOT_FILES) candidates.add(join9(appRoot, name));
16154
+ for (const base2 of SOURCE_FILES) {
16155
+ for (const ext of SOURCE_EXTENSIONS) {
16156
+ candidates.add(join9(appRoot, `${base2}.${ext}`));
16157
+ }
16158
+ }
16159
+ const entries = [];
16160
+ for (const path of candidates) {
16161
+ const digest = await hashFile(path);
16162
+ if (digest === null) continue;
16163
+ entries.push([relative4(checkout, path).split(sep3).join("/"), digest]);
16164
+ }
16165
+ return entries.toSorted(
16166
+ ([left], [right]) => left < right ? -1 : left > right ? 1 : 0
16167
+ );
16168
+ }
16169
+ async function computeStrategyFingerprint(input) {
16170
+ const entries = input.entries ?? await hashStrategyInputs(input.checkout, input.appRoot);
16171
+ const lines = [
16172
+ `next=${input.nextVersion}`,
16173
+ `root=${input.rootDirectory ?? ""}`,
16174
+ `vinext=${input.vinextEnabled ? "on" : "off"}`,
16175
+ ...entries.map(([name, digest]) => `${name}\0${digest}`)
16176
+ ];
16177
+ return {
16178
+ hash: sha256(lines.join("\n")),
16179
+ inputs: entries.map(([name]) => name)
15464
16180
  };
15465
- for (const name of PASSTHROUGH_ENVIRONMENT) {
15466
- const value = process.env[name];
15467
- if (value !== void 0) environment[name] = value;
16181
+ }
16182
+
16183
+ // ../../packages/build/src/strategy/project-facts.ts
16184
+ import { access as access6, readFile as readFile9 } from "fs/promises";
16185
+ import { join as join10 } from "path";
16186
+ var SOURCE_EXTENSIONS2 = ["ts", "tsx", "js", "jsx", "mjs", "mts"];
16187
+ var SOURCE_DIRECTORIES = ["", "src"];
16188
+ var VITE_CONFIGS = [
16189
+ "vite.config.ts",
16190
+ "vite.config.mts",
16191
+ "vite.config.js",
16192
+ "vite.config.mjs"
16193
+ ];
16194
+ async function exists4(path) {
16195
+ try {
16196
+ await access6(path);
16197
+ return true;
16198
+ } catch {
16199
+ return false;
15468
16200
  }
15469
- return { ...environment, ...overrides };
15470
16201
  }
15471
- async function runCommand(spec, observer) {
15472
- const detached = process.platform !== "win32";
15473
- const child = spawn(spec.command, [...spec.args], {
15474
- cwd: spec.cwd,
15475
- shell: false,
15476
- env: commandEnvironment(spec.env),
15477
- detached,
15478
- stdio: ["ignore", "pipe", "pipe"]
16202
+ async function findSourceFile(appRoot, basename4) {
16203
+ for (const dir of SOURCE_DIRECTORIES) {
16204
+ for (const ext of SOURCE_EXTENSIONS2) {
16205
+ const rel = dir ? `${dir}/${basename4}.${ext}` : `${basename4}.${ext}`;
16206
+ if (await exists4(join10(appRoot, rel))) return rel;
16207
+ }
16208
+ }
16209
+ return null;
16210
+ }
16211
+ function matcherRoutesFromSource(source) {
16212
+ const matcher = /matcher\s*:\s*(\[[\s\S]*?\]|["'`][^"'`]*["'`])/.exec(source);
16213
+ if (!matcher?.[1]) return [];
16214
+ const literals = [...matcher[1].matchAll(/["'`]([^"'`]*)["'`]/g)].map((entry) => entry[1] ?? "").map((entry) => entry.trim());
16215
+ const routes = /* @__PURE__ */ new Set();
16216
+ for (const literal2 of literals) {
16217
+ if (!literal2.startsWith("/")) continue;
16218
+ if (/[:*+?()[\]{}\\|]/.test(literal2)) continue;
16219
+ routes.add(literal2);
16220
+ if (routes.size >= 5) break;
16221
+ }
16222
+ return [...routes];
16223
+ }
16224
+ async function readIfExists(path) {
16225
+ try {
16226
+ return await readFile9(path, "utf8");
16227
+ } catch {
16228
+ return null;
16229
+ }
16230
+ }
16231
+ async function collectProjectFacts(appRoot) {
16232
+ const proxyFile = await findSourceFile(appRoot, "proxy");
16233
+ const middlewareFile = await findSourceFile(appRoot, "middleware");
16234
+ const routeSourceFile = middlewareFile ?? proxyFile;
16235
+ const routeSource = routeSourceFile ? await readIfExists(join10(appRoot, routeSourceFile)) : null;
16236
+ let usesVinext = false;
16237
+ let nextSpec = null;
16238
+ const packageJsonSource = await readIfExists(join10(appRoot, "package.json"));
16239
+ if (packageJsonSource) {
16240
+ try {
16241
+ const packageJson = JSON.parse(packageJsonSource);
16242
+ const dependency = (name) => {
16243
+ const value = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
16244
+ return typeof value === "string" ? value : null;
16245
+ };
16246
+ nextSpec = dependency("next");
16247
+ if (dependency("vinext") !== null) {
16248
+ for (const name of VITE_CONFIGS) {
16249
+ const config2 = await readIfExists(join10(appRoot, name));
16250
+ if (config2 && /\bvinext\s*\(/.test(config2) && /@cloudflare\/vite-plugin/.test(config2)) {
16251
+ usesVinext = true;
16252
+ break;
16253
+ }
16254
+ }
16255
+ }
16256
+ } catch {
16257
+ }
16258
+ }
16259
+ return {
16260
+ proxyFile,
16261
+ middlewareFile,
16262
+ matcherRoutes: routeSource ? matcherRoutesFromSource(routeSource) : [],
16263
+ usesVinext,
16264
+ nextSpec
16265
+ };
16266
+ }
16267
+
16268
+ // ../../packages/build/src/strategy/resolver.ts
16269
+ function label(strategy) {
16270
+ return `P${String(strategy.priority)} ${strategy.id}`;
16271
+ }
16272
+ function describeOutcome(outcome) {
16273
+ switch (outcome.status) {
16274
+ case "success":
16275
+ return "succeeded";
16276
+ case "failed":
16277
+ return `failed at ${outcome.stage}: ${outcome.reason}`;
16278
+ case "skipped":
16279
+ return `skipped: ${outcome.reason}`;
16280
+ }
16281
+ }
16282
+ async function evaluateOne(strategy, context, observer) {
16283
+ let outcome;
16284
+ try {
16285
+ outcome = await strategy.evaluate(context, observer);
16286
+ } catch (error51) {
16287
+ outcome = {
16288
+ status: "failed",
16289
+ stage: "build",
16290
+ reason: error51 instanceof Error ? error51.message : String(error51)
16291
+ };
16292
+ }
16293
+ await observer.onLog({
16294
+ level: outcome.status === "success" ? "INFO" : "WARN",
16295
+ stage: "BUILDING",
16296
+ message: `${label(strategy)} ${describeOutcome(outcome)}.`
15479
16297
  });
15480
- let timedOut = false;
15481
- const captured = { stdout: [], stderr: [] };
15482
- const consume = async (input, level) => {
15483
- const lines = createInterface({ input });
15484
- for await (const message of lines) {
15485
- const output = captured[level === "INFO" ? "stdout" : "stderr"];
15486
- if (output.length < MAX_CAPTURED_LINES) output.push(message);
16298
+ return { id: strategy.id, priority: strategy.priority, outcome };
16299
+ }
16300
+ async function runPool(tasks, limit) {
16301
+ const results = Array.from({ length: tasks.length });
16302
+ let next = 0;
16303
+ const workers = Array.from(
16304
+ { length: Math.max(1, Math.min(limit, tasks.length)) },
16305
+ async () => {
16306
+ for (; ; ) {
16307
+ const index = next++;
16308
+ const task = tasks[index];
16309
+ if (!task) return;
16310
+ results[index] = await task();
16311
+ }
16312
+ }
16313
+ );
16314
+ await Promise.all(workers);
16315
+ return results;
16316
+ }
16317
+ function pickBest(candidates, attempts) {
16318
+ const successes = attempts.filter(
16319
+ (attempt) => attempt.outcome.status === "success"
16320
+ ).toSorted((left, right) => left.priority - right.priority);
16321
+ const best = successes[0];
16322
+ if (!best) return null;
16323
+ const strategy = candidates.find((candidate) => candidate.id === best.id);
16324
+ return strategy ? { strategy, outcome: best.outcome } : null;
16325
+ }
16326
+ function record2(strategy, outcome, context, options) {
16327
+ return {
16328
+ strategy: strategy.id,
16329
+ priority: strategy.priority,
16330
+ nextMajor: context.nextMajor,
16331
+ ...outcome.variant ? { variant: outcome.variant } : {},
16332
+ fingerprint: options.fingerprint,
16333
+ resolvedAt: (options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
16334
+ deploymentId: context.identity.deploymentId
16335
+ };
16336
+ }
16337
+ async function resolveDeploymentStrategy(strategies, context, observer, options) {
16338
+ const ordered = strategies.toSorted(
16339
+ (left, right) => left.priority - right.priority
16340
+ );
16341
+ const candidates = [];
16342
+ for (const strategy of ordered) {
16343
+ const support = strategy.supports(context);
16344
+ if (support.supported) {
16345
+ candidates.push(strategy);
16346
+ } else {
15487
16347
  await observer.onLog({
15488
- level,
15489
- stage: spec.stage,
15490
- message: redactLog(message)
16348
+ level: "INFO",
16349
+ stage: "BUILDING",
16350
+ message: `${label(strategy)} not a candidate: ${support.reason}`
15491
16351
  });
15492
16352
  }
15493
- };
15494
- const outputComplete = Promise.all([
15495
- consume(child.stdout, "INFO"),
15496
- consume(child.stderr, "ERROR")
15497
- ]);
15498
- const kill = (signal) => {
15499
- if (detached && child.pid) {
15500
- try {
15501
- process.kill(-child.pid, signal);
15502
- return;
15503
- } catch {
16353
+ }
16354
+ if (candidates.length === 0) {
16355
+ throw new DeploymentError(
16356
+ "NO_DEPLOYMENT_STRATEGY",
16357
+ `No deployment strategy can build Next.js ${context.nextVersion} on this platform.`
16358
+ );
16359
+ }
16360
+ const attempts = [];
16361
+ const pinnedStrategy = options.pinned ? candidates.find((candidate) => candidate.id === options.pinned?.strategy) : void 0;
16362
+ if (options.pinned && pinnedStrategy) {
16363
+ const pinned = options.pinned;
16364
+ const stale = pinned.fingerprint !== options.fingerprint ? "its inputs changed" : pinned.nextMajor !== context.nextMajor ? `Next.js moved from ${String(pinned.nextMajor)} to ${String(context.nextMajor)}` : null;
16365
+ if (stale) {
16366
+ await observer.onLog({
16367
+ level: "INFO",
16368
+ stage: "BUILDING",
16369
+ message: `Stored strategy ${label(pinnedStrategy)} is stale (${stale}); resolving again.`
16370
+ });
16371
+ } else {
16372
+ await observer.onLog({
16373
+ level: "INFO",
16374
+ stage: "BUILDING",
16375
+ message: `Reusing stored strategy ${label(pinnedStrategy)}${pinned.variant ? ` (${pinned.variant})` : ""}.`
16376
+ });
16377
+ const attempt = await evaluateOne(
16378
+ pinnedStrategy,
16379
+ pinned.variant ? { ...context, pinnedVariant: pinned.variant } : context,
16380
+ observer
16381
+ );
16382
+ attempts.push(attempt);
16383
+ if (attempt.outcome.status === "success") {
16384
+ const outcome = attempt.outcome;
16385
+ const unchanged = (outcome.variant ?? null) === (pinned.variant ?? null);
16386
+ return {
16387
+ strategy: pinnedStrategy,
16388
+ outcome,
16389
+ record: unchanged ? pinned : record2(pinnedStrategy, outcome, context, options),
16390
+ pinned: true,
16391
+ attempts
16392
+ };
15504
16393
  }
16394
+ await observer.onLog({
16395
+ level: "WARN",
16396
+ stage: "BUILDING",
16397
+ message: `Stored strategy ${label(pinnedStrategy)} no longer works; resolving again with the remaining candidates.`
16398
+ });
15505
16399
  }
15506
- child.kill(signal);
15507
- };
15508
- let forceKill;
15509
- const timeout = setTimeout(() => {
15510
- timedOut = true;
15511
- kill("SIGTERM");
15512
- forceKill = setTimeout(() => kill("SIGKILL"), 2e3);
15513
- forceKill.unref();
15514
- }, spec.timeoutMs);
15515
- const outcome = await new Promise((resolveOutcome) => {
15516
- child.once("error", (error51) => resolveOutcome({ type: "error", error: error51 }));
15517
- child.once(
15518
- "close",
15519
- (exitCode, signal) => resolveOutcome({ type: "close", exitCode, signal })
16400
+ } else if (options.pinned) {
16401
+ await observer.onLog({
16402
+ level: "INFO",
16403
+ stage: "BUILDING",
16404
+ message: `Stored strategy ${options.pinned.strategy} is not a candidate for this checkout; resolving again.`
16405
+ });
16406
+ }
16407
+ const remaining = candidates.filter(
16408
+ (candidate) => !attempts.some((attempt) => attempt.id === candidate.id)
16409
+ );
16410
+ await observer.onLog({
16411
+ level: "INFO",
16412
+ stage: "BUILDING",
16413
+ message: `Resolving deployment strategy for Next.js ${context.nextVersion}: ${remaining.map(label).join(
16414
+ ", "
16415
+ )}${options.concurrency > 1 && remaining.length > 1 ? " (in parallel)" : ""}.`
16416
+ });
16417
+ if (options.concurrency > 1) {
16418
+ const results = await runPool(
16419
+ remaining.map(
16420
+ (strategy) => () => evaluateOne(strategy, context, observer)
16421
+ ),
16422
+ options.concurrency
16423
+ );
16424
+ attempts.push(...results);
16425
+ } else {
16426
+ for (const strategy of remaining) {
16427
+ const attempt = await evaluateOne(strategy, context, observer);
16428
+ attempts.push(attempt);
16429
+ if (attempt.outcome.status === "success") break;
16430
+ }
16431
+ }
16432
+ const best = pickBest(candidates, attempts);
16433
+ if (!best) {
16434
+ throw new DeploymentError(
16435
+ "NO_DEPLOYMENT_STRATEGY",
16436
+ `No deployment strategy succeeded for Next.js ${context.nextVersion}: ${attempts.map(
16437
+ (attempt) => `P${String(attempt.priority)} ${attempt.id} ${describeOutcome(attempt.outcome)}`
16438
+ ).join("; ")}.`
15520
16439
  );
16440
+ }
16441
+ await observer.onLog({
16442
+ level: "INFO",
16443
+ stage: "BUILDING",
16444
+ message: `Selected ${label(best.strategy)}${best.outcome.variant ? ` (${best.outcome.variant})` : ""}.`
15521
16445
  });
15522
- clearTimeout(timeout);
15523
- if (forceKill) clearTimeout(forceKill);
15524
- await outputComplete;
15525
- if (outcome.type === "error") {
15526
- throw new CommandExecutionError(
15527
- spec.errorCode,
15528
- `Could not start ${spec.command}`,
16446
+ return {
16447
+ strategy: best.strategy,
16448
+ outcome: best.outcome,
16449
+ record: record2(best.strategy, best.outcome, context, options),
16450
+ pinned: false,
16451
+ attempts
16452
+ };
16453
+ }
16454
+
16455
+ // ../../packages/build/src/strategy/smoke.ts
16456
+ import { createServer } from "net";
16457
+ function allocateLoopbackPort() {
16458
+ return new Promise((resolvePort, rejectPort) => {
16459
+ const server = createServer();
16460
+ server.unref();
16461
+ server.once("error", rejectPort);
16462
+ server.listen(0, "127.0.0.1", () => {
16463
+ const address = server.address();
16464
+ const port = typeof address === "object" && address !== null ? address.port : null;
16465
+ server.close(() => {
16466
+ if (port === null) rejectPort(new Error("Could not allocate a port."));
16467
+ else resolvePort(port);
16468
+ });
16469
+ });
16470
+ });
16471
+ }
16472
+ function sleep(ms) {
16473
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
16474
+ }
16475
+ async function fetchStatus(fetchImpl, url2, timeoutMs) {
16476
+ const controller = new AbortController();
16477
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
16478
+ try {
16479
+ const response = await fetchImpl(url2, {
16480
+ method: "GET",
16481
+ redirect: "manual",
16482
+ signal: controller.signal,
16483
+ headers: { "user-agent": "naro-smoke/1" }
16484
+ });
16485
+ await response.arrayBuffer().catch(() => void 0);
16486
+ return { status: response.status };
16487
+ } catch (error51) {
16488
+ return { error: error51 instanceof Error ? error51.message : String(error51) };
16489
+ } finally {
16490
+ clearTimeout(timer);
16491
+ }
16492
+ }
16493
+ function probeOk(status) {
16494
+ return status < 500;
16495
+ }
16496
+ function createSmokeTester(options = {}) {
16497
+ const start = options.startProcess ?? startProcess;
16498
+ const fetchImpl = options.fetch ?? ((input, init) => fetch(input, init));
16499
+ const allocatePort = options.allocatePort ?? allocateLoopbackPort;
16500
+ const pollIntervalMs = options.pollIntervalMs ?? 1e3;
16501
+ async function runOnce(request, observer) {
16502
+ const stage = request.stage ?? "BUILDING";
16503
+ const errorCode = request.errorCode ?? "BUILD_FAILED";
16504
+ const bootTimeoutMs = request.bootTimeoutMs ?? 9e4;
16505
+ const probeTimeoutMs = request.probeTimeoutMs ?? 15e3;
16506
+ const port = await allocatePort();
16507
+ const origin = `http://127.0.0.1:${String(port)}`;
16508
+ const boot = request.boot(port);
16509
+ const recent = [];
16510
+ const server = start(
16511
+ { ...boot, stage, errorCode },
16512
+ // The dev server is chatty; keep its stderr from reading as build
16513
+ // errors in the deployment log.
15529
16514
  {
15530
- exitCode: null,
15531
- timedOut,
15532
- cause: outcome.error
16515
+ ...observer,
16516
+ onLog: (log2) => {
16517
+ if (log2.message.trim() !== "") {
16518
+ recent.push(log2.message.trim());
16519
+ if (recent.length > 6) recent.shift();
16520
+ }
16521
+ return observer.onLog({
16522
+ ...log2,
16523
+ level: log2.level === "ERROR" ? "WARN" : log2.level
16524
+ });
16525
+ }
15533
16526
  }
15534
16527
  );
16528
+ const probes = [];
16529
+ const withOutput = (reason) => recent.length > 0 ? `${reason} Last output: ${recent.join(" | ")}` : reason;
16530
+ try {
16531
+ const deadline = Date.now() + bootTimeoutMs;
16532
+ let booted = false;
16533
+ let lastError = "no response";
16534
+ while (Date.now() < deadline) {
16535
+ const exit = await Promise.race([
16536
+ server.exited.then((code) => ({ exited: code })),
16537
+ sleep(0).then(() => null)
16538
+ ]);
16539
+ if (exit) {
16540
+ return {
16541
+ ok: false,
16542
+ reason: withOutput(
16543
+ `The server exited with code ${String(exit.exited)} before answering a request.`
16544
+ ),
16545
+ probes,
16546
+ // The port was free when allocated and taken by the time the
16547
+ // server bound it — a race, not a broken build.
16548
+ portTaken: recent.some(
16549
+ (line) => /EADDRINUSE|address already in use/i.test(line)
16550
+ )
16551
+ };
16552
+ }
16553
+ const answer = await fetchStatus(
16554
+ fetchImpl,
16555
+ `${origin}/`,
16556
+ probeTimeoutMs
16557
+ );
16558
+ if ("status" in answer) {
16559
+ booted = true;
16560
+ break;
16561
+ }
16562
+ lastError = answer.error;
16563
+ await sleep(pollIntervalMs);
16564
+ }
16565
+ if (!booted) {
16566
+ return {
16567
+ ok: false,
16568
+ reason: withOutput(
16569
+ `The server did not answer within ${String(bootTimeoutMs)}ms (${lastError}).`
16570
+ ),
16571
+ probes
16572
+ };
16573
+ }
16574
+ for (const probe of request.probes) {
16575
+ const answer = await fetchStatus(
16576
+ fetchImpl,
16577
+ `${origin}${probe.path}`,
16578
+ probeTimeoutMs
16579
+ );
16580
+ const result = "status" in answer ? { ...probe, status: answer.status, ok: probeOk(answer.status) } : { ...probe, status: null, ok: false, detail: answer.error };
16581
+ probes.push(result);
16582
+ await observer.onLog({
16583
+ level: result.ok ? "INFO" : "ERROR",
16584
+ stage,
16585
+ message: `Smoke ${probe.label}: GET ${probe.path} -> ${result.status === null ? result.detail ?? "no response" : String(result.status)}`
16586
+ });
16587
+ }
16588
+ const failed2 = probes.filter((probe) => !probe.ok);
16589
+ if (failed2.length > 0) {
16590
+ return {
16591
+ ok: false,
16592
+ reason: `${String(failed2.length)} of ${String(probes.length)} smoke probes failed: ${failed2.map(
16593
+ (probe) => `${probe.path} (${probe.status === null ? probe.detail ?? "no response" : String(probe.status)})`
16594
+ ).join(", ")}.`,
16595
+ probes
16596
+ };
16597
+ }
16598
+ return { ok: true, probes };
16599
+ } finally {
16600
+ await server.stop();
16601
+ }
16602
+ }
16603
+ return {
16604
+ async run(request, observer) {
16605
+ const first = await runOnce(request, observer);
16606
+ if (first.ok || !first.portTaken) return first;
16607
+ await observer.onLog({
16608
+ level: "WARN",
16609
+ stage: request.stage ?? "BUILDING",
16610
+ message: "Smoke port was taken before the server bound it; retrying on a fresh port."
16611
+ });
16612
+ return runOnce(request, observer);
16613
+ }
16614
+ };
16615
+ }
16616
+ function defaultSmokeProbes(matcherRoutes) {
16617
+ const probes = [{ path: "/", label: "root" }];
16618
+ for (const route of matcherRoutes) {
16619
+ if (route === "/") continue;
16620
+ probes.push({ path: route, label: "middleware matcher" });
16621
+ }
16622
+ return probes;
16623
+ }
16624
+
16625
+ // ../../packages/build/src/strategy/strategies/opennext-aws.ts
16626
+ import { writeFile as writeFile6 } from "fs/promises";
16627
+ import { join as join12 } from "path";
16628
+
16629
+ // ../../packages/build/src/strategy/strategies/shared.ts
16630
+ import { access as access7, readFile as readFile10, rm, writeFile as writeFile5 } from "fs/promises";
16631
+ import { dirname as dirname2, join as join11, resolve as resolve5 } from "path";
16632
+ function capturingObserver(observer) {
16633
+ const lines = [];
16634
+ return {
16635
+ lines,
16636
+ observer: {
16637
+ ...observer,
16638
+ onLog: (log2) => {
16639
+ lines.push(log2.message);
16640
+ return observer.onLog(log2);
16641
+ }
16642
+ }
16643
+ };
16644
+ }
16645
+ async function runStep(context, spec, observer) {
16646
+ const capture = capturingObserver(observer);
16647
+ try {
16648
+ return {
16649
+ ok: true,
16650
+ result: await context.ports.runCommand(spec, capture.observer)
16651
+ };
16652
+ } catch (error51) {
16653
+ return { ok: false, error: error51, output: capture.lines };
16654
+ }
16655
+ }
16656
+ function describeError(error51) {
16657
+ return error51 instanceof Error ? error51.message : String(error51);
16658
+ }
16659
+ function describeFailure(step) {
16660
+ const tail = step.output.map((line) => line.trim()).filter((line) => line !== "").slice(-4);
16661
+ return tail.length > 0 ? `${describeError(step.error)} \u2014 ${tail.join(" | ")}` : describeError(step.error);
16662
+ }
16663
+ function failed(stage, reason) {
16664
+ return { status: "failed", stage, reason };
16665
+ }
16666
+ async function info(observer, message) {
16667
+ await observer.onLog({ level: "INFO", stage: "BUILDING", message });
16668
+ }
16669
+ async function warn(observer, message) {
16670
+ await observer.onLog({ level: "WARN", stage: "BUILDING", message });
16671
+ }
16672
+ async function pathExists2(path) {
16673
+ try {
16674
+ await access7(path);
16675
+ return true;
16676
+ } catch {
16677
+ return false;
16678
+ }
16679
+ }
16680
+ async function findProjectBin(appRoot, checkout, name) {
16681
+ let dir = resolve5(appRoot);
16682
+ const stop = resolve5(checkout);
16683
+ for (; ; ) {
16684
+ const candidate = join11(dir, "node_modules", ".bin", name);
16685
+ if (await pathExists2(candidate)) return candidate;
16686
+ if (dir === stop) return null;
16687
+ const parent = dirname2(dir);
16688
+ if (parent === dir) return null;
16689
+ dir = parent;
16690
+ }
16691
+ }
16692
+ function dotenvQuote(value) {
16693
+ if (!value.includes("'")) return `'${value}'`;
16694
+ if (!value.includes("`")) return `\`${value}\``;
16695
+ return `"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`;
16696
+ }
16697
+ async function withDevVars(wranglerConfigPath, env, run) {
16698
+ const path = join11(dirname2(wranglerConfigPath), ".dev.vars");
16699
+ const keys = Object.keys(env).filter(
16700
+ (key) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)
16701
+ );
16702
+ if (keys.length === 0) return run();
16703
+ await writeFile5(
16704
+ path,
16705
+ `${keys.map((key) => `${key}=${dotenvQuote(env[key] ?? "")}`).join("\n")}
16706
+ `,
16707
+ { mode: 384 }
16708
+ );
16709
+ try {
16710
+ return await run();
16711
+ } finally {
16712
+ await rm(path, { force: true });
16713
+ }
16714
+ }
16715
+ async function bundleWorker(context, layout, observer, errorCode) {
16716
+ return runStep(
16717
+ context,
16718
+ {
16719
+ command: context.toolchain.wrangler,
16720
+ args: [
16721
+ "deploy",
16722
+ "--dry-run",
16723
+ "--config",
16724
+ layout.wranglerConfigPath,
16725
+ "--outdir",
16726
+ layout.bundleDir
16727
+ ],
16728
+ cwd: layout.appRoot,
16729
+ env: {
16730
+ CI: "1",
16731
+ WRANGLER_LOG_PATH: join11(
16732
+ layout.appRoot,
16733
+ layout.outputRoot,
16734
+ "wrangler.log"
16735
+ )
16736
+ },
16737
+ timeoutMs: 5 * 6e4,
16738
+ stage: "BUILDING",
16739
+ errorCode
16740
+ },
16741
+ observer
16742
+ );
16743
+ }
16744
+ async function smokeWorker(context, layout, observer) {
16745
+ return withDevVars(
16746
+ layout.wranglerConfigPath,
16747
+ context.env,
16748
+ () => context.ports.smoke.run(
16749
+ {
16750
+ boot: (port) => ({
16751
+ command: context.toolchain.wrangler,
16752
+ args: [
16753
+ "dev",
16754
+ "--config",
16755
+ layout.wranglerConfigPath,
16756
+ "--local",
16757
+ "--ip",
16758
+ "127.0.0.1",
16759
+ "--port",
16760
+ String(port),
16761
+ "--show-interactive-dev-session=false",
16762
+ "--log-level=warn",
16763
+ "--persist-to",
16764
+ join11(layout.appRoot, layout.outputRoot, ".naro-smoke-state")
16765
+ ],
16766
+ cwd: layout.appRoot,
16767
+ env: {
16768
+ CI: "1",
16769
+ WRANGLER_LOG_PATH: join11(
16770
+ layout.appRoot,
16771
+ layout.outputRoot,
16772
+ "wrangler-dev.log"
16773
+ )
16774
+ }
16775
+ }),
16776
+ probes: defaultSmokeProbes(context.facts.matcherRoutes),
16777
+ stage: "BUILDING",
16778
+ errorCode: "BUILD_FAILED"
16779
+ },
16780
+ observer
16781
+ )
16782
+ );
16783
+ }
16784
+ async function approvePnpmBuilds(checkout, packages) {
16785
+ const path = join11(checkout, "pnpm-workspace.yaml");
16786
+ let source = "";
16787
+ try {
16788
+ source = await readFile10(path, "utf8");
16789
+ } catch {
16790
+ }
16791
+ let next = source;
16792
+ const mapEntries = packages.map((name) => ` ${name}: true`).join("\n");
16793
+ const flowMap = /^(allowBuilds\s*:\s*\{)([^}]*)\}/m;
16794
+ if (flowMap.test(next)) {
16795
+ next = next.replace(flowMap, (_match, open, body) => {
16796
+ const items = body.trim();
16797
+ const added = packages.map((name) => `${name}: true`).join(", ");
16798
+ return `${open} ${items ? `${items.replace(/,\s*$/, "")}, ` : ""}${added} }`;
16799
+ });
16800
+ } else if (/^allowBuilds\s*:/m.test(next)) {
16801
+ next = next.replace(/^(allowBuilds\s*:[^\n]*\n)/m, `$1${mapEntries}
16802
+ `);
16803
+ } else {
16804
+ next = `${next}${next.endsWith("\n") || next === "" ? "" : "\n"}allowBuilds:
16805
+ ${mapEntries}
16806
+ `;
15535
16807
  }
15536
- if (timedOut) {
15537
- throw new CommandExecutionError(
15538
- spec.errorCode,
15539
- `${spec.command} exceeded its ${spec.timeoutMs}ms time limit`,
15540
- { exitCode: outcome.exitCode, timedOut: true }
16808
+ const listEntries = packages.map((name) => ` - ${name}`).join("\n");
16809
+ const flowList = /^(onlyBuiltDependencies\s*:\s*\[)([^\]]*)\]/m;
16810
+ if (flowList.test(next)) {
16811
+ next = next.replace(flowList, (_match, open, body) => {
16812
+ const items = body.trim();
16813
+ return `${open}${items ? `${items.replace(/,\s*$/, "")}, ` : ""}${packages.join(", ")}]`;
16814
+ });
16815
+ } else if (/^onlyBuiltDependencies\s*:/m.test(next)) {
16816
+ next = next.replace(
16817
+ /^(onlyBuiltDependencies\s*:[^\n]*\n)/m,
16818
+ `$1${listEntries}
16819
+ `
15541
16820
  );
16821
+ } else {
16822
+ next = `${next}onlyBuiltDependencies:
16823
+ ${listEntries}
16824
+ `;
15542
16825
  }
15543
- if (outcome.exitCode !== 0) {
15544
- throw new CommandExecutionError(
15545
- spec.errorCode,
15546
- `${spec.command} exited with code ${String(outcome.exitCode)}${outcome.signal ? ` (${outcome.signal})` : ""}`,
15547
- { exitCode: outcome.exitCode, timedOut: false }
15548
- );
16826
+ if (next === source) return true;
16827
+ try {
16828
+ await writeFile5(path, next, "utf8");
16829
+ return true;
16830
+ } catch {
16831
+ return false;
15549
16832
  }
15550
- return captured;
15551
16833
  }
15552
16834
 
15553
- // ../../packages/build/src/repository.ts
15554
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "path";
15555
- function resolveAppRoot(checkout, configuredRoot) {
15556
- const checkoutRoot = resolve2(checkout);
15557
- if (!configuredRoot) return checkoutRoot;
15558
- if (isAbsolute2(configuredRoot)) {
15559
- throw new Error("Configured root directory escapes repository");
15560
- }
15561
- const appRoot = resolve2(checkoutRoot, configuredRoot);
15562
- const relativeRoot = relative2(checkoutRoot, appRoot);
15563
- if (relativeRoot === ".." || relativeRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(relativeRoot)) {
15564
- throw new Error("Configured root directory escapes repository");
15565
- }
15566
- return appRoot;
16835
+ // ../../packages/build/src/strategy/strategies/opennext-aws.ts
16836
+ var CONFIG_NAME = ".next-deploy-open-next-aws.config.ts";
16837
+ var SERVER_FUNCTION = ".open-next/server-functions/default/index.mjs";
16838
+ function smokeConfig(buildCommand) {
16839
+ return [
16840
+ "const config = {",
16841
+ ` buildCommand: ${JSON.stringify(buildCommand)},`,
16842
+ " default: {",
16843
+ ' override: { wrapper: "node", converter: "node", incrementalCache: "dummy", tagCache: "dummy", queue: "dummy" },',
16844
+ " },",
16845
+ ' imageOptimization: { loader: "dummy" },',
16846
+ " dangerous: { disableIncrementalCache: true, disableTagCache: true },",
16847
+ "};",
16848
+ "export default config;",
16849
+ ""
16850
+ ].join("\n");
16851
+ }
16852
+ function createOpenNextAwsStrategy() {
16853
+ return {
16854
+ id: "opennext-aws",
16855
+ priority: 5,
16856
+ target: "aws",
16857
+ supports(context) {
16858
+ if (!context.targets.aws)
16859
+ return {
16860
+ supported: false,
16861
+ reason: "No AWS deploy target is configured on this control plane (no provider, credentials, or routing exist for it)."
16862
+ };
16863
+ if (!context.toolchain.openNextAws)
16864
+ return {
16865
+ supported: false,
16866
+ reason: "The @opennextjs/aws toolchain is not installed on this build runner."
16867
+ };
16868
+ return { supported: true };
16869
+ },
16870
+ async evaluate(context, observer) {
16871
+ await info(
16872
+ observer,
16873
+ "P5 opennext-aws: building with the OpenNext AWS adapter."
16874
+ );
16875
+ let fork;
16876
+ try {
16877
+ fork = await context.workspace.fork("opennext-aws");
16878
+ } catch (error51) {
16879
+ return failed("prepare", describeError(error51));
16880
+ }
16881
+ const configPath = join12(fork.appRoot, CONFIG_NAME);
16882
+ try {
16883
+ await writeFile6(
16884
+ configPath,
16885
+ smokeConfig(
16886
+ `${context.packageManager.name} run ${context.buildScript}`
16887
+ ),
16888
+ "utf8"
16889
+ );
16890
+ } catch (error51) {
16891
+ return failed("prepare", describeError(error51));
16892
+ }
16893
+ const build = await runStep(
16894
+ context,
16895
+ {
16896
+ command: context.toolchain.openNextAws ?? "open-next",
16897
+ args: ["build", "--config-path", configPath],
16898
+ cwd: fork.appRoot,
16899
+ env: {
16900
+ ...context.env,
16901
+ NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
16902
+ },
16903
+ timeoutMs: 20 * 6e4,
16904
+ stage: "BUILDING",
16905
+ errorCode: "BUILD_FAILED"
16906
+ },
16907
+ observer
16908
+ );
16909
+ if (!build.ok) return failed("build", describeFailure(build));
16910
+ const serverFunction = join12(fork.appRoot, SERVER_FUNCTION);
16911
+ if (!await pathExists2(serverFunction))
16912
+ return failed("bundle", `OpenNext AWS produced no ${SERVER_FUNCTION}.`);
16913
+ const smoke = await context.ports.smoke.run(
16914
+ {
16915
+ boot: (port) => ({
16916
+ command: process.execPath,
16917
+ args: [serverFunction],
16918
+ cwd: fork.appRoot,
16919
+ env: { ...context.env, PORT: String(port), NODE_ENV: "production" }
16920
+ }),
16921
+ probes: defaultSmokeProbes(context.facts.matcherRoutes),
16922
+ stage: "BUILDING",
16923
+ errorCode: "BUILD_FAILED"
16924
+ },
16925
+ observer
16926
+ );
16927
+ if (!smoke.ok) return failed("smoke", smoke.reason);
16928
+ return {
16929
+ status: "success",
16930
+ artifact: {
16931
+ kind: "opennext-aws",
16932
+ outputRoot: join12(fork.appRoot, ".open-next"),
16933
+ serverFunction
16934
+ },
16935
+ notes: [
16936
+ "Verified with the Node wrapper; an AWS deploy needs a Lambda-wrapper build against provisioned resources."
16937
+ ]
16938
+ };
16939
+ }
16940
+ };
15567
16941
  }
15568
16942
 
15569
- // ../../packages/build/src/static-artifact.ts
15570
- import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
15571
- import { isAbsolute as isAbsolute3, join as join7, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
15572
- var SHIM_WORKER_SOURCE = "export default { fetch: (request, env) => env.ASSETS.fetch(request) };\n";
15573
- var EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules"]);
15574
- var ALLOWED_DOTFILE = ".well-known";
15575
- function excluded(name) {
15576
- if (name === ALLOWED_DOTFILE) return false;
15577
- return name.startsWith(".") || EXCLUDED_DIRECTORIES.has(name);
16943
+ // ../../packages/build/src/strategy/strategies/opennext-cloudflare.ts
16944
+ import { readFile as readFile11 } from "fs/promises";
16945
+ import { join as join13 } from "path";
16946
+ var PROXY_TO_MIDDLEWARE_VARIANT = "proxy-to-middleware";
16947
+ var NODE_MIDDLEWARE_REFUSAL = /Node\.js middleware is not (?:currently )?supported/i;
16948
+ async function buildDeclaresNodeMiddleware(appRoot) {
16949
+ try {
16950
+ const manifest2 = JSON.parse(
16951
+ await readFile11(
16952
+ join13(appRoot, ".next", "server", "functions-config-manifest.json"),
16953
+ "utf8"
16954
+ )
16955
+ );
16956
+ return Boolean(manifest2.functions?.["/_middleware"]);
16957
+ } catch {
16958
+ return false;
16959
+ }
15578
16960
  }
15579
- async function readStaticArtifact(appRoot, outputDirectory, identity, options, onWarn) {
15580
- if (isAbsolute3(outputDirectory))
15581
- throw new DeploymentError(
15582
- "ARTIFACT_INVALID",
15583
- "Output directory must be relative to the application root."
16961
+ function createOpenNextCloudflareStrategy(options) {
16962
+ async function attempt(context, roots, observer) {
16963
+ let platform;
16964
+ try {
16965
+ platform = await options.createPlatformBuildConfig(
16966
+ roots.appRoot,
16967
+ context.identity.deploymentId
16968
+ );
16969
+ } catch (error51) {
16970
+ return {
16971
+ ok: false,
16972
+ nodeMiddleware: false,
16973
+ outcome: failed("prepare", describeError(error51))
16974
+ };
16975
+ }
16976
+ const nextBuild = await runStep(
16977
+ context,
16978
+ {
16979
+ command: context.packageManager.name,
16980
+ args: ["run", context.buildScript],
16981
+ cwd: roots.appRoot,
16982
+ env: {
16983
+ ...context.env,
16984
+ NEXT_PRIVATE_STANDALONE: "true",
16985
+ NPM_CONFIG_USERCONFIG: platform.packageManagerConfigPath
16986
+ },
16987
+ timeoutMs: 15 * 6e4,
16988
+ stage: "BUILDING",
16989
+ errorCode: "BUILD_FAILED"
16990
+ },
16991
+ observer
15584
16992
  );
15585
- const outputRoot = resolve3(appRoot, outputDirectory);
15586
- const relation = relative3(appRoot, outputRoot);
15587
- if (relation === ".." || relation.startsWith(`..${sep2}`) || isAbsolute3(relation))
15588
- throw new DeploymentError(
15589
- "ARTIFACT_INVALID",
15590
- "Output directory escapes the application root."
16993
+ if (!nextBuild.ok) {
16994
+ return {
16995
+ ok: false,
16996
+ nodeMiddleware: false,
16997
+ outcome: failed("build", describeFailure(nextBuild))
16998
+ };
16999
+ }
17000
+ const openNext = await runStep(
17001
+ context,
17002
+ {
17003
+ command: context.toolchain.openNextCloudflare,
17004
+ args: [
17005
+ "build",
17006
+ "--skip-next-build",
17007
+ "--openNextConfigPath",
17008
+ platform.openNextConfigPath,
17009
+ "--config",
17010
+ platform.wranglerConfigPath,
17011
+ ...context.nextSupport.status === "needs-unsupported-flag" ? ["--dangerouslyUseUnsupportedNextVersion"] : []
17012
+ ],
17013
+ cwd: roots.appRoot,
17014
+ timeoutMs: 15 * 6e4,
17015
+ stage: "BUILDING",
17016
+ errorCode: "OPENNEXT_FAILED"
17017
+ },
17018
+ observer
15591
17019
  );
15592
- const assets = [];
15593
- async function visit(directory) {
15594
- const entries = await readdir2(directory, { withFileTypes: true });
15595
- for (const entry of entries.sort(
15596
- (left, right) => left.name.localeCompare(right.name)
15597
- )) {
15598
- if (excluded(entry.name)) continue;
15599
- const path = join7(directory, entry.name);
15600
- if (entry.isSymbolicLink())
15601
- throw new DeploymentError(
15602
- "ARTIFACT_INVALID",
15603
- "Static output cannot contain symbolic links."
17020
+ if (!openNext.ok) {
17021
+ const nodeMiddleware = openNext.output.some((line) => NODE_MIDDLEWARE_REFUSAL.test(line)) || await buildDeclaresNodeMiddleware(roots.appRoot);
17022
+ return {
17023
+ ok: false,
17024
+ nodeMiddleware,
17025
+ outcome: failed(
17026
+ "build",
17027
+ nodeMiddleware ? "OpenNext Cloudflare does not support Node.js middleware (Next.js 16 proxy.ts)." : describeFailure(openNext)
17028
+ )
17029
+ };
17030
+ }
17031
+ const layout = {
17032
+ appRoot: roots.appRoot,
17033
+ wranglerConfigPath: platform.wranglerConfigPath,
17034
+ ...OPEN_NEXT_LAYOUT
17035
+ };
17036
+ const bundle = await bundleWorker(
17037
+ context,
17038
+ layout,
17039
+ observer,
17040
+ "OPENNEXT_FAILED"
17041
+ );
17042
+ if (!bundle.ok) {
17043
+ return {
17044
+ ok: false,
17045
+ nodeMiddleware: false,
17046
+ outcome: failed("bundle", describeFailure(bundle))
17047
+ };
17048
+ }
17049
+ let artifact;
17050
+ try {
17051
+ artifact = await options.readOpenNextArtifact(
17052
+ roots.appRoot,
17053
+ context.identity,
17054
+ platform.wranglerConfigPath
17055
+ );
17056
+ } catch (error51) {
17057
+ return {
17058
+ ok: false,
17059
+ nodeMiddleware: false,
17060
+ outcome: failed("bundle", describeError(error51))
17061
+ };
17062
+ }
17063
+ const smoke = await smokeWorker(context, layout, observer);
17064
+ if (!smoke.ok) {
17065
+ return {
17066
+ ok: false,
17067
+ nodeMiddleware: false,
17068
+ outcome: failed("smoke", smoke.reason)
17069
+ };
17070
+ }
17071
+ return { ok: true, artifact };
17072
+ }
17073
+ return {
17074
+ id: "opennext-cloudflare",
17075
+ priority: 2,
17076
+ target: "cloudflare",
17077
+ supports: () => ({ supported: true }),
17078
+ async evaluate(context, observer) {
17079
+ const rememberedRewrite = context.pinnedVariant === PROXY_TO_MIDDLEWARE_VARIANT && context.facts.proxyFile !== null;
17080
+ if (rememberedRewrite) {
17081
+ await info(
17082
+ observer,
17083
+ `P2 opennext-cloudflare: the stored strategy already needed ${context.facts.proxyFile ?? "proxy"} rewritten to Edge middleware; building that way directly.`
17084
+ );
17085
+ } else {
17086
+ await info(
17087
+ observer,
17088
+ "P2 opennext-cloudflare: building the project as written."
17089
+ );
17090
+ const first = await attempt(context, context.workspace, observer);
17091
+ if (first.ok) {
17092
+ return {
17093
+ status: "success",
17094
+ artifact: { kind: "cloudflare-worker", artifact: first.artifact },
17095
+ notes: []
17096
+ };
17097
+ }
17098
+ if (!first.nodeMiddleware || !context.facts.proxyFile)
17099
+ return first.outcome;
17100
+ await warn(
17101
+ observer,
17102
+ `P2 opennext-cloudflare: OpenNext refused the Node.js middleware compiled from ${context.facts.proxyFile}. Retrying in a temporary workspace with it rewritten to Edge middleware.`
17103
+ );
17104
+ }
17105
+ let fork;
17106
+ try {
17107
+ fork = await context.workspace.fork("opennext-proxy-to-middleware");
17108
+ } catch (error51) {
17109
+ return failed("prepare", describeError(error51));
17110
+ }
17111
+ const conversion = await options.convertProxyToMiddleware(fork.appRoot);
17112
+ if (!conversion.converted) {
17113
+ return failed(
17114
+ "prepare",
17115
+ `Could not rewrite ${context.facts.proxyFile} to middleware (a middleware file already exists).`
15604
17116
  );
15605
- if (entry.isDirectory()) await visit(path);
15606
- else if (entry.isFile()) {
15607
- assets.push({
15608
- name: relative3(outputRoot, path).split(sep2).join("/"),
15609
- data: await readFile6(path),
15610
- contentType: contentType(path)
15611
- });
15612
17117
  }
17118
+ await warn(
17119
+ observer,
17120
+ `Converted ${conversion.file ?? context.facts.proxyFile} to edge middleware for this build only. Next.js 16 proxy runs on the Node.js runtime, which Cloudflare does not support; middleware runs on the Edge runtime, so Node.js-only APIs are unavailable. Your repository is unchanged.`
17121
+ );
17122
+ const second = await attempt(context, fork, observer);
17123
+ if (!second.ok) return second.outcome;
17124
+ return {
17125
+ status: "success",
17126
+ artifact: { kind: "cloudflare-worker", artifact: second.artifact },
17127
+ variant: PROXY_TO_MIDDLEWARE_VARIANT,
17128
+ notes: [
17129
+ `${conversion.file ?? "proxy"} was rewritten to Edge middleware for the build.`
17130
+ ]
17131
+ };
15613
17132
  }
15614
- }
17133
+ };
17134
+ }
17135
+
17136
+ // ../../packages/build/src/strategy/strategies/unavailable.ts
17137
+ function createUnavailableStrategy(options) {
17138
+ return {
17139
+ id: options.id,
17140
+ priority: options.priority,
17141
+ target: options.target,
17142
+ supports: () => ({ supported: false, reason: options.reason }),
17143
+ evaluate: async () => ({ status: "skipped", reason: options.reason })
17144
+ };
17145
+ }
17146
+ function createSplitCloudflareStrategy() {
17147
+ return createUnavailableStrategy({
17148
+ id: "split-cloudflare",
17149
+ priority: 3,
17150
+ target: "cloudflare-split",
17151
+ reason: "Split Cloudflare deployment needs the dispatcher to chain a middleware Worker in front of the app Worker; the runtime plane routes one Worker per hostname today."
17152
+ });
17153
+ }
17154
+ function createAwsMiddlewareCloudflareAppStrategy() {
17155
+ return createUnavailableStrategy({
17156
+ id: "aws-middleware-cloudflare-app",
17157
+ priority: 4,
17158
+ target: "aws-hybrid",
17159
+ reason: "Hybrid AWS middleware + Cloudflare app needs an AWS deploy target and cross-cloud request chaining; neither exists on this control plane."
17160
+ });
17161
+ }
17162
+
17163
+ // ../../packages/build/src/strategy/strategies/vinext-cloudflare.ts
17164
+ import { join as join14 } from "path";
17165
+ var VINEXT_MIN_NEXT_MAJOR = 16;
17166
+ var VINEXT_BUILD_SCRIPTS = ["esbuild", "workerd"];
17167
+ var VINEXT_OUTPUT = {
17168
+ outputRoot: "dist",
17169
+ bundleDir: "dist/.naro-bundle"
17170
+ };
17171
+ async function runCheck(context, observer) {
17172
+ if (!context.toolchain.vinext) return null;
17173
+ const held = [];
15615
17174
  try {
15616
- await visit(outputRoot);
15617
- } catch (error51) {
15618
- if (error51 instanceof DeploymentError) throw error51;
15619
- throw new DeploymentError(
15620
- "ARTIFACT_INVALID",
15621
- `Could not read the build output directory "${outputDirectory}".`,
15622
- { cause: error51 }
17175
+ const result = await context.ports.runCommand(
17176
+ {
17177
+ command: context.toolchain.vinext,
17178
+ args: ["check"],
17179
+ cwd: context.workspace.appRoot,
17180
+ timeoutMs: 2 * 6e4,
17181
+ stage: "BUILDING",
17182
+ errorCode: "BUILD_FAILED"
17183
+ },
17184
+ {
17185
+ onLog: (log2) => {
17186
+ held.push(log2);
17187
+ }
17188
+ }
15623
17189
  );
17190
+ const report = parseVinextCheckReport(result.stdout.join("\n"));
17191
+ if (!report || report.issues > 0) {
17192
+ for (const log2 of held) await observer.onLog(log2);
17193
+ }
17194
+ return report;
17195
+ } catch (error51) {
17196
+ for (const log2 of held) await observer.onLog(log2);
17197
+ await observer.onLog({
17198
+ level: "WARN",
17199
+ stage: "BUILDING",
17200
+ message: `vinext check could not run: ${describeError(error51)}`
17201
+ });
17202
+ return null;
15624
17203
  }
15625
- if (assets.length === 0)
15626
- throw new DeploymentError(
15627
- "ARTIFACT_INVALID",
15628
- `The build output directory "${outputDirectory}" contains no files.`
15629
- );
15630
- if (!assets.some((file2) => file2.name === "index.html"))
15631
- await onWarn?.(
15632
- `No index.html in "${outputDirectory}" \u2014 the site root will not serve a page.`
15633
- );
17204
+ }
17205
+ function createVinextCloudflareStrategy(options) {
17206
+ const toolchain = options.toolchain ?? VINEXT_TOOLCHAIN;
15634
17207
  return {
15635
- ...identity,
15636
- entrypoint: {
15637
- name: "worker.js",
15638
- data: new TextEncoder().encode(SHIM_WORKER_SOURCE),
15639
- contentType: "application/javascript"
17208
+ id: "vinext-cloudflare",
17209
+ priority: 1,
17210
+ target: "cloudflare",
17211
+ supports(context) {
17212
+ if (!context.vinextEnabled)
17213
+ return {
17214
+ supported: false,
17215
+ reason: "vinext is disabled for this project."
17216
+ };
17217
+ if (context.nextMajor < VINEXT_MIN_NEXT_MAJOR)
17218
+ return {
17219
+ supported: false,
17220
+ reason: `vinext targets Next.js ${String(VINEXT_MIN_NEXT_MAJOR)}; this project runs Next.js ${String(context.nextMajor)}.`
17221
+ };
17222
+ if (!context.toolchain.vinext)
17223
+ return {
17224
+ supported: false,
17225
+ reason: "The vinext toolchain is not installed on this build runner."
17226
+ };
17227
+ return { supported: true };
15640
17228
  },
15641
- modules: [],
15642
- assets,
15643
- compatibilityDate: COMPATIBILITY_DATE,
15644
- compatibilityFlags: [],
15645
- assetsBinding: "ASSETS",
15646
- assetsConfig: {
15647
- notFoundHandling: options.spaFallback ? "single-page-application" : "404-page"
17229
+ async evaluate(context, observer) {
17230
+ const adopt = !context.facts.usesVinext;
17231
+ if (adopt) {
17232
+ const decision = vinextRouteFromCheck(
17233
+ await runCheck(context, observer)
17234
+ );
17235
+ await info(observer, `P1 vinext-cloudflare: ${decision.reason}`);
17236
+ if (!decision.eligible) return failed("check", decision.reason);
17237
+ } else {
17238
+ await info(
17239
+ observer,
17240
+ "P1 vinext-cloudflare: the project already builds with vinext; using its own configuration."
17241
+ );
17242
+ }
17243
+ let fork;
17244
+ try {
17245
+ fork = await context.workspace.fork("vinext");
17246
+ } catch (error51) {
17247
+ return failed("prepare", describeError(error51));
17248
+ }
17249
+ if (adopt) {
17250
+ try {
17251
+ await options.createVinextBuildConfig(
17252
+ fork.appRoot,
17253
+ context.identity.deploymentId
17254
+ );
17255
+ } catch (error51) {
17256
+ return failed("prepare", describeError(error51));
17257
+ }
17258
+ if (context.packageManager.name === "pnpm") {
17259
+ await approvePnpmBuilds(fork.checkout, VINEXT_BUILD_SCRIPTS);
17260
+ }
17261
+ const [addCommand, ...addArgs] = context.packageManager.addArgs(toolchain);
17262
+ if (!addCommand) {
17263
+ return failed(
17264
+ "prepare",
17265
+ new DeploymentError(
17266
+ "UNSUPPORTED_PACKAGE_MANAGER",
17267
+ "Package manager cannot add packages."
17268
+ ).message
17269
+ );
17270
+ }
17271
+ const added = await runStep(
17272
+ context,
17273
+ {
17274
+ command: addCommand,
17275
+ args: addArgs,
17276
+ cwd: fork.appRoot,
17277
+ // Not production: npm and classic Yarn read that as omit=dev and
17278
+ // would prune the project's devDependencies while adding ours.
17279
+ env: {
17280
+ NODE_ENV: "development",
17281
+ NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
17282
+ },
17283
+ timeoutMs: 10 * 6e4,
17284
+ stage: "BUILDING",
17285
+ errorCode: "INSTALL_FAILED"
17286
+ },
17287
+ observer
17288
+ );
17289
+ if (!added.ok) return failed("prepare", describeFailure(added));
17290
+ }
17291
+ const projectBin = adopt ? null : await findProjectBin(fork.appRoot, fork.checkout, "vinext");
17292
+ const build = await runStep(
17293
+ context,
17294
+ {
17295
+ command: projectBin ?? context.toolchain.vinext ?? "vinext",
17296
+ args: ["build"],
17297
+ cwd: fork.appRoot,
17298
+ env: { ...context.env, CI: "1" },
17299
+ timeoutMs: 15 * 6e4,
17300
+ stage: "BUILDING",
17301
+ errorCode: "BUILD_FAILED"
17302
+ },
17303
+ observer
17304
+ );
17305
+ if (!build.ok) return failed("build", describeFailure(build));
17306
+ let artifact;
17307
+ try {
17308
+ artifact = await options.readVinextArtifact(
17309
+ fork.appRoot,
17310
+ context.identity
17311
+ );
17312
+ } catch (error51) {
17313
+ return failed("bundle", describeError(error51));
17314
+ }
17315
+ const smoke = await smokeWorker(
17316
+ context,
17317
+ {
17318
+ appRoot: fork.appRoot,
17319
+ wranglerConfigPath: join14(
17320
+ fork.appRoot,
17321
+ "dist",
17322
+ "server",
17323
+ "wrangler.json"
17324
+ ),
17325
+ ...VINEXT_OUTPUT
17326
+ },
17327
+ observer
17328
+ );
17329
+ if (!smoke.ok) return failed("smoke", smoke.reason);
17330
+ return {
17331
+ status: "success",
17332
+ artifact: { kind: "cloudflare-worker", artifact },
17333
+ notes: adopt ? [
17334
+ "Built with the naro-provided vinext toolchain in a temporary workspace."
17335
+ ] : ["Built with the project's own vinext configuration."]
17336
+ };
15648
17337
  }
15649
17338
  };
15650
17339
  }
15651
17340
 
17341
+ // ../../packages/build/src/strategy/workspace.ts
17342
+ import { cp } from "fs/promises";
17343
+ import { basename as basename2, join as join15, relative as relative5, resolve as resolve6 } from "path";
17344
+ var SKIPPED_ANYWHERE = /* @__PURE__ */ new Set(["node_modules"]);
17345
+ function shouldCopy(path) {
17346
+ const name = basename2(path);
17347
+ if (SKIPPED_ANYWHERE.has(name)) return false;
17348
+ return !name.startsWith(".next-deploy-");
17349
+ }
17350
+ async function copyCheckout(source, target) {
17351
+ const origin = resolve6(source);
17352
+ await cp(origin, target, {
17353
+ recursive: true,
17354
+ verbatimSymlinks: true,
17355
+ filter: (path) => resolve6(path) === origin || shouldCopy(path)
17356
+ });
17357
+ }
17358
+ function forkedAppRoot(checkout, appRoot, forkCheckout) {
17359
+ return join15(forkCheckout, relative5(checkout, appRoot));
17360
+ }
17361
+
15652
17362
  // ../../packages/build/src/local-build-executor.ts
15653
- var defaultDependencies = {
15654
- createWorkspace: () => mkdtemp(join8(tmpdir(), "next-deploy-build-")),
15655
- removeWorkspace: (path) => rm(path, { recursive: true, force: true }),
17363
+ function concurrencyFromEnvironment() {
17364
+ const raw = Number(process.env.NARO_RESOLVER_CONCURRENCY ?? "1");
17365
+ return Number.isInteger(raw) && raw > 0 ? raw : 1;
17366
+ }
17367
+ var defaultLocalBuildDependencies = {
17368
+ createWorkspace: () => mkdtemp(join16(tmpdir(), "next-deploy-build-")),
17369
+ removeWorkspace: (path) => rm2(path, { recursive: true, force: true }),
15656
17370
  runCommand,
15657
17371
  detectPackageManager,
15658
17372
  resolveAppRoot,
@@ -15665,8 +17379,38 @@ var defaultDependencies = {
15665
17379
  wranglerCommand: "wrangler",
15666
17380
  cacheFetch: (input, init) => fetch(input, init),
15667
17381
  convertProxyToMiddleware,
15668
- commandAvailable: hasCommand
17382
+ commandAvailable: hasCommand,
17383
+ readInstalledNextVersion,
17384
+ now: () => /* @__PURE__ */ new Date(),
17385
+ createVinextBuildConfig,
17386
+ readVinextArtifact,
17387
+ vinextCommand: "vinext",
17388
+ forkWorkspace: copyCheckout,
17389
+ collectProjectFacts,
17390
+ hashFingerprintInputs: hashStrategyInputs,
17391
+ computeFingerprint: computeStrategyFingerprint,
17392
+ targets: { cloudflare: true },
17393
+ resolverConcurrency: concurrencyFromEnvironment()
15669
17394
  };
17395
+ async function readInstalledNextVersion(appRoot, checkout) {
17396
+ let dir = resolve7(appRoot);
17397
+ const stop = resolve7(checkout);
17398
+ for (; ; ) {
17399
+ try {
17400
+ const source = await readFile12(
17401
+ join16(dir, "node_modules", "next", "package.json"),
17402
+ "utf8"
17403
+ );
17404
+ const version2 = JSON.parse(source).version;
17405
+ return typeof version2 === "string" ? version2 : null;
17406
+ } catch {
17407
+ }
17408
+ if (dir === stop) return null;
17409
+ const parent = dirname3(dir);
17410
+ if (parent === dir) return null;
17411
+ dir = parent;
17412
+ }
17413
+ }
15670
17414
  async function hasCommand(command) {
15671
17415
  try {
15672
17416
  await runCommand(
@@ -15685,6 +17429,14 @@ async function hasCommand(command) {
15685
17429
  return false;
15686
17430
  }
15687
17431
  }
17432
+ async function pathExists3(path) {
17433
+ try {
17434
+ await access8(path);
17435
+ return true;
17436
+ } catch {
17437
+ return false;
17438
+ }
17439
+ }
15688
17440
  function sanitizeCacheKey(value) {
15689
17441
  return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 128);
15690
17442
  }
@@ -15714,25 +17466,42 @@ async function trySave(scope, key, sourceDir, ports, observer) {
15714
17466
  async function notifyStatus(observer, status) {
15715
17467
  await observer.onStatus?.(status);
15716
17468
  }
15717
- async function info(observer, stage, message) {
17469
+ async function info2(observer, stage, message) {
15718
17470
  await observer.onLog({ level: "INFO", stage, message });
15719
17471
  }
17472
+ function defaultStrategies(dependencies) {
17473
+ return [
17474
+ createVinextCloudflareStrategy({
17475
+ createVinextBuildConfig: dependencies.createVinextBuildConfig ?? createVinextBuildConfig,
17476
+ readVinextArtifact: dependencies.readVinextArtifact ?? readVinextArtifact
17477
+ }),
17478
+ createOpenNextCloudflareStrategy({
17479
+ createPlatformBuildConfig: dependencies.createPlatformBuildConfig,
17480
+ convertProxyToMiddleware: dependencies.convertProxyToMiddleware ?? convertProxyToMiddleware,
17481
+ readOpenNextArtifact: dependencies.readOpenNextArtifact
17482
+ }),
17483
+ createSplitCloudflareStrategy(),
17484
+ createAwsMiddlewareCloudflareAppStrategy(),
17485
+ createOpenNextAwsStrategy()
17486
+ ];
17487
+ }
15720
17488
  var LocalBuildExecutor = class {
15721
- constructor(dependencies = defaultDependencies) {
17489
+ constructor(dependencies = defaultLocalBuildDependencies) {
15722
17490
  this.dependencies = dependencies;
15723
17491
  }
15724
17492
  async build(input, observer, cache) {
15725
17493
  const workspace = await this.dependencies.createWorkspace();
15726
- const checkout = join8(workspace, "checkout");
15727
- const pmCacheDir = join8(workspace, ".pm-store");
17494
+ const checkout = join16(workspace, "checkout");
17495
+ const pmCacheDir = join16(workspace, ".pm-store");
15728
17496
  let cacheSeq = 0;
17497
+ const commandAvailable = this.dependencies.commandAvailable ?? hasCommand;
15729
17498
  const compressor = "zstd";
15730
- const canCompress = cache ? await (this.dependencies.commandAvailable ?? hasCommand)(compressor) : false;
17499
+ const canCompress = cache ? await commandAvailable(compressor) : false;
15731
17500
  const cachePorts = cache && canCompress ? {
15732
17501
  transport: cache,
15733
17502
  fetch: this.dependencies.cacheFetch ?? ((url2, init) => fetch(url2, init)),
15734
17503
  runCommand: this.dependencies.runCommand,
15735
- tmpFile: (suffix) => join8(workspace, `cache-${String(cacheSeq++)}${suffix}`),
17504
+ tmpFile: (suffix) => join16(workspace, `cache-${String(cacheSeq++)}${suffix}`),
15736
17505
  // One latch for the whole build so a disabled cache warns once.
15737
17506
  report: { unavailable: false }
15738
17507
  } : null;
@@ -15809,27 +17578,31 @@ var LocalBuildExecutor = class {
15809
17578
  buildCommand: input.buildCommand ?? null,
15810
17579
  outputDirectory: input.outputDirectory ?? null
15811
17580
  });
15812
- await info(observer, "CLONING", `Detected ${resolved.description}`);
17581
+ await info2(observer, "CLONING", `Detected ${resolved.description}`);
15813
17582
  const isNext = resolved.preset.id === "next";
15814
- let platformConfig = null;
15815
- let packageManagerConfigPath = null;
15816
- if (isNext) {
15817
- const conversion = await (this.dependencies.convertProxyToMiddleware ?? convertProxyToMiddleware)(appRoot);
15818
- if (conversion.converted) {
15819
- await observer.onLog({
15820
- level: "WARN",
15821
- stage: "CLONING",
15822
- message: `Converted ${conversion.file ?? "proxy"} to edge middleware. Next.js 16 proxy runs on the Node.js runtime, which Cloudflare does not support; middleware runs on the Edge runtime, so Node.js-only APIs are unavailable.`
15823
- });
17583
+ if (isNext && resolved.dependencyRange) {
17584
+ const declared = declaredNextMajor(resolved.dependencyRange);
17585
+ if (declared !== null && declared < MIN_SUPPORTED_NEXT_MAJOR) {
17586
+ throw new DeploymentError(
17587
+ "NEXT_VERSION_UNSUPPORTED",
17588
+ `Naro deploys Next.js ${String(MIN_SUPPORTED_NEXT_MAJOR)} or newer; this project declares next@${resolved.dependencyRange}. Upgrade Next.js and deploy again.`
17589
+ );
15824
17590
  }
15825
- platformConfig = await this.dependencies.createPlatformBuildConfig(
15826
- appRoot,
15827
- input.deploymentId
15828
- );
15829
- packageManagerConfigPath = platformConfig.packageManagerConfigPath;
15830
- } else if (resolved.buildScript !== null) {
17591
+ }
17592
+ let packageManagerConfigPath = null;
17593
+ if (resolved.buildScript !== null) {
15831
17594
  packageManagerConfigPath = await this.dependencies.createPackageManagerConfig(appRoot);
15832
17595
  }
17596
+ const fingerprintEntries = isNext ? await (this.dependencies.hashFingerprintInputs ?? hashStrategyInputs)(
17597
+ checkout,
17598
+ appRoot
17599
+ ) : [];
17600
+ let artifact = null;
17601
+ let strategyRecord;
17602
+ const identity = {
17603
+ deploymentId: input.deploymentId,
17604
+ projectId: input.projectId
17605
+ };
15833
17606
  if (resolved.buildScript !== null) {
15834
17607
  await notifyStatus(observer, "INSTALLING");
15835
17608
  const packageManager = await this.dependencies.detectPackageManager(checkout);
@@ -15853,28 +17626,57 @@ var LocalBuildExecutor = class {
15853
17626
  );
15854
17627
  }
15855
17628
  }
15856
- await this.dependencies.runCommand(
15857
- {
15858
- command: installCommand,
15859
- args: cachePorts ? [...installArgs, ...packageManager.cacheArgs(pmCacheDir)] : installArgs,
15860
- cwd: checkout,
15861
- env: {
15862
- NODE_ENV: "development",
15863
- NPM_CONFIG_USERCONFIG: packageManagerConfigPath,
15864
- ...cachePorts ? {
15865
- npm_config_store_dir: pmCacheDir,
15866
- npm_config_cache: pmCacheDir,
15867
- YARN_CACHE_FOLDER: pmCacheDir
15868
- } : {}
17629
+ const install = async (cwd) => {
17630
+ await this.dependencies.runCommand(
17631
+ {
17632
+ command: installCommand,
17633
+ args: cachePorts ? [...installArgs, ...packageManager.cacheArgs(pmCacheDir)] : installArgs,
17634
+ cwd,
17635
+ env: {
17636
+ NODE_ENV: "development",
17637
+ NPM_CONFIG_USERCONFIG: packageManagerConfigPath,
17638
+ ...cachePorts ? {
17639
+ npm_config_store_dir: pmCacheDir,
17640
+ npm_config_cache: pmCacheDir,
17641
+ YARN_CACHE_FOLDER: pmCacheDir
17642
+ } : {}
17643
+ },
17644
+ timeoutMs: 10 * 6e4,
17645
+ stage: "INSTALLING",
17646
+ errorCode: "INSTALL_FAILED"
15869
17647
  },
15870
- timeoutMs: 10 * 6e4,
15871
- stage: "INSTALLING",
15872
- errorCode: "INSTALL_FAILED"
15873
- },
15874
- observer
15875
- );
17648
+ observer
17649
+ );
17650
+ };
17651
+ await install(checkout);
17652
+ let nextSupport = {
17653
+ status: "unknown",
17654
+ version: null,
17655
+ reason: ""
17656
+ };
17657
+ if (isNext) {
17658
+ const installed = await (this.dependencies.readInstalledNextVersion ?? readInstalledNextVersion)(appRoot, checkout);
17659
+ if (installed) {
17660
+ nextSupport = classifyNextVersion(
17661
+ installed,
17662
+ (this.dependencies.now ?? (() => /* @__PURE__ */ new Date()))()
17663
+ );
17664
+ if (nextSupport.status === "too-old")
17665
+ throw new DeploymentError(
17666
+ "NEXT_VERSION_UNSUPPORTED",
17667
+ nextSupport.reason
17668
+ );
17669
+ if (nextSupport.status === "needs-unsupported-flag" || nextSupport.status === "outside-peer-range") {
17670
+ await observer.onLog({
17671
+ level: "WARN",
17672
+ stage: "BUILDING",
17673
+ message: nextSupport.reason
17674
+ });
17675
+ }
17676
+ }
17677
+ }
15876
17678
  await notifyStatus(observer, "BUILDING");
15877
- const buildCacheDir = resolved.preset.cachePath ? join8(appRoot, resolved.preset.cachePath) : null;
17679
+ const buildCacheDir = resolved.preset.cachePath ? join16(appRoot, resolved.preset.cachePath) : null;
15878
17680
  const buildCacheKey = sanitizeCacheKey(input.branch);
15879
17681
  if (cachePorts && buildCacheDir) {
15880
17682
  await tryRestore(
@@ -15885,59 +17687,97 @@ var LocalBuildExecutor = class {
15885
17687
  observer
15886
17688
  );
15887
17689
  }
15888
- await this.dependencies.runCommand(
15889
- {
15890
- command: packageManager.name,
15891
- args: ["run", resolved.buildScript],
15892
- cwd: appRoot,
15893
- env: {
15894
- ...input.env,
15895
- ...isNext ? { NEXT_PRIVATE_STANDALONE: "true" } : {},
15896
- NPM_CONFIG_USERCONFIG: packageManagerConfigPath
15897
- },
15898
- timeoutMs: 15 * 6e4,
15899
- stage: "BUILDING",
15900
- errorCode: "BUILD_FAILED"
15901
- },
15902
- observer
15903
- );
15904
17690
  if (isNext) {
15905
- await this.dependencies.runCommand(
15906
- {
15907
- command: this.dependencies.openNextCommand,
15908
- args: [
15909
- "build",
15910
- "--skip-next-build",
15911
- "--openNextConfigPath",
15912
- platformConfig.openNextConfigPath,
15913
- "--config",
15914
- platformConfig.wranglerConfigPath
15915
- ],
15916
- cwd: appRoot,
15917
- timeoutMs: 15 * 6e4,
15918
- stage: "BUILDING",
15919
- errorCode: "OPENNEXT_FAILED"
17691
+ const nextVersion = nextSupport.version;
17692
+ const declaredMajor = resolved.dependencyRange ? declaredNextMajor(resolved.dependencyRange) : null;
17693
+ const nextMajor = nextVersion?.major ?? declaredMajor ?? 16;
17694
+ const facts = await (this.dependencies.collectProjectFacts ?? collectProjectFacts)(appRoot);
17695
+ const fingerprint = await (this.dependencies.computeFingerprint ?? computeStrategyFingerprint)({
17696
+ checkout,
17697
+ appRoot,
17698
+ rootDirectory: input.rootDirectory,
17699
+ vinextEnabled: input.vinextEnabled !== false,
17700
+ entries: fingerprintEntries,
17701
+ nextVersion: nextVersion ? `${String(nextVersion.major)}.${String(nextVersion.minor)}.${String(nextVersion.patch)}` : "unknown"
17702
+ });
17703
+ const pristine = join16(workspace, "pristine");
17704
+ const fork = this.dependencies.forkWorkspace ?? defaultFork;
17705
+ await fork(checkout, pristine);
17706
+ let forks = 0;
17707
+ const forkWorkspace = async (label2) => {
17708
+ const target = join16(
17709
+ workspace,
17710
+ "forks",
17711
+ `${String(forks++)}-${label2.replace(/[^a-z0-9-]/gi, "-")}`
17712
+ );
17713
+ await fork(pristine, target);
17714
+ await install(target);
17715
+ return {
17716
+ checkout: target,
17717
+ appRoot: forkedAppRoot(checkout, appRoot, target)
17718
+ };
17719
+ };
17720
+ const vinextCommand = this.dependencies.vinextCommand;
17721
+ const openNextAwsCommand = this.dependencies.openNextAwsCommand;
17722
+ const context = {
17723
+ identity,
17724
+ branch: input.branch,
17725
+ env: input.env ?? {},
17726
+ packageManager,
17727
+ packageManagerConfigPath,
17728
+ buildScript: resolved.buildScript,
17729
+ nextVersion: nextVersion ? `${String(nextVersion.major)}.${String(nextVersion.minor)}.${String(nextVersion.patch)}` : "unknown",
17730
+ nextMajor,
17731
+ nextSupport,
17732
+ vinextEnabled: input.vinextEnabled !== false,
17733
+ facts,
17734
+ workspace: { checkout, appRoot, fork: forkWorkspace },
17735
+ toolchain: {
17736
+ openNextCloudflare: this.dependencies.openNextCommand,
17737
+ wrangler: this.dependencies.wranglerCommand,
17738
+ ...vinextCommand && await commandAvailable(vinextCommand) ? { vinext: vinextCommand } : {},
17739
+ ...openNextAwsCommand && await commandAvailable(openNextAwsCommand) ? { openNextAws: openNextAwsCommand } : {}
15920
17740
  },
15921
- observer
17741
+ targets: this.dependencies.targets ?? { cloudflare: true },
17742
+ ports: {
17743
+ runCommand: this.dependencies.runCommand,
17744
+ smoke: this.dependencies.smokeTester ?? createSmokeTester(),
17745
+ commandAvailable
17746
+ }
17747
+ };
17748
+ const resolution = await resolveDeploymentStrategy(
17749
+ this.dependencies.strategies ?? defaultStrategies(this.dependencies),
17750
+ context,
17751
+ observer,
17752
+ {
17753
+ pinned: input.strategy ?? null,
17754
+ fingerprint: fingerprint.hash,
17755
+ concurrency: this.dependencies.resolverConcurrency ?? 1,
17756
+ ...this.dependencies.now ? { now: this.dependencies.now } : {}
17757
+ }
15922
17758
  );
17759
+ if (resolution.outcome.artifact.kind !== "cloudflare-worker") {
17760
+ throw new DeploymentError(
17761
+ "NO_DEPLOYMENT_STRATEGY",
17762
+ `Strategy ${resolution.strategy.id} produced a ${resolution.outcome.artifact.kind} artifact, which this control plane cannot deploy.`
17763
+ );
17764
+ }
17765
+ artifact = resolution.outcome.artifact.artifact;
17766
+ strategyRecord = resolution.record;
17767
+ await observer.onMetadata?.({ strategy: resolution.record });
17768
+ } else {
15923
17769
  await this.dependencies.runCommand(
15924
17770
  {
15925
- command: this.dependencies.wranglerCommand,
15926
- args: [
15927
- "deploy",
15928
- "--dry-run",
15929
- "--config",
15930
- platformConfig.wranglerConfigPath,
15931
- "--outdir",
15932
- ".open-next/worker-bundle"
15933
- ],
17771
+ command: packageManager.name,
17772
+ args: ["run", resolved.buildScript],
15934
17773
  cwd: appRoot,
15935
17774
  env: {
15936
- WRANGLER_LOG_PATH: join8(appRoot, ".open-next", "wrangler.log")
17775
+ ...input.env,
17776
+ NPM_CONFIG_USERCONFIG: packageManagerConfigPath
15937
17777
  },
15938
- timeoutMs: 5 * 6e4,
17778
+ timeoutMs: 15 * 6e4,
15939
17779
  stage: "BUILDING",
15940
- errorCode: "OPENNEXT_FAILED"
17780
+ errorCode: "BUILD_FAILED"
15941
17781
  },
15942
17782
  observer
15943
17783
  );
@@ -15946,7 +17786,7 @@ var LocalBuildExecutor = class {
15946
17786
  if (depsKey && !depsRestored) {
15947
17787
  await trySave("deps", depsKey, pmCacheDir, cachePorts, observer);
15948
17788
  }
15949
- if (buildCacheDir) {
17789
+ if (buildCacheDir && await pathExists3(buildCacheDir)) {
15950
17790
  await trySave(
15951
17791
  "next",
15952
17792
  buildCacheKey,
@@ -15958,27 +17798,101 @@ var LocalBuildExecutor = class {
15958
17798
  }
15959
17799
  }
15960
17800
  await notifyStatus(observer, "DEPLOYING");
15961
- const identity = {
15962
- deploymentId: input.deploymentId,
15963
- projectId: input.projectId
17801
+ artifact ??= await this.readStatic(appRoot, resolved, identity, observer);
17802
+ return {
17803
+ gitCommitSha,
17804
+ artifact,
17805
+ ...strategyRecord ? { strategy: strategyRecord } : {}
15964
17806
  };
15965
- const artifact = isNext ? await this.dependencies.readOpenNextArtifact(
15966
- appRoot,
15967
- identity,
15968
- platformConfig.wranglerConfigPath
15969
- ) : await this.dependencies.readStaticArtifact(
15970
- appRoot,
15971
- resolved.outputDirectory,
15972
- identity,
15973
- { spaFallback: resolved.preset.spaFallback },
15974
- (message) => observer.onLog({ level: "WARN", stage: "DEPLOYING", message })
15975
- );
15976
- return { gitCommitSha, artifact };
15977
17807
  } finally {
15978
17808
  await this.dependencies.removeWorkspace(workspace);
15979
17809
  }
15980
17810
  }
17811
+ readStatic(appRoot, resolved, identity, observer) {
17812
+ return this.dependencies.readStaticArtifact(
17813
+ appRoot,
17814
+ resolved.outputDirectory,
17815
+ identity,
17816
+ { spaFallback: resolved.preset.spaFallback },
17817
+ (message) => observer.onLog({
17818
+ level: "WARN",
17819
+ stage: "DEPLOYING",
17820
+ message
17821
+ })
17822
+ );
17823
+ }
15981
17824
  };
17825
+ var defaultFork = copyCheckout;
17826
+
17827
+ // ../../packages/build/src/log-batch.ts
17828
+ var DEPLOYMENT_LOG_BATCH_LIMIT = 40;
17829
+ var DEPLOYMENT_LOG_FLUSH_MS = 1e3;
17830
+ function createLogBatcher(options) {
17831
+ let buffer = [];
17832
+ let timer = null;
17833
+ let pending = null;
17834
+ function disarm() {
17835
+ if (timer === null) return;
17836
+ clearTimeout(timer);
17837
+ timer = null;
17838
+ }
17839
+ async function flush() {
17840
+ disarm();
17841
+ if (pending !== null) {
17842
+ const { error: error51 } = pending;
17843
+ pending = null;
17844
+ throw error51;
17845
+ }
17846
+ if (buffer.length === 0) return;
17847
+ const batch = buffer;
17848
+ buffer = [];
17849
+ await options.send(batch);
17850
+ }
17851
+ function arm() {
17852
+ if (timer !== null || !options.intervalMs) return;
17853
+ timer = setTimeout(() => {
17854
+ timer = null;
17855
+ flush().catch((error51) => {
17856
+ pending ??= { error: error51 };
17857
+ });
17858
+ }, options.intervalMs);
17859
+ timer.unref?.();
17860
+ }
17861
+ return {
17862
+ add: async (log2) => {
17863
+ buffer.push(log2);
17864
+ if (buffer.length >= options.limit) {
17865
+ await flush();
17866
+ return;
17867
+ }
17868
+ arm();
17869
+ },
17870
+ flush
17871
+ };
17872
+ }
17873
+
17874
+ // ../../packages/build/src/log-sender.ts
17875
+ function createLogSender(ports) {
17876
+ let warned = false;
17877
+ return async (logs) => {
17878
+ if (ports.toIngest !== null) {
17879
+ try {
17880
+ const response = await ports.toIngest(logs);
17881
+ if (response.ok) return;
17882
+ } catch {
17883
+ }
17884
+ }
17885
+ await ports.toControlPlane(logs);
17886
+ if (warned) return;
17887
+ warned = true;
17888
+ try {
17889
+ await ports.warn(
17890
+ ports.toIngest === null ? "Sent build logs through the control plane because no log ingest URL was configured." : `Sent build logs through the control plane because the log ingest worker at ${ports.ingestUrl ?? "the configured URL"} did not accept them.`
17891
+ );
17892
+ } catch {
17893
+ }
17894
+ };
17895
+ }
15982
17896
 
15983
17897
  // ../../packages/sdk/src/auth.ts
15984
17898
  import { homedir as homedir2 } from "os";
@@ -16023,6 +17937,8 @@ async function errorFromResponse(response, context) {
16023
17937
  if (body?.error?.fields !== void 0) details.fields = body.error.fields;
16024
17938
  if (typeof body?.error?.installUrl === "string")
16025
17939
  details.installUrl = body.error.installUrl;
17940
+ if (typeof body?.error?.statementIndex === "number")
17941
+ details.statementIndex = body.error.statementIndex;
16026
17942
  return new NaroError(
16027
17943
  code,
16028
17944
  serverMessage ?? `${context} failed: the control plane returned ${String(response.status)}.`,
@@ -16036,12 +17952,12 @@ async function errorFromResponse(response, context) {
16036
17952
  // ../../packages/sdk/src/session.ts
16037
17953
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
16038
17954
  import { homedir } from "os";
16039
- import { join as join9 } from "path";
17955
+ import { join as join17 } from "path";
16040
17956
  function credentialsDir(home = homedir()) {
16041
- return join9(home, ".naro");
17957
+ return join17(home, ".naro");
16042
17958
  }
16043
17959
  function credentialsFile(home = homedir()) {
16044
- return join9(credentialsDir(home), "credentials.json");
17960
+ return join17(credentialsDir(home), "credentials.json");
16045
17961
  }
16046
17962
  function saveSession(session, home = homedir()) {
16047
17963
  mkdirSync(credentialsDir(home), { recursive: true, mode: 448 });
@@ -16082,7 +17998,7 @@ function controlPlaneUrl(env = process.env) {
16082
17998
  }
16083
17999
 
16084
18000
  // ../../packages/sdk/src/callback-server.ts
16085
- import { createServer } from "http";
18001
+ import { createServer as createServer2 } from "http";
16086
18002
 
16087
18003
  // ../../packages/sdk/src/callback-page.ts
16088
18004
  function escapeHtml(value) {
@@ -16229,11 +18145,11 @@ function sendPage(res, status, html) {
16229
18145
  async function startCallbackServer() {
16230
18146
  let resolveCode;
16231
18147
  let rejectCode;
16232
- const codePromise = new Promise((resolve5, reject) => {
16233
- resolveCode = resolve5;
18148
+ const codePromise = new Promise((resolve10, reject) => {
18149
+ resolveCode = resolve10;
16234
18150
  rejectCode = reject;
16235
18151
  });
16236
- const server = createServer((req, res) => {
18152
+ const server = createServer2((req, res) => {
16237
18153
  const url2 = new URL(req.url ?? "/", "http://localhost");
16238
18154
  if (url2.pathname !== "/callback") {
16239
18155
  res.statusCode = 404;
@@ -16259,7 +18175,7 @@ async function startCallbackServer() {
16259
18175
  sendPage(res, 200, successPage());
16260
18176
  resolveCode(code);
16261
18177
  });
16262
- await new Promise((resolve5) => server.listen(0, "127.0.0.1", resolve5));
18178
+ await new Promise((resolve10) => server.listen(0, "127.0.0.1", resolve10));
16263
18179
  const port = server.address().port;
16264
18180
  return {
16265
18181
  port,
@@ -16484,10 +18400,12 @@ function buildLogEntry(row) {
16484
18400
  }
16485
18401
 
16486
18402
  // ../../packages/sdk/src/client.ts
18403
+ var DEFAULT_REQUEST_TIMEOUT_MS = 45e3;
16487
18404
  var NaroClient = class {
16488
18405
  baseUrl;
16489
18406
  getAccessToken;
16490
18407
  doFetch;
18408
+ requestTimeoutMs;
16491
18409
  teams;
16492
18410
  projects;
16493
18411
  github;
@@ -16495,10 +18413,12 @@ var NaroClient = class {
16495
18413
  env;
16496
18414
  domains;
16497
18415
  logs;
18416
+ db;
16498
18417
  constructor(options) {
16499
18418
  this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
16500
18419
  this.getAccessToken = options.getAccessToken;
16501
18420
  this.doFetch = options.fetch ?? fetch;
18421
+ this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
16502
18422
  this.teams = new TeamsApi(this);
16503
18423
  this.projects = new ProjectsApi(this);
16504
18424
  this.github = new GithubApi(this);
@@ -16506,6 +18426,7 @@ var NaroClient = class {
16506
18426
  this.env = new EnvApi(this);
16507
18427
  this.domains = new DomainsApi(this);
16508
18428
  this.logs = new LogsApi(this);
18429
+ this.db = new DbApi(this);
16509
18430
  }
16510
18431
  async request(path, options) {
16511
18432
  const token = await this.getAccessToken();
@@ -16517,9 +18438,17 @@ var NaroClient = class {
16517
18438
  Authorization: `Bearer ${token}`,
16518
18439
  ...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
16519
18440
  },
16520
- ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
18441
+ ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
18442
+ signal: AbortSignal.timeout(this.requestTimeoutMs)
16521
18443
  });
16522
18444
  } catch (error51) {
18445
+ if (error51 instanceof Error && (error51.name === "TimeoutError" || error51.name === "AbortError")) {
18446
+ throw new NaroError(
18447
+ "TIMEOUT",
18448
+ `${options.context} timed out after ${String(Math.round(this.requestTimeoutMs / 1e3))}s.`,
18449
+ { cause: error51 }
18450
+ );
18451
+ }
16523
18452
  throw new NaroError(
16524
18453
  "NETWORK_ERROR",
16525
18454
  `${options.context} failed: could not reach ${this.baseUrl}.`,
@@ -16687,11 +18616,23 @@ var DeploymentsApi = class {
16687
18616
  );
16688
18617
  return deploymentInfo(body.deployment);
16689
18618
  }
18619
+ /**
18620
+ * Stops an in-flight build (QUEUED through BUILDING) and marks the row
18621
+ * FAILED/CANCELED. A deployment that has reached DEPLOYING is past the
18622
+ * point where stopping is safe and answers DEPLOYMENT_NOT_CANCELABLE.
18623
+ */
18624
+ async cancel(projectId, deploymentId) {
18625
+ const body = await this.client.request(
18626
+ `/api/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/cancel`,
18627
+ { method: "POST", context: "Cancel deployment" }
18628
+ );
18629
+ return deploymentInfo(body.deployment);
18630
+ }
16690
18631
  /** Polls a deployment until READY/FAILED, streaming new build logs. */
16691
18632
  async wait(deploymentId, options = {}) {
16692
18633
  const intervalMs = options.intervalMs ?? 2500;
16693
18634
  const timeoutMs = options.timeoutMs ?? 15 * 6e4;
16694
- const sleep = options.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
18635
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve10) => setTimeout(resolve10, ms)));
16695
18636
  const startedAt = Date.now();
16696
18637
  let lastStatus = null;
16697
18638
  let lastSequence = -1;
@@ -16715,7 +18656,7 @@ var DeploymentsApi = class {
16715
18656
  { details: { deploymentId } }
16716
18657
  );
16717
18658
  }
16718
- await sleep(intervalMs);
18659
+ await sleep2(intervalMs);
16719
18660
  }
16720
18661
  }
16721
18662
  };
@@ -16771,6 +18712,9 @@ var EnvApi = class {
16771
18712
  return matches.length;
16772
18713
  }
16773
18714
  };
18715
+ function normalizeHostname(hostname3) {
18716
+ return hostname3.trim().toLowerCase().replace(/\.+$/, "");
18717
+ }
16774
18718
  var DomainsApi = class {
16775
18719
  constructor(client) {
16776
18720
  this.client = client;
@@ -16782,12 +18726,27 @@ var DomainsApi = class {
16782
18726
  );
16783
18727
  return body.domains;
16784
18728
  }
16785
- async add(projectId, hostname3) {
16786
- const body = await this.client.request(
16787
- `/api/projects/${encodeURIComponent(projectId)}/domains`,
16788
- { method: "POST", body: { hostname: hostname3 }, context: "Add domain" }
16789
- );
16790
- return body.domain;
18729
+ /**
18730
+ * Attaches a hostname.
18731
+ *
18732
+ * `withWww` is the apex pair the web panel offers, ticked by default: the
18733
+ * server also registers www.<apex> and turns the apex into a 308 redirect to
18734
+ * it, because most registrars (Gabia, Whois, Cafe24, GoDaddy) cannot put a
18735
+ * CNAME on an apex at all. Leaving it out is not the same as passing false —
18736
+ * it means "don't ask for the pair" — so any caller that wants the panel's
18737
+ * behaviour has to send it. The server ignores it for a hostname that is not
18738
+ * an apex, so it is safe to pass unconditionally.
18739
+ */
18740
+ async add(projectId, hostname3, options = {}) {
18741
+ const body = await this.client.request(`/api/projects/${encodeURIComponent(projectId)}/domains`, {
18742
+ method: "POST",
18743
+ body: {
18744
+ hostname: hostname3,
18745
+ ...options.withWww === void 0 ? {} : { withWww: options.withWww }
18746
+ },
18747
+ context: "Add domain"
18748
+ });
18749
+ return body.companion ? { ...body.domain, companion: body.companion } : body.domain;
16791
18750
  }
16792
18751
  async refresh(projectId, domainId) {
16793
18752
  const body = await this.client.request(
@@ -16796,6 +18755,38 @@ var DomainsApi = class {
16796
18755
  );
16797
18756
  return body.domain;
16798
18757
  }
18758
+ /**
18759
+ * Forces a check instead of reading the last one back: the control plane
18760
+ * asks Cloudflare to revalidate now (skipping its hours-long backoff),
18761
+ * probes DNS and returns the recomputed diagnosis. This is what turns a
18762
+ * stuck "pending" into something the user can act on, so `refresh` is not
18763
+ * a substitute for it.
18764
+ *
18765
+ * `auto: true` marks a check nobody asked for (a polling loop, a page that
18766
+ * refreshes itself). Revalidating rotates Cloudflare's DCV token, so a
18767
+ * background check would invalidate the TXT value the user is halfway
18768
+ * through pasting into their registrar; the server reads the stored state
18769
+ * instead for those. The DNS probe happens either way — it is the cheap half.
18770
+ *
18771
+ * The body is always sent, even for the default: a POST with no body at all
18772
+ * is the shape a JSON-parsing route rejects outright.
18773
+ */
18774
+ async verify(projectId, domainId, options = {}) {
18775
+ const body = await this.client.request(
18776
+ `/api/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domainId)}/verify`,
18777
+ {
18778
+ method: "POST",
18779
+ body: { auto: options.auto === true },
18780
+ context: "Verify domain"
18781
+ }
18782
+ );
18783
+ return body.domain;
18784
+ }
18785
+ /** Verifies by the handle users actually have: the hostname. */
18786
+ async verifyByHostname(projectId, hostname3, options = {}) {
18787
+ const match = await this.findByHostname(projectId, hostname3);
18788
+ return this.verify(projectId, match.id, options);
18789
+ }
16799
18790
  async removeById(projectId, domainId) {
16800
18791
  await this.client.request(
16801
18792
  `/api/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domainId)}`,
@@ -16804,8 +18795,19 @@ var DomainsApi = class {
16804
18795
  }
16805
18796
  /** Removes a domain by hostname. */
16806
18797
  async remove(projectId, hostname3) {
18798
+ const match = await this.findByHostname(projectId, hostname3);
18799
+ await this.removeById(projectId, match.id);
18800
+ }
18801
+ /**
18802
+ * Domain ids exist only in our database, so every hostname-shaped call has
18803
+ * to translate through the list first.
18804
+ */
18805
+ async findByHostname(projectId, hostname3) {
18806
+ const wanted = normalizeHostname(hostname3);
16807
18807
  const domains = await this.list(projectId);
16808
- const match = domains.find((domain2) => domain2.hostname === hostname3);
18808
+ const match = domains.find(
18809
+ (domain2) => normalizeHostname(domain2.hostname) === wanted
18810
+ );
16809
18811
  if (!match) {
16810
18812
  throw new NaroError(
16811
18813
  "DOMAIN_NOT_FOUND",
@@ -16813,7 +18815,7 @@ var DomainsApi = class {
16813
18815
  { details: { hostname: hostname3 } }
16814
18816
  );
16815
18817
  }
16816
- await this.removeById(projectId, match.id);
18818
+ return match;
16817
18819
  }
16818
18820
  };
16819
18821
  var LogsApi = class {
@@ -16831,10 +18833,234 @@ var LogsApi = class {
16831
18833
  return body.entries;
16832
18834
  }
16833
18835
  };
18836
+ var EXPORT_FIRST_POLL_MS = 1e3;
18837
+ var EXPORT_MAX_POLL_MS = 5e3;
18838
+ var EXPORT_TIMEOUT_MS = 15 * 6e4;
18839
+ var DbApi = class {
18840
+ constructor(client) {
18841
+ this.client = client;
18842
+ }
18843
+ path(projectId, suffix = "") {
18844
+ return `/api/projects/${encodeURIComponent(projectId)}/database${suffix}`;
18845
+ }
18846
+ async info(projectId) {
18847
+ const body = await this.client.request(
18848
+ this.path(projectId),
18849
+ { context: "Read database" }
18850
+ );
18851
+ return body.database;
18852
+ }
18853
+ /** Idempotent: turns the provider on and provisions the D1 when there is none. */
18854
+ async enable(projectId) {
18855
+ const body = await this.client.request(
18856
+ this.path(projectId),
18857
+ { method: "POST", body: {}, context: "Enable database" }
18858
+ );
18859
+ return body.database;
18860
+ }
18861
+ async query(projectId, input) {
18862
+ const body = await this.client.request(
18863
+ this.path(projectId, "/query"),
18864
+ {
18865
+ method: "POST",
18866
+ // Only the keys given: the server validates each one it sees.
18867
+ body: {
18868
+ sql: input.sql,
18869
+ ...input.params !== void 0 ? { params: input.params } : {},
18870
+ ...input.readOnly !== void 0 ? { readOnly: input.readOnly } : {}
18871
+ },
18872
+ context: "Run SQL"
18873
+ }
18874
+ );
18875
+ return body.result;
18876
+ }
18877
+ async tables(projectId) {
18878
+ const body = await this.client.request(
18879
+ this.path(projectId, "/tables"),
18880
+ { context: "List tables" }
18881
+ );
18882
+ return body.tables;
18883
+ }
18884
+ /** The `d1_migrations` ledger, oldest first; [] when nothing was ever applied. */
18885
+ async migrations(projectId) {
18886
+ const body = await this.client.request(
18887
+ this.path(projectId, "/migrations"),
18888
+ { context: "List migrations" }
18889
+ );
18890
+ return body.migrations;
18891
+ }
18892
+ /**
18893
+ * Runs one migration file and records it. All-or-nothing: when a statement
18894
+ * fails, D1 rolls the whole file back and nothing is recorded. The
18895
+ * request makes three round trips against the 45s client timeout, so a
18896
+ * `TIMEOUT` here leaves the outcome unknown — check `migrations()` before
18897
+ * retrying rather than re-running the file blind.
18898
+ */
18899
+ async applyMigration(projectId, input) {
18900
+ return this.client.request(
18901
+ this.path(projectId, "/migrations"),
18902
+ {
18903
+ method: "POST",
18904
+ body: { name: input.name, sql: input.sql },
18905
+ context: "Apply migration"
18906
+ }
18907
+ );
18908
+ }
18909
+ /**
18910
+ * The current Time Travel bookmark, or the one `timestamp` (RFC 3339 with a
18911
+ * timezone, or Unix seconds) names. The server checks the range - the last
18912
+ * 30 days, not before the database existed - before asking Cloudflare.
18913
+ */
18914
+ async bookmark(projectId, options = {}) {
18915
+ const query = options.timestamp === void 0 ? "" : `?timestamp=${encodeURIComponent(options.timestamp)}`;
18916
+ const body = await this.client.request(
18917
+ this.path(projectId, `/bookmark${query}`),
18918
+ { context: "Read bookmark" }
18919
+ );
18920
+ return { bookmark: body.bookmark, timestamp: body.timestamp };
18921
+ }
18922
+ /**
18923
+ * Rolls the database back to a bookmark or a timestamp - rows, schema and
18924
+ * the migration ledger alike - and cancels queries in flight. Restoring to
18925
+ * `previousBookmark` undoes it.
18926
+ */
18927
+ async restore(projectId, target) {
18928
+ const given = target;
18929
+ const body = await this.client.request(
18930
+ this.path(projectId, "/restore"),
18931
+ {
18932
+ method: "POST",
18933
+ body: {
18934
+ ...given.bookmark !== void 0 ? { bookmark: given.bookmark } : {},
18935
+ ...given.timestamp !== void 0 ? { timestamp: given.timestamp } : {}
18936
+ },
18937
+ context: "Restore database"
18938
+ }
18939
+ );
18940
+ return { bookmark: body.bookmark, previousBookmark: body.previousBookmark };
18941
+ }
18942
+ /**
18943
+ * One step of an export: starts it, or - with the first step's
18944
+ * `atBookmark` as `currentBookmark` - polls it. See `export` for the loop.
18945
+ */
18946
+ async exportStep(projectId, options = {}) {
18947
+ const body = await this.client.request(
18948
+ this.path(projectId, "/export"),
18949
+ {
18950
+ method: "POST",
18951
+ // Only the keys given: the server validates each one it sees.
18952
+ body: {
18953
+ ...options.schemaOnly !== void 0 ? { schemaOnly: options.schemaOnly } : {},
18954
+ ...options.dataOnly !== void 0 ? { dataOnly: options.dataOnly } : {},
18955
+ ...options.tables !== void 0 ? { tables: options.tables } : {},
18956
+ ...options.currentBookmark !== void 0 ? { currentBookmark: options.currentBookmark } : {}
18957
+ },
18958
+ context: "Export database"
18959
+ }
18960
+ );
18961
+ return body.export;
18962
+ }
18963
+ /**
18964
+ * Exports the database and polls until the dump is ready. The server takes
18965
+ * one Cloudflare step per call - a big database runs for minutes, longer
18966
+ * than a Worker should wait - so the loop is here: 1 s, then 1.5x per poll
18967
+ * up to 5 s. Cloudflare holds every other query to the database while the
18968
+ * export runs.
18969
+ *
18970
+ * On TIMEOUT the export is still running at Cloudflare; nothing was
18971
+ * cancelled.
18972
+ */
18973
+ async export(projectId, options = {}, wait = {}) {
18974
+ const timeoutMs = wait.timeoutMs ?? EXPORT_TIMEOUT_MS;
18975
+ const sleep2 = wait.sleep ?? ((ms) => new Promise((resolve10) => setTimeout(resolve10, ms)));
18976
+ const given = wait.pollIntervalMs;
18977
+ const firstPollMs = given !== void 0 && Number.isFinite(given) && given > 0 ? given : EXPORT_FIRST_POLL_MS;
18978
+ const maxPollMs = Math.max(EXPORT_MAX_POLL_MS, firstPollMs);
18979
+ const startedAt = Date.now();
18980
+ let step = await this.exportStep(projectId, options);
18981
+ wait.onProgress?.(step);
18982
+ const currentBookmark = step.atBookmark;
18983
+ const polled = {
18984
+ ...options,
18985
+ ...step.tables !== void 0 ? { tables: step.tables } : {}
18986
+ };
18987
+ let intervalMs = firstPollMs;
18988
+ while (step.status !== "complete") {
18989
+ if (Date.now() - startedAt >= timeoutMs) {
18990
+ throw new NaroError(
18991
+ "TIMEOUT",
18992
+ `The export was still running after ${String(Math.round(timeoutMs / 6e4))} minutes. Cloudflare keeps it going; run naro db dump again later.`,
18993
+ { details: { atBookmark: currentBookmark } }
18994
+ );
18995
+ }
18996
+ await sleep2(intervalMs);
18997
+ intervalMs = Math.min(intervalMs * 1.5, maxPollMs);
18998
+ step = await this.exportStep(projectId, { ...polled, currentBookmark });
18999
+ wait.onProgress?.(step);
19000
+ }
19001
+ return step;
19002
+ }
19003
+ /**
19004
+ * Records the remote schema as the already-applied migration `name` and
19005
+ * returns its SQL - a baseline for a database that has none. Refused with
19006
+ * DATABASE_HAS_MIGRATIONS when the ledger has rows, DATABASE_EMPTY when
19007
+ * there is no schema. The caller writes the file.
19008
+ */
19009
+ async pull(projectId, input) {
19010
+ return this.client.request(this.path(projectId, "/pull"), {
19011
+ method: "POST",
19012
+ body: { name: input.name },
19013
+ context: "Pull schema"
19014
+ });
19015
+ }
19016
+ };
19017
+
19018
+ // ../../packages/sdk/src/database.ts
19019
+ var BOOKMARK_RE = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{32}$/;
19020
+ var BOOKMARK_FORMAT_MESSAGE = "bookmark must look like 00000001-00000008-000050ed-272153b8520cfb93b49834d93a1a1ab3 (four hyphen-separated hex groups, 59 characters).";
19021
+ function isBookmark(value) {
19022
+ return typeof value === "string" && BOOKMARK_RE.test(value);
19023
+ }
19024
+ var RESTORE_OUTCOME_UNKNOWN_CODES = /* @__PURE__ */ new Set([
19025
+ "DATABASE_RESTORE_FAILED",
19026
+ "DATABASE_UNAVAILABLE",
19027
+ "TIMEOUT",
19028
+ "NETWORK_ERROR"
19029
+ ]);
19030
+ var MAX_CELL_CHARS = 60;
19031
+ function formatCell(value) {
19032
+ if (value === null || value === void 0) return "NULL";
19033
+ const text = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
19034
+ const oneLine = text.replace(/\s+/g, " ");
19035
+ return oneLine.length > MAX_CELL_CHARS ? `${oneLine.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine;
19036
+ }
19037
+ function renderRows(statement) {
19038
+ if (statement.columns.length === 0) return [];
19039
+ const cells = statement.rows.map(
19040
+ (row) => statement.columns.map((_, index) => formatCell(row[index]))
19041
+ );
19042
+ const widths = statement.columns.map(
19043
+ (column, index) => Math.max(column.length, ...cells.map((row) => row[index]?.length ?? 0))
19044
+ );
19045
+ const line = (values) => values.map((value, index) => value.padEnd(widths[index] ?? 0)).join(" ").trimEnd();
19046
+ return [
19047
+ line(statement.columns),
19048
+ widths.map((width) => "-".repeat(width)).join(" "),
19049
+ ...cells.map(line)
19050
+ ];
19051
+ }
19052
+ function formatMs(ms) {
19053
+ return ms >= 100 ? String(Math.round(ms)) : ms.toFixed(1);
19054
+ }
19055
+ function summarizeStatement(statement) {
19056
+ const rows = statement.rows.length;
19057
+ const { meta: meta3 } = statement;
19058
+ return `\u21B3 ${String(rows)} row${rows === 1 ? "" : "s"} \xB7 ${String(meta3.rowsRead)} read \xB7 ${String(meta3.rowsWritten)} written \xB7 ${formatMs(meta3.durationMs)} ms`;
19059
+ }
16834
19060
 
16835
19061
  // ../../packages/sdk/src/dotenv.ts
16836
19062
  import { existsSync, readFileSync as readFileSync2 } from "fs";
16837
- import { join as join10 } from "path";
19063
+ import { join as join18 } from "path";
16838
19064
  function parseDotenv(text) {
16839
19065
  const out = {};
16840
19066
  for (const line of text.split(/\r?\n/)) {
@@ -16852,7 +19078,7 @@ function parseDotenv(text) {
16852
19078
  }
16853
19079
  function readProjectEnv(dir) {
16854
19080
  for (const name of [".env.local", ".env.production", ".env"]) {
16855
- const path = join10(dir, name);
19081
+ const path = join18(dir, name);
16856
19082
  if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
16857
19083
  }
16858
19084
  return {};
@@ -16867,8 +19093,8 @@ function serializeDotenv(vars) {
16867
19093
 
16868
19094
  // ../../packages/sdk/src/env-scan.ts
16869
19095
  import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
16870
- import { join as join11 } from "path";
16871
- var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
19096
+ import { join as join19 } from "path";
19097
+ var SOURCE_EXTENSIONS3 = /* @__PURE__ */ new Set([
16872
19098
  ".ts",
16873
19099
  ".tsx",
16874
19100
  ".js",
@@ -16922,7 +19148,7 @@ function collect(dir, out) {
16922
19148
  for (const entry of entries) {
16923
19149
  if (out.length >= MAX_FILES) return;
16924
19150
  if (entry.isSymbolicLink()) continue;
16925
- const path = join11(dir, entry.name);
19151
+ const path = join19(dir, entry.name);
16926
19152
  if (entry.isDirectory()) {
16927
19153
  if (entry.name.startsWith(".") || SKIP_DIRECTORIES.has(entry.name))
16928
19154
  continue;
@@ -16931,7 +19157,7 @@ function collect(dir, out) {
16931
19157
  }
16932
19158
  if (!entry.isFile()) continue;
16933
19159
  const dot = entry.name.lastIndexOf(".");
16934
- if (dot < 0 || !SOURCE_EXTENSIONS.has(entry.name.slice(dot))) continue;
19160
+ if (dot < 0 || !SOURCE_EXTENSIONS3.has(entry.name.slice(dot))) continue;
16935
19161
  out.push(path);
16936
19162
  }
16937
19163
  }
@@ -16954,7 +19180,7 @@ function scanEnvReferences(dir) {
16954
19180
  }
16955
19181
  }
16956
19182
  for (const name of EXAMPLE_FILES) {
16957
- const path = join11(dir, name);
19183
+ const path = join19(dir, name);
16958
19184
  if (!existsSync2(path)) continue;
16959
19185
  let parsed;
16960
19186
  try {
@@ -16972,11 +19198,11 @@ function scanEnvReferences(dir) {
16972
19198
 
16973
19199
  // ../../packages/sdk/src/framework.ts
16974
19200
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
16975
- import { join as join12 } from "path";
19201
+ import { join as join20 } from "path";
16976
19202
  function readPackageJson2(dir) {
16977
19203
  try {
16978
19204
  return JSON.parse(
16979
- readFileSync4(join12(dir, "package.json"), "utf8")
19205
+ readFileSync4(join20(dir, "package.json"), "utf8")
16980
19206
  );
16981
19207
  } catch {
16982
19208
  return null;
@@ -16987,7 +19213,7 @@ function hasDependency(pkg, name) {
16987
19213
  }
16988
19214
  function hasConfig(dir, base2, evidence) {
16989
19215
  for (const ext of ["js", "mjs", "cjs", "ts", "mts"]) {
16990
- if (existsSync3(join12(dir, `${base2}.${ext}`))) {
19216
+ if (existsSync3(join20(dir, `${base2}.${ext}`))) {
16991
19217
  evidence.push(`${base2}.${ext}`);
16992
19218
  return true;
16993
19219
  }
@@ -16999,8 +19225,8 @@ function detectFramework2(dir) {
16999
19225
  const pkg = readPackageJson2(dir);
17000
19226
  if (hasDependency(pkg, "next") || hasConfig(dir, "next.config", evidence)) {
17001
19227
  if (hasDependency(pkg, "next")) evidence.push("package.json: next");
17002
- if (existsSync3(join12(dir, "app"))) evidence.push("app/");
17003
- if (existsSync3(join12(dir, "pages"))) evidence.push("pages/");
19228
+ if (existsSync3(join20(dir, "app"))) evidence.push("app/");
19229
+ if (existsSync3(join20(dir, "pages"))) evidence.push("pages/");
17004
19230
  return {
17005
19231
  framework: "nextjs",
17006
19232
  name: "Next.js",
@@ -17054,7 +19280,7 @@ function detectFramework2(dir) {
17054
19280
  deployable: true
17055
19281
  };
17056
19282
  }
17057
- if (!pkg && existsSync3(join12(dir, "index.html"))) {
19283
+ if (!pkg && existsSync3(join20(dir, "index.html"))) {
17058
19284
  evidence.push("index.html (no package.json)");
17059
19285
  return {
17060
19286
  framework: "static",
@@ -17075,6 +19301,71 @@ function detectFramework2(dir) {
17075
19301
  };
17076
19302
  }
17077
19303
 
19304
+ // ../../packages/sdk/src/generate-types.ts
19305
+ var HEADER = [
19306
+ "// Generated by `naro gen types`. Do not edit - re-run the command instead.",
19307
+ "// `D1Database` comes from @cloudflare/workers-types."
19308
+ ];
19309
+ function valueType(declared) {
19310
+ const type = declared.toUpperCase();
19311
+ if (type.trim() === "") return "number | string | ArrayBuffer";
19312
+ if (type.includes("INT")) return "number";
19313
+ if (type.includes("CHAR") || type.includes("CLOB") || type.includes("TEXT"))
19314
+ return "string";
19315
+ if (type.includes("BLOB")) return "ArrayBuffer";
19316
+ if (type.includes("REAL") || type.includes("FLOA") || type.includes("DOUB"))
19317
+ return "number";
19318
+ return "number | string";
19319
+ }
19320
+ function interfaceBase(tableName) {
19321
+ const pascal = tableName.split(/[^A-Za-z0-9$]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("").replace(/[^A-Za-z0-9_$]/g, "");
19322
+ return pascal === "" || /^[0-9]/.test(pascal) ? `_${pascal}` : pascal;
19323
+ }
19324
+ function rowidAlias(table) {
19325
+ const keys = table.columns.filter((column) => column.primaryKey);
19326
+ const only = keys.length === 1 ? keys[0] : void 0;
19327
+ return only && only.type.trim().toUpperCase() === "INTEGER" ? only.name : null;
19328
+ }
19329
+ var UNICODE_ESCAPE = `${String.fromCharCode(92)}u`;
19330
+ function keyLiteral(name) {
19331
+ return JSON.stringify(name).replace(
19332
+ /[^ -~]/g,
19333
+ (unit) => `${UNICODE_ESCAPE}${unit.charCodeAt(0).toString(16).padStart(4, "0")}`
19334
+ );
19335
+ }
19336
+ function property(column, alias) {
19337
+ const nullable2 = !column.notNull && column.name !== alias;
19338
+ return ` ${keyLiteral(column.name)}: ${valueType(column.type)}${nullable2 ? " | null" : ""};`;
19339
+ }
19340
+ function block(name, lines) {
19341
+ return lines.length === 0 ? [`export interface ${name} {}`] : [`export interface ${name} {`, ...lines, "}"];
19342
+ }
19343
+ function generateTypes(tables) {
19344
+ const used = /* @__PURE__ */ new Set();
19345
+ const out = [...HEADER];
19346
+ const map2 = [];
19347
+ for (const table of tables) {
19348
+ const base2 = `${interfaceBase(table.name)}Row`;
19349
+ let name = base2;
19350
+ for (let suffix = 2; used.has(name); suffix += 1)
19351
+ name = `${base2}_${String(suffix)}`;
19352
+ used.add(name);
19353
+ const alias = rowidAlias(table);
19354
+ out.push(
19355
+ "",
19356
+ ...block(
19357
+ name,
19358
+ table.columns.map((column) => property(column, alias))
19359
+ )
19360
+ );
19361
+ map2.push(` ${keyLiteral(table.name)}: ${name};`);
19362
+ }
19363
+ out.push("", ...block("Database", map2));
19364
+ out.push("", ...block("Env", [" DB: D1Database;"]));
19365
+ return `${out.join("\n")}
19366
+ `;
19367
+ }
19368
+
17078
19369
  // ../../packages/sdk/src/link.ts
17079
19370
  import {
17080
19371
  existsSync as existsSync4,
@@ -17083,12 +19374,12 @@ import {
17083
19374
  rmSync as rmSync2,
17084
19375
  writeFileSync as writeFileSync2
17085
19376
  } from "fs";
17086
- import { join as join13 } from "path";
19377
+ import { join as join21 } from "path";
17087
19378
  function linkDir(dir) {
17088
- return join13(dir, ".naro");
19379
+ return join21(dir, ".naro");
17089
19380
  }
17090
19381
  function linkFile(dir) {
17091
- return join13(linkDir(dir), "project.json");
19382
+ return join21(linkDir(dir), "project.json");
17092
19383
  }
17093
19384
  function readProjectLink(dir) {
17094
19385
  try {
@@ -17110,7 +19401,7 @@ function writeProjectLink(dir, link) {
17110
19401
  writeFileSync2(linkFile(dir), `${JSON.stringify(link, null, 2)}
17111
19402
  `);
17112
19403
  writeFileSync2(
17113
- join13(linkDir(dir), "README.txt"),
19404
+ join21(linkDir(dir), "README.txt"),
17114
19405
  "This directory links the project to Naro and should not be committed.\n"
17115
19406
  );
17116
19407
  }
@@ -17120,9 +19411,9 @@ function removeProjectLink(dir) {
17120
19411
  return true;
17121
19412
  }
17122
19413
  function ensureGitignoreHasNaro(dir) {
17123
- const gitignorePath = join13(dir, ".gitignore");
19414
+ const gitignorePath = join21(dir, ".gitignore");
17124
19415
  const hasGitignore = existsSync4(gitignorePath);
17125
- if (!hasGitignore && !existsSync4(join13(dir, ".git"))) return false;
19416
+ if (!hasGitignore && !existsSync4(join21(dir, ".git"))) return false;
17126
19417
  const current = hasGitignore ? readFileSync5(gitignorePath, "utf8") : "";
17127
19418
  const covered = current.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".naro" || line === ".naro/" || line === "/.naro");
17128
19419
  if (covered) return false;
@@ -17132,9 +19423,94 @@ function ensureGitignoreHasNaro(dir) {
17132
19423
  return true;
17133
19424
  }
17134
19425
 
19426
+ // ../../packages/sdk/src/migrations.ts
19427
+ import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
19428
+ import { resolve as resolve8 } from "path";
19429
+ function resolveMigrationsDir(input) {
19430
+ if (input.dir) return resolve8(input.baseDir, input.dir);
19431
+ return resolve8(input.baseDir, input.rootDirectory ?? "", "migrations");
19432
+ }
19433
+ function normalizeMigrationName(name) {
19434
+ return name.trim().toLowerCase().replace(/\.sql$/, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
19435
+ }
19436
+ function leadingNumber(name) {
19437
+ const match = /^(\d+)/.exec(name);
19438
+ return match ? Number(match[1]) : null;
19439
+ }
19440
+ function sortMigrationNames(names) {
19441
+ return names.toSorted((a, b) => {
19442
+ const left = leadingNumber(a);
19443
+ const right = leadingNumber(b);
19444
+ if (left !== null && right !== null && left !== right) return left - right;
19445
+ if (left === null && right !== null) return 1;
19446
+ if (left !== null && right === null) return -1;
19447
+ return a < b ? -1 : a > b ? 1 : 0;
19448
+ });
19449
+ }
19450
+ function highestMigrationNumber(names) {
19451
+ return names.reduce(
19452
+ (max, name) => Math.max(max, leadingNumber(name) ?? 0),
19453
+ 0
19454
+ );
19455
+ }
19456
+ function listMigrationFiles(dir) {
19457
+ if (!existsSync5(dir)) return [];
19458
+ return sortMigrationNames(
19459
+ readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name)
19460
+ );
19461
+ }
19462
+ function nextMigrationFileName(existing, name) {
19463
+ const normalized = normalizeMigrationName(name);
19464
+ if (normalized === "")
19465
+ throw new NaroError(
19466
+ "VALIDATION_FAILED",
19467
+ "Migration name must contain letters or digits."
19468
+ );
19469
+ const number4 = String(highestMigrationNumber(existing) + 1).padStart(4, "0");
19470
+ return `${number4}_${normalized}.sql`;
19471
+ }
19472
+ function compareMigrations(local, applied) {
19473
+ const ledger = new Map(applied.map((row) => [row.name, row]));
19474
+ const highestApplied = highestMigrationNumber(applied.map((row) => row.name));
19475
+ const rows = [];
19476
+ for (const name of local) {
19477
+ const exact = ledger.get(name);
19478
+ if (exact) {
19479
+ ledger.delete(name);
19480
+ rows.push({ name, status: "applied", appliedAt: exact.appliedAt });
19481
+ continue;
19482
+ }
19483
+ const bare = ledger.get(name.replace(/\.sql$/, ""));
19484
+ if (bare) {
19485
+ ledger.delete(bare.name);
19486
+ rows.push({
19487
+ name,
19488
+ status: "applied",
19489
+ appliedAt: bare.appliedAt,
19490
+ warning: "ledger-name-missing-extension"
19491
+ });
19492
+ continue;
19493
+ }
19494
+ const number4 = leadingNumber(name);
19495
+ rows.push({
19496
+ name,
19497
+ status: "pending",
19498
+ ...number4 !== null && number4 < highestApplied ? { warning: "out-of-order" } : {}
19499
+ });
19500
+ }
19501
+ for (const row of ledger.values())
19502
+ rows.push({
19503
+ name: row.name,
19504
+ status: "remote-only",
19505
+ appliedAt: row.appliedAt
19506
+ });
19507
+ const order = sortMigrationNames(rows.map((row) => row.name));
19508
+ return rows.toSorted((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
19509
+ }
19510
+
17135
19511
  // src/env-consent.ts
17136
- import { existsSync as existsSync5 } from "fs";
17137
- import { join as join14 } from "path";
19512
+ import { existsSync as existsSync6 } from "fs";
19513
+ import { join as join22 } from "path";
17138
19514
  var DOTENV_FILES = [".env.local", ".env.production", ".env"];
17139
19515
  var PUBLIC_PREFIXES = ["NEXT_PUBLIC_", "VITE_", "REACT_APP_", "PUBLIC_"];
17140
19516
  function maskEnvValue(key, value) {
@@ -17143,7 +19519,7 @@ function maskEnvValue(key, value) {
17143
19519
  return value.length <= 8 ? "****" : `${value.slice(0, 4)}****`;
17144
19520
  }
17145
19521
  function findDotenvFile(dir) {
17146
- for (const name of DOTENV_FILES) if (existsSync5(join14(dir, name))) return name;
19522
+ for (const name of DOTENV_FILES) if (existsSync6(join22(dir, name))) return name;
17147
19523
  return null;
17148
19524
  }
17149
19525
  function envAddGuidance(keys) {
@@ -17200,10 +19576,10 @@ var SUPPORTED_REMOTE = [
17200
19576
  /^ssh:\/\/[^\s]+$/,
17201
19577
  /^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
17202
19578
  ];
17203
- var defaultExec = (args, cwd) => new Promise((resolve5, reject) => {
19579
+ var defaultExec = (args, cwd) => new Promise((resolve10, reject) => {
17204
19580
  execFile("git", args, { cwd }, (error51, stdout) => {
17205
19581
  if (error51) reject(error51);
17206
- else resolve5(stdout);
19582
+ else resolve10(stdout);
17207
19583
  });
17208
19584
  });
17209
19585
  async function tryExec(exec, args, cwd) {
@@ -17335,8 +19711,8 @@ function parseDotenv2(text) {
17335
19711
  function readProjectEnv2(dir) {
17336
19712
  return readProjectEnv(dir);
17337
19713
  }
17338
- function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
17339
- const post = (body) => fetchImpl(`${cp2}/api/internal/deployments/${deploymentId}/events`, {
19714
+ function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUrl) {
19715
+ const post = (body) => fetchImpl(`${cp3}/api/internal/deployments/${deploymentId}/events`, {
17340
19716
  method: "POST",
17341
19717
  headers: {
17342
19718
  "Content-Type": "application/json",
@@ -17353,18 +19729,48 @@ function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
17353
19729
  } catch (error51) {
17354
19730
  lastError = error51;
17355
19731
  await new Promise(
17356
- (resolve5) => setTimeout(resolve5, 500 * (attempt + 1))
19732
+ (resolve10) => setTimeout(resolve10, 500 * (attempt + 1))
17357
19733
  );
17358
19734
  }
17359
19735
  }
17360
19736
  throw lastError;
17361
19737
  };
19738
+ const send = createLogSender({
19739
+ ...logIngestUrl ? { ingestUrl: logIngestUrl } : {},
19740
+ toIngest: logIngestUrl ? (logs) => fetchImpl(`${logIngestUrl}/deployments/${deploymentId}/logs`, {
19741
+ method: "POST",
19742
+ headers: {
19743
+ "Content-Type": "application/json",
19744
+ Authorization: `Bearer ${token}`
19745
+ },
19746
+ body: JSON.stringify({ logs })
19747
+ }) : null,
19748
+ toControlPlane: (logs) => post({ type: "logs", logs }),
19749
+ // The CLI swallows log failures so a deploy never dies over a log line.
19750
+ warn: (message) => post({
19751
+ type: "log",
19752
+ log: { level: "WARN", stage: "BUILDING", message }
19753
+ }).catch(() => void 0)
19754
+ });
19755
+ const batcher = createLogBatcher({
19756
+ limit: DEPLOYMENT_LOG_BATCH_LIMIT,
19757
+ intervalMs: DEPLOYMENT_LOG_FLUSH_MS,
19758
+ send
19759
+ });
19760
+ const flushLogs = () => batcher.flush().catch(() => {
19761
+ });
17362
19762
  return {
17363
- onStatus: (status) => postStatus({ type: "status", status }),
17364
- onLog: (log2) => post({ type: "log", log: log2 }).catch(() => {
19763
+ onStatus: async (status) => {
19764
+ await flushLogs();
19765
+ await postStatus({ type: "status", status });
19766
+ },
19767
+ onLog: (log2) => batcher.add(log2).catch(() => {
17365
19768
  }),
17366
- onMetadata: (metadata) => post({ type: "metadata", metadata }).catch(() => {
17367
- })
19769
+ onMetadata: async (metadata) => {
19770
+ await flushLogs();
19771
+ await post({ type: "metadata", metadata }).catch(() => {
19772
+ });
19773
+ }
17368
19774
  };
17369
19775
  }
17370
19776
  function slugify2(value) {
@@ -17374,20 +19780,20 @@ function slugify2(value) {
17374
19780
  async function projectName(sourceDir) {
17375
19781
  try {
17376
19782
  const pkg = JSON.parse(
17377
- await readFile7(join15(sourceDir, "package.json"), "utf8")
19783
+ await readFile13(join23(sourceDir, "package.json"), "utf8")
17378
19784
  );
17379
19785
  if (pkg.name) return pkg.name;
17380
19786
  } catch {
17381
19787
  }
17382
- return basename(sourceDir);
19788
+ return basename3(sourceDir);
17383
19789
  }
17384
19790
  var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo|\.naro)([\\/]|$)/;
17385
19791
  function resolveBin(name) {
17386
- let dir = dirname(fileURLToPath(import.meta.url));
19792
+ let dir = dirname4(fileURLToPath(import.meta.url));
17387
19793
  for (; ; ) {
17388
- const candidate = join15(dir, "node_modules", ".bin", name);
17389
- if (existsSync6(candidate)) return candidate;
17390
- const parent = dirname(dir);
19794
+ const candidate = join23(dir, "node_modules", ".bin", name);
19795
+ if (existsSync7(candidate)) return candidate;
19796
+ const parent = dirname4(dir);
17391
19797
  if (parent === dir) return name;
17392
19798
  dir = parent;
17393
19799
  }
@@ -17405,20 +19811,25 @@ async function detectPackageManagerLenient(root) {
17405
19811
  "--no-fund",
17406
19812
  "--legacy-peer-deps"
17407
19813
  ],
19814
+ addArgs: (specs) => ["npm", "install", "--no-save", ...specs],
17408
19815
  cacheArgs: (dir) => ["--cache", dir]
17409
19816
  };
17410
19817
  }
17411
19818
  }
17412
19819
  function localDependencies(sourceDir) {
17413
19820
  return {
17414
- createWorkspace: () => mkdtemp2(join15(tmpdir2(), "naro-build-")),
17415
- removeWorkspace: (path) => rm2(path, { recursive: true, force: true }),
19821
+ createWorkspace: () => mkdtemp2(join23(tmpdir2(), "naro-build-")),
19822
+ removeWorkspace: (path) => rm3(path, { recursive: true, force: true }),
17416
19823
  async runCommand(spec, observer) {
17417
19824
  if (spec.command === "git" && spec.args[0] === "clone") {
17418
19825
  const checkout = spec.args.at(-1);
17419
19826
  if (!checkout) throw new Error("Missing checkout path.");
17420
- await cp(sourceDir, checkout, {
19827
+ await cp2(sourceDir, checkout, {
17421
19828
  recursive: true,
19829
+ // Relative symlinks must stay relative: resolved, they would point
19830
+ // back into the developer's real tree, and a strategy's temporary
19831
+ // rewrite (proxy -> middleware, vinext toolchain) would land there.
19832
+ verbatimSymlinks: true,
17422
19833
  filter: (src) => !COPY_SKIP.test(src)
17423
19834
  });
17424
19835
  return { stdout: [], stderr: [] };
@@ -17439,13 +19850,16 @@ function localDependencies(sourceDir) {
17439
19850
  createPlatformBuildConfig,
17440
19851
  readOpenNextArtifact,
17441
19852
  openNextCommand: resolveBin("opennextjs-cloudflare"),
17442
- wranglerCommand: resolveBin("wrangler")
19853
+ wranglerCommand: resolveBin("wrangler"),
19854
+ // `vinext check` runs on the bare checkout, before the toolchain is added
19855
+ // to the user's tree, so this binary has to come from the CLI's own deps.
19856
+ vinextCommand: resolveBin("vinext")
17443
19857
  };
17444
19858
  }
17445
19859
  async function deployDirectory(sourceDir, options, log2) {
17446
19860
  const cpUrl = controlPlaneUrl();
17447
19861
  const production = options.production ?? false;
17448
- const appRoot = options.rootDirectory ? resolve4(sourceDir, options.rootDirectory) : sourceDir;
19862
+ const appRoot = options.rootDirectory ? resolve9(sourceDir, options.rootDirectory) : sourceDir;
17449
19863
  const framework = detectFramework2(appRoot);
17450
19864
  if (!framework.deployable && !options.force) {
17451
19865
  throw new NaroError(
@@ -17518,7 +19932,8 @@ async function deployDirectory(sourceDir, options, log2) {
17518
19932
  cpUrl,
17519
19933
  ctx.deploymentId,
17520
19934
  ctx.callbackToken,
17521
- options.fetch
19935
+ options.fetch,
19936
+ ctx.logIngestUrl
17522
19937
  );
17523
19938
  let artifact;
17524
19939
  try {
@@ -17529,7 +19944,9 @@ async function deployDirectory(sourceDir, options, log2) {
17529
19944
  repositoryUrl: sourceDir,
17530
19945
  branch: ctx.branch,
17531
19946
  rootDirectory: options.rootDirectory ?? null,
17532
- env: { ...ctx.env, ...envToSend }
19947
+ env: { ...ctx.env, ...envToSend },
19948
+ strategy: ctx.strategy ?? null,
19949
+ ...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
17533
19950
  },
17534
19951
  observer,
17535
19952
  createCacheTransport({
@@ -17609,11 +20026,21 @@ export {
17609
20026
  openBrowser,
17610
20027
  isTerminalStatus,
17611
20028
  NaroClient,
20029
+ BOOKMARK_FORMAT_MESSAGE,
20030
+ isBookmark,
20031
+ RESTORE_OUTCOME_UNKNOWN_CODES,
20032
+ renderRows,
20033
+ summarizeStatement,
17612
20034
  serializeDotenv,
20035
+ generateTypes,
17613
20036
  readProjectLink,
17614
20037
  writeProjectLink,
17615
20038
  removeProjectLink,
17616
20039
  ensureGitignoreHasNaro,
20040
+ resolveMigrationsDir,
20041
+ listMigrationFiles,
20042
+ nextMigrationFileName,
20043
+ compareMigrations,
17617
20044
  promptForMissingEnv,
17618
20045
  detectGitInfo,
17619
20046
  parseDotenv2 as parseDotenv,