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.
- package/README.md +38 -4
- package/dist/artifact-types.d.ts +15 -0
- package/dist/{chunk-WKJELBOU.js → chunk-E2M6CQL7.js} +2931 -628
- package/dist/deploy.d.ts +4 -2
- package/dist/deploy.js +1 -1
- package/dist/index.js +598 -41
- package/dist/output.d.ts +5 -0
- package/package.json +5 -1
|
@@ -6,10 +6,10 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/deploy.ts
|
|
9
|
-
import { existsSync as
|
|
10
|
-
import { cp, mkdtemp as mkdtemp2, readFile as
|
|
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
|
|
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
|
|
146
|
-
if (!
|
|
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
|
|
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,
|
|
2477
|
+
function handleCheckPropertyResult(result, payload, property2) {
|
|
2472
2478
|
if (result.issues.length) {
|
|
2473
|
-
payload.issues.push(...prefixIssues(
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
13788
|
-
if (!
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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/
|
|
14984
|
-
import { access as
|
|
14985
|
-
import {
|
|
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
|
|
15191
|
-
import {
|
|
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,
|
|
15133
|
+
function confinedPath(root, configuredPath, label2, base2 = root) {
|
|
15232
15134
|
if (isAbsolute(configuredPath)) {
|
|
15233
15135
|
throw new DeploymentError(
|
|
15234
15136
|
"ARTIFACT_INVALID",
|
|
15235
|
-
`${
|
|
15137
|
+
`${label2} must be relative to the application root.`
|
|
15236
15138
|
);
|
|
15237
15139
|
}
|
|
15238
|
-
const resolved = resolve(
|
|
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
|
-
`${
|
|
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
|
|
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 =
|
|
15206
|
+
const path = join4(directory, entry.name);
|
|
15305
15207
|
if (entry.isSymbolicLink()) {
|
|
15306
15208
|
throw new DeploymentError(
|
|
15307
15209
|
"ARTIFACT_INVALID",
|
|
15308
|
-
|
|
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
|
|
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
|
|
15236
|
+
await access3(resolved);
|
|
15335
15237
|
return resolved;
|
|
15336
15238
|
}
|
|
15337
15239
|
for (const name of ["wrangler.jsonc", "wrangler.json"]) {
|
|
15338
|
-
const path =
|
|
15240
|
+
const path = join4(root, name);
|
|
15339
15241
|
try {
|
|
15340
|
-
await
|
|
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(
|
|
15266
|
+
const configPath = await findWranglerConfig(
|
|
15267
|
+
appRoot,
|
|
15268
|
+
layout.wranglerConfigPath
|
|
15269
|
+
);
|
|
15353
15270
|
const metadata = parseJsonc(
|
|
15354
|
-
await
|
|
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
|
|
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
|
-
|
|
15291
|
+
`Worker entrypoint must be inside ${layout.outputRoot}.`
|
|
15373
15292
|
);
|
|
15374
15293
|
}
|
|
15375
|
-
await
|
|
15376
|
-
const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(
|
|
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
|
-
|
|
15305
|
+
`Assets directory must be inside ${layout.outputRoot}.`
|
|
15382
15306
|
);
|
|
15383
15307
|
}
|
|
15384
|
-
const bundleRoot =
|
|
15308
|
+
const bundleRoot = confinedPath(appRoot, layout.bundleDir, "Bundle dir");
|
|
15385
15309
|
let bundleFiles = null;
|
|
15386
15310
|
try {
|
|
15387
|
-
await
|
|
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 ?
|
|
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
|
-
"
|
|
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
|
|
15341
|
+
"Could not read the Worker build output.",
|
|
15418
15342
|
{ cause: error51 }
|
|
15419
15343
|
);
|
|
15420
15344
|
}
|
|
15421
15345
|
}
|
|
15422
15346
|
|
|
15423
|
-
// ../../packages/build/src/
|
|
15424
|
-
import {
|
|
15425
|
-
import {
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
var
|
|
15429
|
-
|
|
15430
|
-
|
|
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
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
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
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15453
|
-
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
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
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
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
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
|
|
15478
|
-
|
|
15479
|
-
|
|
15480
|
-
|
|
15481
|
-
|
|
15482
|
-
|
|
15483
|
-
|
|
15484
|
-
|
|
15485
|
-
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
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
|
-
|
|
15502
|
-
|
|
15503
|
-
|
|
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
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
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
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
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
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
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
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
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
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
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
|
|
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/
|
|
15554
|
-
|
|
15555
|
-
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
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
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
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
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
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
|
-
|
|
15619
|
-
|
|
15620
|
-
"
|
|
15621
|
-
|
|
15622
|
-
|
|
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
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
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
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
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
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
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
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
15727
|
-
const pmCacheDir =
|
|
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
|
|
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) =>
|
|
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
|
|
17581
|
+
await info2(observer, "CLONING", `Detected ${resolved.description}`);
|
|
15813
17582
|
const isNext = resolved.preset.id === "next";
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
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
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
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
|
-
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
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
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
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 ?
|
|
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
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
17775
|
+
...input.env,
|
|
17776
|
+
NPM_CONFIG_USERCONFIG: packageManagerConfigPath
|
|
15937
17777
|
},
|
|
15938
|
-
timeoutMs:
|
|
17778
|
+
timeoutMs: 15 * 6e4,
|
|
15939
17779
|
stage: "BUILDING",
|
|
15940
|
-
errorCode: "
|
|
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
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
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
|
|
17955
|
+
import { join as join17 } from "path";
|
|
16040
17956
|
function credentialsDir(home = homedir()) {
|
|
16041
|
-
return
|
|
17957
|
+
return join17(home, ".naro");
|
|
16042
17958
|
}
|
|
16043
17959
|
function credentialsFile(home = homedir()) {
|
|
16044
|
-
return
|
|
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((
|
|
16233
|
-
resolveCode =
|
|
18148
|
+
const codePromise = new Promise((resolve10, reject) => {
|
|
18149
|
+
resolveCode = resolve10;
|
|
16234
18150
|
rejectCode = reject;
|
|
16235
18151
|
});
|
|
16236
|
-
const server =
|
|
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((
|
|
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
|
|
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
|
|
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
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
16871
|
-
var
|
|
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 =
|
|
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 || !
|
|
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 =
|
|
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
|
|
19080
|
+
import { join as join20 } from "path";
|
|
16976
19081
|
function readPackageJson2(dir) {
|
|
16977
19082
|
try {
|
|
16978
19083
|
return JSON.parse(
|
|
16979
|
-
readFileSync4(
|
|
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(
|
|
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(
|
|
17003
|
-
if (existsSync3(
|
|
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(
|
|
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
|
|
19256
|
+
import { join as join21 } from "path";
|
|
17087
19257
|
function linkDir(dir) {
|
|
17088
|
-
return
|
|
19258
|
+
return join21(dir, ".naro");
|
|
17089
19259
|
}
|
|
17090
19260
|
function linkFile(dir) {
|
|
17091
|
-
return
|
|
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
|
-
|
|
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 =
|
|
19293
|
+
const gitignorePath = join21(dir, ".gitignore");
|
|
17124
19294
|
const hasGitignore = existsSync4(gitignorePath);
|
|
17125
|
-
if (!hasGitignore && !existsSync4(
|
|
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
|
|
17137
|
-
import { join as
|
|
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 (
|
|
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((
|
|
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
|
|
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(
|
|
17339
|
-
const post = (body) => fetchImpl(`${
|
|
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
|
-
(
|
|
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) =>
|
|
17364
|
-
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
19671
|
+
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
17387
19672
|
for (; ; ) {
|
|
17388
|
-
const candidate =
|
|
17389
|
-
if (
|
|
17390
|
-
const parent =
|
|
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(
|
|
17415
|
-
removeWorkspace: (path) =>
|
|
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
|
|
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 ?
|
|
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,
|