naro-cli 0.6.1 → 0.7.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,245 +15338,2035 @@ 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 }
15442
15821
  );
15443
- this.name = "CommandExecutionError";
15444
- this.exitCode = options.exitCode;
15445
- this.timedOut = options.timedOut;
15822
+ this.name = "CommandExecutionError";
15823
+ this.exitCode = options.exitCode;
15824
+ this.timedOut = options.timedOut;
15825
+ }
15826
+ };
15827
+ var PASSTHROUGH_ENVIRONMENT = [
15828
+ "PATH",
15829
+ "HOME",
15830
+ "TMPDIR",
15831
+ "TMP",
15832
+ "TEMP",
15833
+ "LANG",
15834
+ "LC_ALL",
15835
+ "CI",
15836
+ "COREPACK_HOME",
15837
+ "XDG_CACHE_HOME"
15838
+ ];
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;
15446
16148
  }
15447
- };
15448
- var PASSTHROUGH_ENVIRONMENT = [
15449
- "PATH",
15450
- "HOME",
15451
- "TMPDIR",
15452
- "TMP",
15453
- "TEMP",
15454
- "LANG",
15455
- "LC_ALL",
15456
- "CI",
15457
- "COREPACK_HOME",
15458
- "XDG_CACHE_HOME"
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)
16180
+ };
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"
15459
16193
  ];
15460
- var MAX_CAPTURED_LINES = 2e3;
15461
- function commandEnvironment(overrides) {
15462
- const environment = {
15463
- NODE_ENV: process.env.NODE_ENV ?? "production"
16194
+ async function exists4(path) {
16195
+ try {
16196
+ await access6(path);
16197
+ return true;
16198
+ } catch {
16199
+ return false;
16200
+ }
16201
+ }
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
15464
16265
  };
15465
- for (const name of PASSTHROUGH_ENVIRONMENT) {
15466
- const value = process.env[name];
15467
- if (value !== void 0) environment[name] = value;
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
+ };
15468
16292
  }
15469
- return { ...environment, ...overrides };
16293
+ await observer.onLog({
16294
+ level: outcome.status === "success" ? "INFO" : "WARN",
16295
+ stage: "BUILDING",
16296
+ message: `${label(strategy)} ${describeOutcome(outcome)}.`
16297
+ });
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 {
16347
+ await observer.onLog({
16348
+ level: "INFO",
16349
+ stage: "BUILDING",
16350
+ message: `${label(strategy)} not a candidate: ${support.reason}`
16351
+ });
16352
+ }
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
+ };
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
+ });
16399
+ }
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("; ")}.`
16439
+ );
16440
+ }
16441
+ await observer.onLog({
16442
+ level: "INFO",
16443
+ stage: "BUILDING",
16444
+ message: `Selected ${label(best.strategy)}${best.outcome.variant ? ` (${best.outcome.variant})` : ""}.`
16445
+ });
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.
16514
+ {
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
+ }
16526
+ }
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
+ `;
16807
+ }
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
+ `
16820
+ );
16821
+ } else {
16822
+ next = `${next}onlyBuiltDependencies:
16823
+ ${listEntries}
16824
+ `;
16825
+ }
16826
+ if (next === source) return true;
16827
+ try {
16828
+ await writeFile5(path, next, "utf8");
16829
+ return true;
16830
+ } catch {
16831
+ return false;
16832
+ }
16833
+ }
16834
+
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");
15470
16851
  }
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"]
15479
- });
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);
15487
- await observer.onLog({
15488
- level,
15489
- stage: spec.stage,
15490
- message: redactLog(message)
15491
- });
15492
- }
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) {
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;
15500
16876
  try {
15501
- process.kill(-child.pid, signal);
15502
- return;
15503
- } catch {
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));
15504
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
+ };
15505
16939
  }
15506
- child.kill(signal);
15507
16940
  };
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 })
16941
+ }
16942
+
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
+ )
15520
16955
  );
15521
- });
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}`,
16956
+ return Boolean(manifest2.functions?.["/_middleware"]);
16957
+ } catch {
16958
+ return false;
16959
+ }
16960
+ }
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,
15529
16978
  {
15530
- exitCode: null,
15531
- timedOut,
15532
- cause: outcome.error
15533
- }
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
15534
16992
  );
15535
- }
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 }
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
15541
17019
  );
15542
- }
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 }
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"
15548
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 };
15549
17072
  }
15550
- return captured;
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).`
17116
+ );
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
+ };
17132
+ }
17133
+ };
15551
17134
  }
15552
17135
 
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;
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
+ });
15567
17153
  }
15568
-
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);
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
+ });
15578
17161
  }
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."
15584
- );
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."
15591
- );
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."
15604
- );
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
- });
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 = [];
17174
+ try {
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
+ }
15612
17188
  }
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);
15613
17193
  }
15614
- }
15615
- try {
15616
- await visit(outputRoot);
17194
+ return report;
15617
17195
  } 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 }
15623
- );
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,113 @@ var LogsApi = class {
16831
18833
  return body.entries;
16832
18834
  }
16833
18835
  };
18836
+ var DbApi = class {
18837
+ constructor(client) {
18838
+ this.client = client;
18839
+ }
18840
+ path(projectId, suffix = "") {
18841
+ return `/api/projects/${encodeURIComponent(projectId)}/database${suffix}`;
18842
+ }
18843
+ async info(projectId) {
18844
+ const body = await this.client.request(
18845
+ this.path(projectId),
18846
+ { context: "Read database" }
18847
+ );
18848
+ return body.database;
18849
+ }
18850
+ /** Idempotent: turns the provider on and provisions the D1 when there is none. */
18851
+ async enable(projectId) {
18852
+ const body = await this.client.request(
18853
+ this.path(projectId),
18854
+ { method: "POST", body: {}, context: "Enable database" }
18855
+ );
18856
+ return body.database;
18857
+ }
18858
+ async query(projectId, input) {
18859
+ const body = await this.client.request(
18860
+ this.path(projectId, "/query"),
18861
+ {
18862
+ method: "POST",
18863
+ // Only the keys given: the server validates each one it sees.
18864
+ body: {
18865
+ sql: input.sql,
18866
+ ...input.params !== void 0 ? { params: input.params } : {},
18867
+ ...input.readOnly !== void 0 ? { readOnly: input.readOnly } : {}
18868
+ },
18869
+ context: "Run SQL"
18870
+ }
18871
+ );
18872
+ return body.result;
18873
+ }
18874
+ async tables(projectId) {
18875
+ const body = await this.client.request(
18876
+ this.path(projectId, "/tables"),
18877
+ { context: "List tables" }
18878
+ );
18879
+ return body.tables;
18880
+ }
18881
+ /** The `d1_migrations` ledger, oldest first; [] when nothing was ever applied. */
18882
+ async migrations(projectId) {
18883
+ const body = await this.client.request(
18884
+ this.path(projectId, "/migrations"),
18885
+ { context: "List migrations" }
18886
+ );
18887
+ return body.migrations;
18888
+ }
18889
+ /**
18890
+ * Runs one migration file and records it. All-or-nothing: when a statement
18891
+ * fails, D1 rolls the whole file back and nothing is recorded. The
18892
+ * request makes three round trips against the 45s client timeout, so a
18893
+ * `TIMEOUT` here leaves the outcome unknown — check `migrations()` before
18894
+ * retrying rather than re-running the file blind.
18895
+ */
18896
+ async applyMigration(projectId, input) {
18897
+ return this.client.request(
18898
+ this.path(projectId, "/migrations"),
18899
+ {
18900
+ method: "POST",
18901
+ body: { name: input.name, sql: input.sql },
18902
+ context: "Apply migration"
18903
+ }
18904
+ );
18905
+ }
18906
+ };
18907
+
18908
+ // ../../packages/sdk/src/database.ts
18909
+ var MAX_CELL_CHARS = 60;
18910
+ function formatCell(value) {
18911
+ if (value === null || value === void 0) return "NULL";
18912
+ const text = typeof value === "string" ? value : typeof value === "object" ? JSON.stringify(value) : String(value);
18913
+ const oneLine = text.replace(/\s+/g, " ");
18914
+ return oneLine.length > MAX_CELL_CHARS ? `${oneLine.slice(0, MAX_CELL_CHARS - 1)}\u2026` : oneLine;
18915
+ }
18916
+ function renderRows(statement) {
18917
+ if (statement.columns.length === 0) return [];
18918
+ const cells = statement.rows.map(
18919
+ (row) => statement.columns.map((_, index) => formatCell(row[index]))
18920
+ );
18921
+ const widths = statement.columns.map(
18922
+ (column, index) => Math.max(column.length, ...cells.map((row) => row[index]?.length ?? 0))
18923
+ );
18924
+ const line = (values) => values.map((value, index) => value.padEnd(widths[index] ?? 0)).join(" ").trimEnd();
18925
+ return [
18926
+ line(statement.columns),
18927
+ widths.map((width) => "-".repeat(width)).join(" "),
18928
+ ...cells.map(line)
18929
+ ];
18930
+ }
18931
+ function formatMs(ms) {
18932
+ return ms >= 100 ? String(Math.round(ms)) : ms.toFixed(1);
18933
+ }
18934
+ function summarizeStatement(statement) {
18935
+ const rows = statement.rows.length;
18936
+ const { meta: meta3 } = statement;
18937
+ return `\u21B3 ${String(rows)} row${rows === 1 ? "" : "s"} \xB7 ${String(meta3.rowsRead)} read \xB7 ${String(meta3.rowsWritten)} written \xB7 ${formatMs(meta3.durationMs)} ms`;
18938
+ }
16834
18939
 
16835
18940
  // ../../packages/sdk/src/dotenv.ts
16836
18941
  import { existsSync, readFileSync as readFileSync2 } from "fs";
16837
- import { join as join10 } from "path";
18942
+ import { join as join18 } from "path";
16838
18943
  function parseDotenv(text) {
16839
18944
  const out = {};
16840
18945
  for (const line of text.split(/\r?\n/)) {
@@ -16852,7 +18957,7 @@ function parseDotenv(text) {
16852
18957
  }
16853
18958
  function readProjectEnv(dir) {
16854
18959
  for (const name of [".env.local", ".env.production", ".env"]) {
16855
- const path = join10(dir, name);
18960
+ const path = join18(dir, name);
16856
18961
  if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
16857
18962
  }
16858
18963
  return {};
@@ -16867,8 +18972,8 @@ function serializeDotenv(vars) {
16867
18972
 
16868
18973
  // ../../packages/sdk/src/env-scan.ts
16869
18974
  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([
18975
+ import { join as join19 } from "path";
18976
+ var SOURCE_EXTENSIONS3 = /* @__PURE__ */ new Set([
16872
18977
  ".ts",
16873
18978
  ".tsx",
16874
18979
  ".js",
@@ -16922,7 +19027,7 @@ function collect(dir, out) {
16922
19027
  for (const entry of entries) {
16923
19028
  if (out.length >= MAX_FILES) return;
16924
19029
  if (entry.isSymbolicLink()) continue;
16925
- const path = join11(dir, entry.name);
19030
+ const path = join19(dir, entry.name);
16926
19031
  if (entry.isDirectory()) {
16927
19032
  if (entry.name.startsWith(".") || SKIP_DIRECTORIES.has(entry.name))
16928
19033
  continue;
@@ -16931,7 +19036,7 @@ function collect(dir, out) {
16931
19036
  }
16932
19037
  if (!entry.isFile()) continue;
16933
19038
  const dot = entry.name.lastIndexOf(".");
16934
- if (dot < 0 || !SOURCE_EXTENSIONS.has(entry.name.slice(dot))) continue;
19039
+ if (dot < 0 || !SOURCE_EXTENSIONS3.has(entry.name.slice(dot))) continue;
16935
19040
  out.push(path);
16936
19041
  }
16937
19042
  }
@@ -16954,7 +19059,7 @@ function scanEnvReferences(dir) {
16954
19059
  }
16955
19060
  }
16956
19061
  for (const name of EXAMPLE_FILES) {
16957
- const path = join11(dir, name);
19062
+ const path = join19(dir, name);
16958
19063
  if (!existsSync2(path)) continue;
16959
19064
  let parsed;
16960
19065
  try {
@@ -16972,11 +19077,11 @@ function scanEnvReferences(dir) {
16972
19077
 
16973
19078
  // ../../packages/sdk/src/framework.ts
16974
19079
  import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
16975
- import { join as join12 } from "path";
19080
+ import { join as join20 } from "path";
16976
19081
  function readPackageJson2(dir) {
16977
19082
  try {
16978
19083
  return JSON.parse(
16979
- readFileSync4(join12(dir, "package.json"), "utf8")
19084
+ readFileSync4(join20(dir, "package.json"), "utf8")
16980
19085
  );
16981
19086
  } catch {
16982
19087
  return null;
@@ -16987,7 +19092,7 @@ function hasDependency(pkg, name) {
16987
19092
  }
16988
19093
  function hasConfig(dir, base2, evidence) {
16989
19094
  for (const ext of ["js", "mjs", "cjs", "ts", "mts"]) {
16990
- if (existsSync3(join12(dir, `${base2}.${ext}`))) {
19095
+ if (existsSync3(join20(dir, `${base2}.${ext}`))) {
16991
19096
  evidence.push(`${base2}.${ext}`);
16992
19097
  return true;
16993
19098
  }
@@ -16999,8 +19104,8 @@ function detectFramework2(dir) {
16999
19104
  const pkg = readPackageJson2(dir);
17000
19105
  if (hasDependency(pkg, "next") || hasConfig(dir, "next.config", evidence)) {
17001
19106
  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/");
19107
+ if (existsSync3(join20(dir, "app"))) evidence.push("app/");
19108
+ if (existsSync3(join20(dir, "pages"))) evidence.push("pages/");
17004
19109
  return {
17005
19110
  framework: "nextjs",
17006
19111
  name: "Next.js",
@@ -17054,7 +19159,7 @@ function detectFramework2(dir) {
17054
19159
  deployable: true
17055
19160
  };
17056
19161
  }
17057
- if (!pkg && existsSync3(join12(dir, "index.html"))) {
19162
+ if (!pkg && existsSync3(join20(dir, "index.html"))) {
17058
19163
  evidence.push("index.html (no package.json)");
17059
19164
  return {
17060
19165
  framework: "static",
@@ -17075,6 +19180,71 @@ function detectFramework2(dir) {
17075
19180
  };
17076
19181
  }
17077
19182
 
19183
+ // ../../packages/sdk/src/generate-types.ts
19184
+ var HEADER = [
19185
+ "// Generated by `naro gen types`. Do not edit - re-run the command instead.",
19186
+ "// `D1Database` comes from @cloudflare/workers-types."
19187
+ ];
19188
+ function valueType(declared) {
19189
+ const type = declared.toUpperCase();
19190
+ if (type.trim() === "") return "number | string | ArrayBuffer";
19191
+ if (type.includes("INT")) return "number";
19192
+ if (type.includes("CHAR") || type.includes("CLOB") || type.includes("TEXT"))
19193
+ return "string";
19194
+ if (type.includes("BLOB")) return "ArrayBuffer";
19195
+ if (type.includes("REAL") || type.includes("FLOA") || type.includes("DOUB"))
19196
+ return "number";
19197
+ return "number | string";
19198
+ }
19199
+ function interfaceBase(tableName) {
19200
+ 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, "");
19201
+ return pascal === "" || /^[0-9]/.test(pascal) ? `_${pascal}` : pascal;
19202
+ }
19203
+ function rowidAlias(table) {
19204
+ const keys = table.columns.filter((column) => column.primaryKey);
19205
+ const only = keys.length === 1 ? keys[0] : void 0;
19206
+ return only && only.type.trim().toUpperCase() === "INTEGER" ? only.name : null;
19207
+ }
19208
+ var UNICODE_ESCAPE = `${String.fromCharCode(92)}u`;
19209
+ function keyLiteral(name) {
19210
+ return JSON.stringify(name).replace(
19211
+ /[^ -~]/g,
19212
+ (unit) => `${UNICODE_ESCAPE}${unit.charCodeAt(0).toString(16).padStart(4, "0")}`
19213
+ );
19214
+ }
19215
+ function property(column, alias) {
19216
+ const nullable2 = !column.notNull && column.name !== alias;
19217
+ return ` ${keyLiteral(column.name)}: ${valueType(column.type)}${nullable2 ? " | null" : ""};`;
19218
+ }
19219
+ function block(name, lines) {
19220
+ return lines.length === 0 ? [`export interface ${name} {}`] : [`export interface ${name} {`, ...lines, "}"];
19221
+ }
19222
+ function generateTypes(tables) {
19223
+ const used = /* @__PURE__ */ new Set();
19224
+ const out = [...HEADER];
19225
+ const map2 = [];
19226
+ for (const table of tables) {
19227
+ const base2 = `${interfaceBase(table.name)}Row`;
19228
+ let name = base2;
19229
+ for (let suffix = 2; used.has(name); suffix += 1)
19230
+ name = `${base2}_${String(suffix)}`;
19231
+ used.add(name);
19232
+ const alias = rowidAlias(table);
19233
+ out.push(
19234
+ "",
19235
+ ...block(
19236
+ name,
19237
+ table.columns.map((column) => property(column, alias))
19238
+ )
19239
+ );
19240
+ map2.push(` ${keyLiteral(table.name)}: ${name};`);
19241
+ }
19242
+ out.push("", ...block("Database", map2));
19243
+ out.push("", ...block("Env", [" DB: D1Database;"]));
19244
+ return `${out.join("\n")}
19245
+ `;
19246
+ }
19247
+
17078
19248
  // ../../packages/sdk/src/link.ts
17079
19249
  import {
17080
19250
  existsSync as existsSync4,
@@ -17083,12 +19253,12 @@ import {
17083
19253
  rmSync as rmSync2,
17084
19254
  writeFileSync as writeFileSync2
17085
19255
  } from "fs";
17086
- import { join as join13 } from "path";
19256
+ import { join as join21 } from "path";
17087
19257
  function linkDir(dir) {
17088
- return join13(dir, ".naro");
19258
+ return join21(dir, ".naro");
17089
19259
  }
17090
19260
  function linkFile(dir) {
17091
- return join13(linkDir(dir), "project.json");
19261
+ return join21(linkDir(dir), "project.json");
17092
19262
  }
17093
19263
  function readProjectLink(dir) {
17094
19264
  try {
@@ -17110,7 +19280,7 @@ function writeProjectLink(dir, link) {
17110
19280
  writeFileSync2(linkFile(dir), `${JSON.stringify(link, null, 2)}
17111
19281
  `);
17112
19282
  writeFileSync2(
17113
- join13(linkDir(dir), "README.txt"),
19283
+ join21(linkDir(dir), "README.txt"),
17114
19284
  "This directory links the project to Naro and should not be committed.\n"
17115
19285
  );
17116
19286
  }
@@ -17120,9 +19290,9 @@ function removeProjectLink(dir) {
17120
19290
  return true;
17121
19291
  }
17122
19292
  function ensureGitignoreHasNaro(dir) {
17123
- const gitignorePath = join13(dir, ".gitignore");
19293
+ const gitignorePath = join21(dir, ".gitignore");
17124
19294
  const hasGitignore = existsSync4(gitignorePath);
17125
- if (!hasGitignore && !existsSync4(join13(dir, ".git"))) return false;
19295
+ if (!hasGitignore && !existsSync4(join21(dir, ".git"))) return false;
17126
19296
  const current = hasGitignore ? readFileSync5(gitignorePath, "utf8") : "";
17127
19297
  const covered = current.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".naro" || line === ".naro/" || line === "/.naro");
17128
19298
  if (covered) return false;
@@ -17132,9 +19302,94 @@ function ensureGitignoreHasNaro(dir) {
17132
19302
  return true;
17133
19303
  }
17134
19304
 
19305
+ // ../../packages/sdk/src/migrations.ts
19306
+ import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
19307
+ import { resolve as resolve8 } from "path";
19308
+ function resolveMigrationsDir(input) {
19309
+ if (input.dir) return resolve8(input.baseDir, input.dir);
19310
+ return resolve8(input.baseDir, input.rootDirectory ?? "", "migrations");
19311
+ }
19312
+ function normalizeMigrationName(name) {
19313
+ return name.trim().toLowerCase().replace(/\.sql$/, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
19314
+ }
19315
+ function leadingNumber(name) {
19316
+ const match = /^(\d+)/.exec(name);
19317
+ return match ? Number(match[1]) : null;
19318
+ }
19319
+ function sortMigrationNames(names) {
19320
+ return names.toSorted((a, b) => {
19321
+ const left = leadingNumber(a);
19322
+ const right = leadingNumber(b);
19323
+ if (left !== null && right !== null && left !== right) return left - right;
19324
+ if (left === null && right !== null) return 1;
19325
+ if (left !== null && right === null) return -1;
19326
+ return a < b ? -1 : a > b ? 1 : 0;
19327
+ });
19328
+ }
19329
+ function highestMigrationNumber(names) {
19330
+ return names.reduce(
19331
+ (max, name) => Math.max(max, leadingNumber(name) ?? 0),
19332
+ 0
19333
+ );
19334
+ }
19335
+ function listMigrationFiles(dir) {
19336
+ if (!existsSync5(dir)) return [];
19337
+ return sortMigrationNames(
19338
+ readdirSync2(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name)
19339
+ );
19340
+ }
19341
+ function nextMigrationFileName(existing, name) {
19342
+ const normalized = normalizeMigrationName(name);
19343
+ if (normalized === "")
19344
+ throw new NaroError(
19345
+ "VALIDATION_FAILED",
19346
+ "Migration name must contain letters or digits."
19347
+ );
19348
+ const number4 = String(highestMigrationNumber(existing) + 1).padStart(4, "0");
19349
+ return `${number4}_${normalized}.sql`;
19350
+ }
19351
+ function compareMigrations(local, applied) {
19352
+ const ledger = new Map(applied.map((row) => [row.name, row]));
19353
+ const highestApplied = highestMigrationNumber(applied.map((row) => row.name));
19354
+ const rows = [];
19355
+ for (const name of local) {
19356
+ const exact = ledger.get(name);
19357
+ if (exact) {
19358
+ ledger.delete(name);
19359
+ rows.push({ name, status: "applied", appliedAt: exact.appliedAt });
19360
+ continue;
19361
+ }
19362
+ const bare = ledger.get(name.replace(/\.sql$/, ""));
19363
+ if (bare) {
19364
+ ledger.delete(bare.name);
19365
+ rows.push({
19366
+ name,
19367
+ status: "applied",
19368
+ appliedAt: bare.appliedAt,
19369
+ warning: "ledger-name-missing-extension"
19370
+ });
19371
+ continue;
19372
+ }
19373
+ const number4 = leadingNumber(name);
19374
+ rows.push({
19375
+ name,
19376
+ status: "pending",
19377
+ ...number4 !== null && number4 < highestApplied ? { warning: "out-of-order" } : {}
19378
+ });
19379
+ }
19380
+ for (const row of ledger.values())
19381
+ rows.push({
19382
+ name: row.name,
19383
+ status: "remote-only",
19384
+ appliedAt: row.appliedAt
19385
+ });
19386
+ const order = sortMigrationNames(rows.map((row) => row.name));
19387
+ return rows.toSorted((a, b) => order.indexOf(a.name) - order.indexOf(b.name));
19388
+ }
19389
+
17135
19390
  // src/env-consent.ts
17136
- import { existsSync as existsSync5 } from "fs";
17137
- import { join as join14 } from "path";
19391
+ import { existsSync as existsSync6 } from "fs";
19392
+ import { join as join22 } from "path";
17138
19393
  var DOTENV_FILES = [".env.local", ".env.production", ".env"];
17139
19394
  var PUBLIC_PREFIXES = ["NEXT_PUBLIC_", "VITE_", "REACT_APP_", "PUBLIC_"];
17140
19395
  function maskEnvValue(key, value) {
@@ -17143,7 +19398,7 @@ function maskEnvValue(key, value) {
17143
19398
  return value.length <= 8 ? "****" : `${value.slice(0, 4)}****`;
17144
19399
  }
17145
19400
  function findDotenvFile(dir) {
17146
- for (const name of DOTENV_FILES) if (existsSync5(join14(dir, name))) return name;
19401
+ for (const name of DOTENV_FILES) if (existsSync6(join22(dir, name))) return name;
17147
19402
  return null;
17148
19403
  }
17149
19404
  function envAddGuidance(keys) {
@@ -17200,10 +19455,10 @@ var SUPPORTED_REMOTE = [
17200
19455
  /^ssh:\/\/[^\s]+$/,
17201
19456
  /^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
17202
19457
  ];
17203
- var defaultExec = (args, cwd) => new Promise((resolve5, reject) => {
19458
+ var defaultExec = (args, cwd) => new Promise((resolve10, reject) => {
17204
19459
  execFile("git", args, { cwd }, (error51, stdout) => {
17205
19460
  if (error51) reject(error51);
17206
- else resolve5(stdout);
19461
+ else resolve10(stdout);
17207
19462
  });
17208
19463
  });
17209
19464
  async function tryExec(exec, args, cwd) {
@@ -17335,8 +19590,8 @@ function parseDotenv2(text) {
17335
19590
  function readProjectEnv2(dir) {
17336
19591
  return readProjectEnv(dir);
17337
19592
  }
17338
- function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
17339
- const post = (body) => fetchImpl(`${cp2}/api/internal/deployments/${deploymentId}/events`, {
19593
+ function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUrl) {
19594
+ const post = (body) => fetchImpl(`${cp3}/api/internal/deployments/${deploymentId}/events`, {
17340
19595
  method: "POST",
17341
19596
  headers: {
17342
19597
  "Content-Type": "application/json",
@@ -17353,18 +19608,48 @@ function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
17353
19608
  } catch (error51) {
17354
19609
  lastError = error51;
17355
19610
  await new Promise(
17356
- (resolve5) => setTimeout(resolve5, 500 * (attempt + 1))
19611
+ (resolve10) => setTimeout(resolve10, 500 * (attempt + 1))
17357
19612
  );
17358
19613
  }
17359
19614
  }
17360
19615
  throw lastError;
17361
19616
  };
19617
+ const send = createLogSender({
19618
+ ...logIngestUrl ? { ingestUrl: logIngestUrl } : {},
19619
+ toIngest: logIngestUrl ? (logs) => fetchImpl(`${logIngestUrl}/deployments/${deploymentId}/logs`, {
19620
+ method: "POST",
19621
+ headers: {
19622
+ "Content-Type": "application/json",
19623
+ Authorization: `Bearer ${token}`
19624
+ },
19625
+ body: JSON.stringify({ logs })
19626
+ }) : null,
19627
+ toControlPlane: (logs) => post({ type: "logs", logs }),
19628
+ // The CLI swallows log failures so a deploy never dies over a log line.
19629
+ warn: (message) => post({
19630
+ type: "log",
19631
+ log: { level: "WARN", stage: "BUILDING", message }
19632
+ }).catch(() => void 0)
19633
+ });
19634
+ const batcher = createLogBatcher({
19635
+ limit: DEPLOYMENT_LOG_BATCH_LIMIT,
19636
+ intervalMs: DEPLOYMENT_LOG_FLUSH_MS,
19637
+ send
19638
+ });
19639
+ const flushLogs = () => batcher.flush().catch(() => {
19640
+ });
17362
19641
  return {
17363
- onStatus: (status) => postStatus({ type: "status", status }),
17364
- onLog: (log2) => post({ type: "log", log: log2 }).catch(() => {
19642
+ onStatus: async (status) => {
19643
+ await flushLogs();
19644
+ await postStatus({ type: "status", status });
19645
+ },
19646
+ onLog: (log2) => batcher.add(log2).catch(() => {
17365
19647
  }),
17366
- onMetadata: (metadata) => post({ type: "metadata", metadata }).catch(() => {
17367
- })
19648
+ onMetadata: async (metadata) => {
19649
+ await flushLogs();
19650
+ await post({ type: "metadata", metadata }).catch(() => {
19651
+ });
19652
+ }
17368
19653
  };
17369
19654
  }
17370
19655
  function slugify2(value) {
@@ -17374,20 +19659,20 @@ function slugify2(value) {
17374
19659
  async function projectName(sourceDir) {
17375
19660
  try {
17376
19661
  const pkg = JSON.parse(
17377
- await readFile7(join15(sourceDir, "package.json"), "utf8")
19662
+ await readFile13(join23(sourceDir, "package.json"), "utf8")
17378
19663
  );
17379
19664
  if (pkg.name) return pkg.name;
17380
19665
  } catch {
17381
19666
  }
17382
- return basename(sourceDir);
19667
+ return basename3(sourceDir);
17383
19668
  }
17384
19669
  var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo|\.naro)([\\/]|$)/;
17385
19670
  function resolveBin(name) {
17386
- let dir = dirname(fileURLToPath(import.meta.url));
19671
+ let dir = dirname4(fileURLToPath(import.meta.url));
17387
19672
  for (; ; ) {
17388
- const candidate = join15(dir, "node_modules", ".bin", name);
17389
- if (existsSync6(candidate)) return candidate;
17390
- const parent = dirname(dir);
19673
+ const candidate = join23(dir, "node_modules", ".bin", name);
19674
+ if (existsSync7(candidate)) return candidate;
19675
+ const parent = dirname4(dir);
17391
19676
  if (parent === dir) return name;
17392
19677
  dir = parent;
17393
19678
  }
@@ -17405,20 +19690,25 @@ async function detectPackageManagerLenient(root) {
17405
19690
  "--no-fund",
17406
19691
  "--legacy-peer-deps"
17407
19692
  ],
19693
+ addArgs: (specs) => ["npm", "install", "--no-save", ...specs],
17408
19694
  cacheArgs: (dir) => ["--cache", dir]
17409
19695
  };
17410
19696
  }
17411
19697
  }
17412
19698
  function localDependencies(sourceDir) {
17413
19699
  return {
17414
- createWorkspace: () => mkdtemp2(join15(tmpdir2(), "naro-build-")),
17415
- removeWorkspace: (path) => rm2(path, { recursive: true, force: true }),
19700
+ createWorkspace: () => mkdtemp2(join23(tmpdir2(), "naro-build-")),
19701
+ removeWorkspace: (path) => rm3(path, { recursive: true, force: true }),
17416
19702
  async runCommand(spec, observer) {
17417
19703
  if (spec.command === "git" && spec.args[0] === "clone") {
17418
19704
  const checkout = spec.args.at(-1);
17419
19705
  if (!checkout) throw new Error("Missing checkout path.");
17420
- await cp(sourceDir, checkout, {
19706
+ await cp2(sourceDir, checkout, {
17421
19707
  recursive: true,
19708
+ // Relative symlinks must stay relative: resolved, they would point
19709
+ // back into the developer's real tree, and a strategy's temporary
19710
+ // rewrite (proxy -> middleware, vinext toolchain) would land there.
19711
+ verbatimSymlinks: true,
17422
19712
  filter: (src) => !COPY_SKIP.test(src)
17423
19713
  });
17424
19714
  return { stdout: [], stderr: [] };
@@ -17439,13 +19729,16 @@ function localDependencies(sourceDir) {
17439
19729
  createPlatformBuildConfig,
17440
19730
  readOpenNextArtifact,
17441
19731
  openNextCommand: resolveBin("opennextjs-cloudflare"),
17442
- wranglerCommand: resolveBin("wrangler")
19732
+ wranglerCommand: resolveBin("wrangler"),
19733
+ // `vinext check` runs on the bare checkout, before the toolchain is added
19734
+ // to the user's tree, so this binary has to come from the CLI's own deps.
19735
+ vinextCommand: resolveBin("vinext")
17443
19736
  };
17444
19737
  }
17445
19738
  async function deployDirectory(sourceDir, options, log2) {
17446
19739
  const cpUrl = controlPlaneUrl();
17447
19740
  const production = options.production ?? false;
17448
- const appRoot = options.rootDirectory ? resolve4(sourceDir, options.rootDirectory) : sourceDir;
19741
+ const appRoot = options.rootDirectory ? resolve9(sourceDir, options.rootDirectory) : sourceDir;
17449
19742
  const framework = detectFramework2(appRoot);
17450
19743
  if (!framework.deployable && !options.force) {
17451
19744
  throw new NaroError(
@@ -17518,7 +19811,8 @@ async function deployDirectory(sourceDir, options, log2) {
17518
19811
  cpUrl,
17519
19812
  ctx.deploymentId,
17520
19813
  ctx.callbackToken,
17521
- options.fetch
19814
+ options.fetch,
19815
+ ctx.logIngestUrl
17522
19816
  );
17523
19817
  let artifact;
17524
19818
  try {
@@ -17529,7 +19823,9 @@ async function deployDirectory(sourceDir, options, log2) {
17529
19823
  repositoryUrl: sourceDir,
17530
19824
  branch: ctx.branch,
17531
19825
  rootDirectory: options.rootDirectory ?? null,
17532
- env: { ...ctx.env, ...envToSend }
19826
+ env: { ...ctx.env, ...envToSend },
19827
+ strategy: ctx.strategy ?? null,
19828
+ ...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
17533
19829
  },
17534
19830
  observer,
17535
19831
  createCacheTransport({
@@ -17609,11 +19905,18 @@ export {
17609
19905
  openBrowser,
17610
19906
  isTerminalStatus,
17611
19907
  NaroClient,
19908
+ renderRows,
19909
+ summarizeStatement,
17612
19910
  serializeDotenv,
19911
+ generateTypes,
17613
19912
  readProjectLink,
17614
19913
  writeProjectLink,
17615
19914
  removeProjectLink,
17616
19915
  ensureGitignoreHasNaro,
19916
+ resolveMigrationsDir,
19917
+ listMigrationFiles,
19918
+ nextMigrationFileName,
19919
+ compareMigrations,
17617
19920
  promptForMissingEnv,
17618
19921
  detectGitInfo,
17619
19922
  parseDotenv2 as parseDotenv,