naro-cli 0.6.0 → 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 +52 -4
- package/dist/artifact-types.d.ts +15 -0
- package/dist/{chunk-TYBC3YSK.js → chunk-E2M6CQL7.js} +3253 -701
- package/dist/deploy.d.ts +6 -3
- package/dist/deploy.js +1 -1
- package/dist/index.js +598 -41
- package/dist/output.d.ts +5 -0
- package/package.json +7 -3
|
@@ -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
|
|
@@ -92,19 +92,43 @@ async function computeDepsCacheKey(checkoutDir, packageManager) {
|
|
|
92
92
|
async function log(observer, scope, level, message) {
|
|
93
93
|
await observer.onLog({ level, stage: STAGE[scope], message });
|
|
94
94
|
}
|
|
95
|
+
async function reportUnavailable(scope, ports, observer) {
|
|
96
|
+
if (ports.report?.unavailable === true) return;
|
|
97
|
+
if (ports.report) ports.report.unavailable = true;
|
|
98
|
+
await log(
|
|
99
|
+
observer,
|
|
100
|
+
scope,
|
|
101
|
+
"WARN",
|
|
102
|
+
"Build cache is unavailable - this build runs uncached and saves nothing for the next one."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
95
105
|
async function restoreCache(args) {
|
|
96
106
|
const { scope, key, targetDir, ports, observer } = args;
|
|
97
107
|
const urls = await ports.transport.resolveUrls(scope, key);
|
|
98
|
-
if (!urls)
|
|
108
|
+
if (!urls) {
|
|
109
|
+
await reportUnavailable(scope, ports, observer);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
99
112
|
const response = await ports.fetch(urls.getUrl);
|
|
100
|
-
if (response.status !== 200)
|
|
113
|
+
if (response.status !== 200) {
|
|
114
|
+
await log(
|
|
115
|
+
observer,
|
|
116
|
+
scope,
|
|
117
|
+
"INFO",
|
|
118
|
+
`No ${scope} cache stored for ${key} yet.`
|
|
119
|
+
);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
101
122
|
const archive = ports.tmpFile(".tar.zst");
|
|
102
123
|
await writeFile(archive, Buffer.from(await response.arrayBuffer()));
|
|
103
124
|
await mkdir(targetDir, { recursive: true });
|
|
104
125
|
await ports.runCommand(
|
|
105
126
|
{
|
|
106
127
|
command: "tar",
|
|
107
|
-
|
|
128
|
+
// --zstd, not --use-compress-program: BSD tar (macOS, where the CLI
|
|
129
|
+
// builds) feeds the external program wrong on extract and dies with
|
|
130
|
+
// "Broken pipe". Both GNU tar 1.31+ and bsdtar 3.x accept --zstd.
|
|
131
|
+
args: ["--zstd", "-xf", archive, "-C", targetDir],
|
|
108
132
|
cwd: targetDir,
|
|
109
133
|
timeoutMs: 12e4,
|
|
110
134
|
stage: STAGE[scope],
|
|
@@ -118,23 +142,27 @@ async function restoreCache(args) {
|
|
|
118
142
|
async function saveCache(args) {
|
|
119
143
|
const { scope, key, sourceDir, ports, observer } = args;
|
|
120
144
|
try {
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
145
|
+
const info3 = await stat(sourceDir);
|
|
146
|
+
if (!info3.isDirectory()) throw new Error("not a directory");
|
|
123
147
|
} catch {
|
|
148
|
+
await log(
|
|
149
|
+
observer,
|
|
150
|
+
scope,
|
|
151
|
+
"WARN",
|
|
152
|
+
`Nothing to save for the ${scope} cache - the build wrote nothing to that directory.`
|
|
153
|
+
);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const urls = await ports.transport.resolveUrls(scope, key);
|
|
157
|
+
if (!urls) {
|
|
158
|
+
await reportUnavailable(scope, ports, observer);
|
|
124
159
|
return;
|
|
125
160
|
}
|
|
126
161
|
const archive = ports.tmpFile(".tar.zst");
|
|
127
162
|
await ports.runCommand(
|
|
128
163
|
{
|
|
129
164
|
command: "tar",
|
|
130
|
-
args: [
|
|
131
|
-
"--use-compress-program=zstd",
|
|
132
|
-
"-cf",
|
|
133
|
-
archive,
|
|
134
|
-
"-C",
|
|
135
|
-
sourceDir,
|
|
136
|
-
"."
|
|
137
|
-
],
|
|
165
|
+
args: ["--zstd", "-cf", archive, "-C", sourceDir, "."],
|
|
138
166
|
cwd: sourceDir,
|
|
139
167
|
timeoutMs: 12e4,
|
|
140
168
|
stage: STAGE[scope],
|
|
@@ -142,8 +170,6 @@ async function saveCache(args) {
|
|
|
142
170
|
},
|
|
143
171
|
observer
|
|
144
172
|
);
|
|
145
|
-
const urls = await ports.transport.resolveUrls(scope, key);
|
|
146
|
-
if (!urls) return;
|
|
147
173
|
const body = await readFile(archive);
|
|
148
174
|
const response = await ports.fetch(urls.putUrl, { method: "PUT", body });
|
|
149
175
|
if (!response.ok) {
|
|
@@ -158,16 +184,51 @@ async function saveCache(args) {
|
|
|
158
184
|
await log(observer, scope, "INFO", `Saved ${scope} cache (${key}).`);
|
|
159
185
|
}
|
|
160
186
|
|
|
187
|
+
// ../../packages/build/src/cache-transport.ts
|
|
188
|
+
function createCacheTransport(options) {
|
|
189
|
+
return {
|
|
190
|
+
async resolveUrls(scope, key) {
|
|
191
|
+
try {
|
|
192
|
+
const url2 = new URL(
|
|
193
|
+
`/api/internal/deployments/${options.deploymentId}/cache/url`,
|
|
194
|
+
options.controlPlaneUrl
|
|
195
|
+
);
|
|
196
|
+
const response = await options.fetch(url2, {
|
|
197
|
+
method: "POST",
|
|
198
|
+
headers: {
|
|
199
|
+
"Content-Type": "application/json",
|
|
200
|
+
Authorization: `Bearer ${options.callbackToken}`
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify({ scope, key })
|
|
203
|
+
});
|
|
204
|
+
if (!response.ok) return null;
|
|
205
|
+
const data = await response.json();
|
|
206
|
+
if (data.disabled === true) return null;
|
|
207
|
+
if (typeof data.getUrl !== "string" || typeof data.putUrl !== "string") {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return { getUrl: data.getUrl, putUrl: data.putUrl };
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
161
218
|
// ../../packages/build/src/hash.ts
|
|
162
219
|
import { createHash as createHash2 } from "crypto";
|
|
163
|
-
|
|
164
|
-
|
|
220
|
+
var FALLBACK_CONTENT_TYPE = "application/octet-stream";
|
|
221
|
+
function assetContentType(value) {
|
|
222
|
+
return value || FALLBACK_CONTENT_TYPE;
|
|
223
|
+
}
|
|
224
|
+
function hashAsset(data, contentType2) {
|
|
225
|
+
return createHash2("sha256").update(assetContentType(contentType2)).update("\0").update(data).digest("hex").slice(0, 32);
|
|
165
226
|
}
|
|
166
227
|
|
|
167
228
|
// ../../packages/build/src/local-build-executor.ts
|
|
168
|
-
import { mkdtemp, rm } from "fs/promises";
|
|
229
|
+
import { access as access8, mkdtemp, readFile as readFile12, rm as rm2 } from "fs/promises";
|
|
169
230
|
import { tmpdir } from "os";
|
|
170
|
-
import { join as
|
|
231
|
+
import { dirname as dirname3, join as join16, resolve as resolve7 } from "path";
|
|
171
232
|
|
|
172
233
|
// ../../packages/core/src/artifact-manifest.ts
|
|
173
234
|
var MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
@@ -183,6 +244,9 @@ var PHASES = [
|
|
|
183
244
|
];
|
|
184
245
|
var ACTIVE_STATUSES = new Set(PHASES);
|
|
185
246
|
|
|
247
|
+
// ../../packages/core/src/deployment-strategy.ts
|
|
248
|
+
var MIN_SUPPORTED_NEXT_MAJOR = 15;
|
|
249
|
+
|
|
186
250
|
// ../../packages/core/src/errors.ts
|
|
187
251
|
var DeploymentError = class extends Error {
|
|
188
252
|
code;
|
|
@@ -217,6 +281,9 @@ function pathnameOf(url2) {
|
|
|
217
281
|
}
|
|
218
282
|
}
|
|
219
283
|
|
|
284
|
+
// ../../packages/core/src/internal-auth.ts
|
|
285
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
286
|
+
|
|
220
287
|
// ../../packages/core/src/paths.ts
|
|
221
288
|
function projectPath(teamSlug, projectSlug) {
|
|
222
289
|
return `/${teamSlug}/projects/${projectSlug}`;
|
|
@@ -2407,9 +2474,9 @@ var $ZodCheckEndsWith = /* @__PURE__ */ $constructor("$ZodCheckEndsWith", (inst,
|
|
|
2407
2474
|
});
|
|
2408
2475
|
};
|
|
2409
2476
|
});
|
|
2410
|
-
function handleCheckPropertyResult(result, payload,
|
|
2477
|
+
function handleCheckPropertyResult(result, payload, property2) {
|
|
2411
2478
|
if (result.issues.length) {
|
|
2412
|
-
payload.issues.push(...prefixIssues(
|
|
2479
|
+
payload.issues.push(...prefixIssues(property2, result.issues));
|
|
2413
2480
|
}
|
|
2414
2481
|
}
|
|
2415
2482
|
var $ZodCheckProperty = /* @__PURE__ */ $constructor("$ZodCheckProperty", (inst, def) => {
|
|
@@ -11236,10 +11303,10 @@ function _endsWith(suffix, params) {
|
|
|
11236
11303
|
});
|
|
11237
11304
|
}
|
|
11238
11305
|
// @__NO_SIDE_EFFECTS__
|
|
11239
|
-
function _property(
|
|
11306
|
+
function _property(property2, schema, params) {
|
|
11240
11307
|
return new $ZodCheckProperty({
|
|
11241
11308
|
check: "property",
|
|
11242
|
-
property,
|
|
11309
|
+
property: property2,
|
|
11243
11310
|
schema,
|
|
11244
11311
|
...normalizeParams(params)
|
|
11245
11312
|
});
|
|
@@ -11336,28 +11403,28 @@ function _tuple(Class2, items, _paramsOrRest, _params) {
|
|
|
11336
11403
|
});
|
|
11337
11404
|
}
|
|
11338
11405
|
// @__NO_SIDE_EFFECTS__
|
|
11339
|
-
function _record(Class2, keyType,
|
|
11406
|
+
function _record(Class2, keyType, valueType2, params) {
|
|
11340
11407
|
return new Class2({
|
|
11341
11408
|
type: "record",
|
|
11342
11409
|
keyType,
|
|
11343
|
-
valueType,
|
|
11410
|
+
valueType: valueType2,
|
|
11344
11411
|
...normalizeParams(params)
|
|
11345
11412
|
});
|
|
11346
11413
|
}
|
|
11347
11414
|
// @__NO_SIDE_EFFECTS__
|
|
11348
|
-
function _map(Class2, keyType,
|
|
11415
|
+
function _map(Class2, keyType, valueType2, params) {
|
|
11349
11416
|
return new Class2({
|
|
11350
11417
|
type: "map",
|
|
11351
11418
|
keyType,
|
|
11352
|
-
valueType,
|
|
11419
|
+
valueType: valueType2,
|
|
11353
11420
|
...normalizeParams(params)
|
|
11354
11421
|
});
|
|
11355
11422
|
}
|
|
11356
11423
|
// @__NO_SIDE_EFFECTS__
|
|
11357
|
-
function _set(Class2,
|
|
11424
|
+
function _set(Class2, valueType2, params) {
|
|
11358
11425
|
return new Class2({
|
|
11359
11426
|
type: "set",
|
|
11360
|
-
valueType,
|
|
11427
|
+
valueType: valueType2,
|
|
11361
11428
|
...normalizeParams(params)
|
|
11362
11429
|
});
|
|
11363
11430
|
}
|
|
@@ -13723,37 +13790,37 @@ var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
|
13723
13790
|
inst.keyType = def.keyType;
|
|
13724
13791
|
inst.valueType = def.valueType;
|
|
13725
13792
|
});
|
|
13726
|
-
function record(keyType,
|
|
13727
|
-
if (!
|
|
13793
|
+
function record(keyType, valueType2, params) {
|
|
13794
|
+
if (!valueType2 || !valueType2._zod) {
|
|
13728
13795
|
return new ZodRecord({
|
|
13729
13796
|
type: "record",
|
|
13730
13797
|
keyType: string2(),
|
|
13731
13798
|
valueType: keyType,
|
|
13732
|
-
...util_exports.normalizeParams(
|
|
13799
|
+
...util_exports.normalizeParams(valueType2)
|
|
13733
13800
|
});
|
|
13734
13801
|
}
|
|
13735
13802
|
return new ZodRecord({
|
|
13736
13803
|
type: "record",
|
|
13737
13804
|
keyType,
|
|
13738
|
-
valueType,
|
|
13805
|
+
valueType: valueType2,
|
|
13739
13806
|
...util_exports.normalizeParams(params)
|
|
13740
13807
|
});
|
|
13741
13808
|
}
|
|
13742
|
-
function partialRecord(keyType,
|
|
13809
|
+
function partialRecord(keyType, valueType2, params) {
|
|
13743
13810
|
const k = clone(keyType);
|
|
13744
13811
|
k._zod.values = void 0;
|
|
13745
13812
|
return new ZodRecord({
|
|
13746
13813
|
type: "record",
|
|
13747
13814
|
keyType: k,
|
|
13748
|
-
valueType,
|
|
13815
|
+
valueType: valueType2,
|
|
13749
13816
|
...util_exports.normalizeParams(params)
|
|
13750
13817
|
});
|
|
13751
13818
|
}
|
|
13752
|
-
function looseRecord(keyType,
|
|
13819
|
+
function looseRecord(keyType, valueType2, params) {
|
|
13753
13820
|
return new ZodRecord({
|
|
13754
13821
|
type: "record",
|
|
13755
13822
|
keyType,
|
|
13756
|
-
valueType,
|
|
13823
|
+
valueType: valueType2,
|
|
13757
13824
|
mode: "loose",
|
|
13758
13825
|
...util_exports.normalizeParams(params)
|
|
13759
13826
|
});
|
|
@@ -13769,11 +13836,11 @@ var ZodMap = /* @__PURE__ */ $constructor("ZodMap", (inst, def) => {
|
|
|
13769
13836
|
inst.max = (...args) => inst.check(_maxSize(...args));
|
|
13770
13837
|
inst.size = (...args) => inst.check(_size(...args));
|
|
13771
13838
|
});
|
|
13772
|
-
function map(keyType,
|
|
13839
|
+
function map(keyType, valueType2, params) {
|
|
13773
13840
|
return new ZodMap({
|
|
13774
13841
|
type: "map",
|
|
13775
13842
|
keyType,
|
|
13776
|
-
valueType,
|
|
13843
|
+
valueType: valueType2,
|
|
13777
13844
|
...util_exports.normalizeParams(params)
|
|
13778
13845
|
});
|
|
13779
13846
|
}
|
|
@@ -13786,10 +13853,10 @@ var ZodSet = /* @__PURE__ */ $constructor("ZodSet", (inst, def) => {
|
|
|
13786
13853
|
inst.max = (...args) => inst.check(_maxSize(...args));
|
|
13787
13854
|
inst.size = (...args) => inst.check(_size(...args));
|
|
13788
13855
|
});
|
|
13789
|
-
function set(
|
|
13856
|
+
function set(valueType2, params) {
|
|
13790
13857
|
return new ZodSet({
|
|
13791
13858
|
type: "set",
|
|
13792
|
-
valueType,
|
|
13859
|
+
valueType: valueType2,
|
|
13793
13860
|
...util_exports.normalizeParams(params)
|
|
13794
13861
|
});
|
|
13795
13862
|
}
|
|
@@ -14748,6 +14815,7 @@ var RESERVED_SUBDOMAINS = [
|
|
|
14748
14815
|
"docs",
|
|
14749
14816
|
"help",
|
|
14750
14817
|
"imap",
|
|
14818
|
+
"ingest",
|
|
14751
14819
|
"mail",
|
|
14752
14820
|
"mx",
|
|
14753
14821
|
"naro",
|
|
@@ -14827,6 +14895,87 @@ var connectRepositorySchema = external_exports.object({
|
|
|
14827
14895
|
})
|
|
14828
14896
|
});
|
|
14829
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
|
+
|
|
14830
14979
|
// ../../packages/build/src/package-manager.ts
|
|
14831
14980
|
import { access, readFile as readFile2 } from "fs/promises";
|
|
14832
14981
|
import { join as join2 } from "path";
|
|
@@ -14853,20 +15002,35 @@ async function detectPackageManager(root) {
|
|
|
14853
15002
|
if (await exists(join2(root, "pnpm-lock.yaml"))) {
|
|
14854
15003
|
return {
|
|
14855
15004
|
name: "pnpm",
|
|
14856
|
-
install: ["pnpm", "install", "--frozen-lockfile"]
|
|
15005
|
+
install: ["pnpm", "install", "--frozen-lockfile"],
|
|
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]
|
|
14857
15011
|
};
|
|
14858
15012
|
}
|
|
14859
15013
|
if (await exists(join2(root, "package-lock.json"))) {
|
|
14860
|
-
return {
|
|
15014
|
+
return {
|
|
15015
|
+
name: "npm",
|
|
15016
|
+
install: ["npm", "ci"],
|
|
15017
|
+
cacheArgs: (dir) => ["--cache", dir],
|
|
15018
|
+
// --no-save keeps package.json and the lockfile untouched.
|
|
15019
|
+
addArgs: (specs) => ["npm", "install", "--no-save", ...specs]
|
|
15020
|
+
};
|
|
14861
15021
|
}
|
|
14862
15022
|
if (await exists(join2(root, "yarn.lock"))) {
|
|
15023
|
+
const modern = await usesModernYarn(root);
|
|
14863
15024
|
return {
|
|
14864
15025
|
name: "yarn",
|
|
14865
15026
|
install: [
|
|
14866
15027
|
"yarn",
|
|
14867
15028
|
"install",
|
|
14868
|
-
|
|
14869
|
-
]
|
|
15029
|
+
modern ? "--immutable" : "--frozen-lockfile"
|
|
15030
|
+
],
|
|
15031
|
+
// Berry takes no install-time cache flag; YARN_CACHE_FOLDER covers it.
|
|
15032
|
+
cacheArgs: (dir) => modern ? [] : ["--cache-folder", dir],
|
|
15033
|
+
addArgs: (specs) => ["yarn", "add", ...specs]
|
|
14870
15034
|
};
|
|
14871
15035
|
}
|
|
14872
15036
|
throw new DeploymentError(
|
|
@@ -14911,270 +15075,77 @@ async function convertProxyToMiddleware(appRoot) {
|
|
|
14911
15075
|
return { converted: false };
|
|
14912
15076
|
}
|
|
14913
15077
|
|
|
14914
|
-
// ../../packages/build/src/
|
|
14915
|
-
import { access as
|
|
14916
|
-
import {
|
|
14917
|
-
import { join as join4 } from "path";
|
|
14918
|
-
var COMPATIBILITY_DATE = "2026-08-19";
|
|
14919
|
-
var NEXT_CONFIG_FILES = [
|
|
14920
|
-
"next.config.js",
|
|
14921
|
-
"next.config.mjs",
|
|
14922
|
-
"next.config.cjs",
|
|
14923
|
-
"next.config.ts"
|
|
14924
|
-
];
|
|
14925
|
-
async function pathExists(path) {
|
|
14926
|
-
try {
|
|
14927
|
-
await access3(path);
|
|
14928
|
-
return true;
|
|
14929
|
-
} catch {
|
|
14930
|
-
return false;
|
|
14931
|
-
}
|
|
14932
|
-
}
|
|
14933
|
-
async function createPackageManagerConfig(appRoot, writer = async (path, contents) => {
|
|
14934
|
-
await writeFile3(path, contents, "utf8");
|
|
14935
|
-
}) {
|
|
14936
|
-
const packageManagerConfigPath = join4(appRoot, ".next-deploy-user.npmrc");
|
|
14937
|
-
await writer(
|
|
14938
|
-
packageManagerConfigPath,
|
|
14939
|
-
"# Host package-manager credentials are intentionally unavailable.\n"
|
|
14940
|
-
);
|
|
14941
|
-
return packageManagerConfigPath;
|
|
14942
|
-
}
|
|
14943
|
-
async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
|
|
14944
|
-
await writeFile3(path, contents, "utf8");
|
|
14945
|
-
}, cloudflareModulePath = createRequire(import.meta.url).resolve(
|
|
14946
|
-
"@opennextjs/cloudflare"
|
|
14947
|
-
), exists3 = pathExists) {
|
|
14948
|
-
const openNextConfigPath = join4(appRoot, ".next-deploy-open-next.config.mjs");
|
|
14949
|
-
const wranglerConfigPath = join4(appRoot, ".next-deploy-wrangler.json");
|
|
14950
|
-
const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
|
|
14951
|
-
let hasNextConfig = false;
|
|
14952
|
-
for (const name of NEXT_CONFIG_FILES) {
|
|
14953
|
-
if (await exists3(join4(appRoot, name))) {
|
|
14954
|
-
hasNextConfig = true;
|
|
14955
|
-
break;
|
|
14956
|
-
}
|
|
14957
|
-
}
|
|
14958
|
-
if (!hasNextConfig) {
|
|
14959
|
-
await writer(
|
|
14960
|
-
join4(appRoot, "next.config.mjs"),
|
|
14961
|
-
'export default { output: "standalone" };\n'
|
|
14962
|
-
);
|
|
14963
|
-
}
|
|
14964
|
-
await writer(
|
|
14965
|
-
openNextConfigPath,
|
|
14966
|
-
`import { defineCloudflareConfig } from ${JSON.stringify(cloudflareModulePath)};
|
|
14967
|
-
|
|
14968
|
-
export default defineCloudflareConfig({});
|
|
14969
|
-
`
|
|
14970
|
-
);
|
|
14971
|
-
await writer(
|
|
14972
|
-
wranglerConfigPath,
|
|
14973
|
-
`${JSON.stringify(
|
|
14974
|
-
{
|
|
14975
|
-
name: `next-deploy-${safeDeploymentId}`,
|
|
14976
|
-
main: ".open-next/worker.js",
|
|
14977
|
-
compatibility_date: COMPATIBILITY_DATE,
|
|
14978
|
-
compatibility_flags: ["nodejs_compat"],
|
|
14979
|
-
assets: {
|
|
14980
|
-
directory: ".open-next/assets",
|
|
14981
|
-
binding: "ASSETS"
|
|
14982
|
-
}
|
|
14983
|
-
},
|
|
14984
|
-
null,
|
|
14985
|
-
2
|
|
14986
|
-
)}
|
|
14987
|
-
`
|
|
14988
|
-
);
|
|
14989
|
-
const packageManagerConfigPath = await createPackageManagerConfig(
|
|
14990
|
-
appRoot,
|
|
14991
|
-
writer
|
|
14992
|
-
);
|
|
14993
|
-
return {
|
|
14994
|
-
openNextConfigPath,
|
|
14995
|
-
wranglerConfigPath,
|
|
14996
|
-
packageManagerConfigPath
|
|
14997
|
-
};
|
|
14998
|
-
}
|
|
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";
|
|
14999
15081
|
|
|
15000
|
-
// ../../packages/build/src/
|
|
15001
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
15002
|
-
import {
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15011
|
-
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
|
|
15016
|
-
|
|
15017
|
-
|
|
15018
|
-
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15029
|
-
|
|
15030
|
-
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15082
|
+
// ../../packages/build/src/open-next.ts
|
|
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";
|
|
15094
|
+
function parseJsonc(source) {
|
|
15095
|
+
let output = "";
|
|
15096
|
+
let index = 0;
|
|
15097
|
+
let inString = false;
|
|
15098
|
+
let escaped = false;
|
|
15099
|
+
while (index < source.length) {
|
|
15100
|
+
const current = source[index] ?? "";
|
|
15101
|
+
const next = source[index + 1] ?? "";
|
|
15102
|
+
if (inString) {
|
|
15103
|
+
output += current;
|
|
15104
|
+
if (escaped) escaped = false;
|
|
15105
|
+
else if (current === "\\") escaped = true;
|
|
15106
|
+
else if (current === '"') inString = false;
|
|
15107
|
+
index += 1;
|
|
15108
|
+
continue;
|
|
15109
|
+
}
|
|
15110
|
+
if (current === '"') {
|
|
15111
|
+
inString = true;
|
|
15112
|
+
output += current;
|
|
15113
|
+
index += 1;
|
|
15114
|
+
continue;
|
|
15115
|
+
}
|
|
15116
|
+
if (current === "/" && next === "/") {
|
|
15117
|
+
index += 2;
|
|
15118
|
+
while (index < source.length && source[index] !== "\n") index += 1;
|
|
15119
|
+
continue;
|
|
15120
|
+
}
|
|
15121
|
+
if (current === "/" && next === "*") {
|
|
15122
|
+
index += 2;
|
|
15123
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/"))
|
|
15124
|
+
index += 1;
|
|
15125
|
+
index += 2;
|
|
15126
|
+
continue;
|
|
15127
|
+
}
|
|
15128
|
+
output += current;
|
|
15129
|
+
index += 1;
|
|
15039
15130
|
}
|
|
15040
|
-
};
|
|
15041
|
-
var DETECTION_ORDER = ["next", "vite", "cra"];
|
|
15042
|
-
var FRAMEWORK_LABELS = {
|
|
15043
|
-
next: "Next.js",
|
|
15044
|
-
vite: "Vite",
|
|
15045
|
-
cra: "Create React App",
|
|
15046
|
-
static: "static files"
|
|
15047
|
-
};
|
|
15048
|
-
function dependencyVersion(packageJson, name) {
|
|
15049
|
-
const version2 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
|
|
15050
|
-
return typeof version2 === "string" && version2.trim() !== "" ? version2 : null;
|
|
15131
|
+
return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
|
|
15051
15132
|
}
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
try {
|
|
15055
|
-
source = await readFile4(join5(root, "package.json"), "utf8");
|
|
15056
|
-
} catch (error51) {
|
|
15057
|
-
if (error51.code === "ENOENT") return null;
|
|
15133
|
+
function confinedPath(root, configuredPath, label2, base2 = root) {
|
|
15134
|
+
if (isAbsolute(configuredPath)) {
|
|
15058
15135
|
throw new DeploymentError(
|
|
15059
|
-
"
|
|
15060
|
-
|
|
15061
|
-
{ cause: error51 }
|
|
15136
|
+
"ARTIFACT_INVALID",
|
|
15137
|
+
`${label2} must be relative to the application root.`
|
|
15062
15138
|
);
|
|
15063
15139
|
}
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
}
|
|
15140
|
+
const resolved = resolve(base2, configuredPath);
|
|
15141
|
+
const relation = relative(root, resolved);
|
|
15142
|
+
if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) {
|
|
15067
15143
|
throw new DeploymentError(
|
|
15068
|
-
"
|
|
15069
|
-
|
|
15070
|
-
{ cause: error51 }
|
|
15144
|
+
"ARTIFACT_INVALID",
|
|
15145
|
+
`${label2} escapes the application root.`
|
|
15071
15146
|
);
|
|
15072
15147
|
}
|
|
15073
|
-
|
|
15074
|
-
async function detectFramework(root, overrides = {}) {
|
|
15075
|
-
const packageJson = await readPackageJson(root);
|
|
15076
|
-
let preset;
|
|
15077
|
-
if (overrides.framework) {
|
|
15078
|
-
const candidate = FRAMEWORK_PRESETS[overrides.framework] ?? null;
|
|
15079
|
-
if (!candidate || candidate.id !== overrides.framework)
|
|
15080
|
-
throw new DeploymentError(
|
|
15081
|
-
"FRAMEWORK_DETECTION_FAILED",
|
|
15082
|
-
`Unknown framework "${overrides.framework}".`
|
|
15083
|
-
);
|
|
15084
|
-
preset = candidate;
|
|
15085
|
-
} else if (!packageJson) {
|
|
15086
|
-
preset = FRAMEWORK_PRESETS.static;
|
|
15087
|
-
} else {
|
|
15088
|
-
const detected = DETECTION_ORDER.map((id) => FRAMEWORK_PRESETS[id]).find(
|
|
15089
|
-
(candidate) => candidate.detectDependency !== null && dependencyVersion(packageJson, candidate.detectDependency) !== null
|
|
15090
|
-
);
|
|
15091
|
-
if (!detected)
|
|
15092
|
-
throw new DeploymentError(
|
|
15093
|
-
"FRAMEWORK_DETECTION_FAILED",
|
|
15094
|
-
"Could not detect the project framework from package.json. Set a framework (and build command / output directory) in the project settings."
|
|
15095
|
-
);
|
|
15096
|
-
preset = detected;
|
|
15097
|
-
}
|
|
15098
|
-
const buildScript = overrides.buildCommand ?? preset.defaultBuildScript;
|
|
15099
|
-
if (buildScript !== null) {
|
|
15100
|
-
if (!packageJson)
|
|
15101
|
-
throw new DeploymentError(
|
|
15102
|
-
"FRAMEWORK_DETECTION_FAILED",
|
|
15103
|
-
"A build command requires a package.json."
|
|
15104
|
-
);
|
|
15105
|
-
if (typeof packageJson.scripts?.[buildScript] !== "string")
|
|
15106
|
-
throw new DeploymentError(
|
|
15107
|
-
"BUILD_FAILED",
|
|
15108
|
-
`The project must declare a "${buildScript}" script.`
|
|
15109
|
-
);
|
|
15110
|
-
}
|
|
15111
|
-
const version2 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
|
|
15112
|
-
return {
|
|
15113
|
-
preset,
|
|
15114
|
-
buildScript,
|
|
15115
|
-
outputDirectory: overrides.outputDirectory ?? preset.defaultOutputDirectory,
|
|
15116
|
-
description: version2 ? `${FRAMEWORK_LABELS[preset.id]} ${version2}` : FRAMEWORK_LABELS[preset.id]
|
|
15117
|
-
};
|
|
15118
|
-
}
|
|
15119
|
-
|
|
15120
|
-
// ../../packages/build/src/open-next.ts
|
|
15121
|
-
import { access as access4, readdir, readFile as readFile5 } from "fs/promises";
|
|
15122
|
-
import { extname, isAbsolute, join as join6, relative, resolve, sep } from "path";
|
|
15123
|
-
function parseJsonc(source) {
|
|
15124
|
-
let output = "";
|
|
15125
|
-
let index = 0;
|
|
15126
|
-
let inString = false;
|
|
15127
|
-
let escaped = false;
|
|
15128
|
-
while (index < source.length) {
|
|
15129
|
-
const current = source[index] ?? "";
|
|
15130
|
-
const next = source[index + 1] ?? "";
|
|
15131
|
-
if (inString) {
|
|
15132
|
-
output += current;
|
|
15133
|
-
if (escaped) escaped = false;
|
|
15134
|
-
else if (current === "\\") escaped = true;
|
|
15135
|
-
else if (current === '"') inString = false;
|
|
15136
|
-
index += 1;
|
|
15137
|
-
continue;
|
|
15138
|
-
}
|
|
15139
|
-
if (current === '"') {
|
|
15140
|
-
inString = true;
|
|
15141
|
-
output += current;
|
|
15142
|
-
index += 1;
|
|
15143
|
-
continue;
|
|
15144
|
-
}
|
|
15145
|
-
if (current === "/" && next === "/") {
|
|
15146
|
-
index += 2;
|
|
15147
|
-
while (index < source.length && source[index] !== "\n") index += 1;
|
|
15148
|
-
continue;
|
|
15149
|
-
}
|
|
15150
|
-
if (current === "/" && next === "*") {
|
|
15151
|
-
index += 2;
|
|
15152
|
-
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/"))
|
|
15153
|
-
index += 1;
|
|
15154
|
-
index += 2;
|
|
15155
|
-
continue;
|
|
15156
|
-
}
|
|
15157
|
-
output += current;
|
|
15158
|
-
index += 1;
|
|
15159
|
-
}
|
|
15160
|
-
return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
|
|
15161
|
-
}
|
|
15162
|
-
function confinedPath(root, configuredPath, label) {
|
|
15163
|
-
if (isAbsolute(configuredPath)) {
|
|
15164
|
-
throw new DeploymentError(
|
|
15165
|
-
"ARTIFACT_INVALID",
|
|
15166
|
-
`${label} must be relative to the application root.`
|
|
15167
|
-
);
|
|
15168
|
-
}
|
|
15169
|
-
const resolved = resolve(root, configuredPath);
|
|
15170
|
-
const relation = relative(root, resolved);
|
|
15171
|
-
if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) {
|
|
15172
|
-
throw new DeploymentError(
|
|
15173
|
-
"ARTIFACT_INVALID",
|
|
15174
|
-
`${label} escapes the application root.`
|
|
15175
|
-
);
|
|
15176
|
-
}
|
|
15177
|
-
return resolved;
|
|
15148
|
+
return resolved;
|
|
15178
15149
|
}
|
|
15179
15150
|
function contentType(path) {
|
|
15180
15151
|
switch (extname(path).toLowerCase()) {
|
|
@@ -15225,25 +15196,25 @@ function contentType(path) {
|
|
|
15225
15196
|
return "application/octet-stream";
|
|
15226
15197
|
}
|
|
15227
15198
|
}
|
|
15228
|
-
async function
|
|
15199
|
+
async function listArtifactFiles(root, label2 = "OpenNext") {
|
|
15229
15200
|
const files = [];
|
|
15230
15201
|
async function visit(directory) {
|
|
15231
15202
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
15232
15203
|
for (const entry of entries.sort(
|
|
15233
15204
|
(left, right) => left.name.localeCompare(right.name)
|
|
15234
15205
|
)) {
|
|
15235
|
-
const path =
|
|
15206
|
+
const path = join4(directory, entry.name);
|
|
15236
15207
|
if (entry.isSymbolicLink()) {
|
|
15237
15208
|
throw new DeploymentError(
|
|
15238
15209
|
"ARTIFACT_INVALID",
|
|
15239
|
-
|
|
15210
|
+
`${label2} output cannot contain symbolic links.`
|
|
15240
15211
|
);
|
|
15241
15212
|
}
|
|
15242
15213
|
if (entry.isDirectory()) await visit(path);
|
|
15243
15214
|
else if (entry.isFile()) {
|
|
15244
15215
|
files.push({
|
|
15245
15216
|
name: relative(root, path).split(sep).join("/"),
|
|
15246
|
-
data: await
|
|
15217
|
+
data: await readFile4(path),
|
|
15247
15218
|
contentType: contentType(path)
|
|
15248
15219
|
});
|
|
15249
15220
|
}
|
|
@@ -15262,13 +15233,13 @@ async function findWranglerConfig(root, configuredPath) {
|
|
|
15262
15233
|
"Wrangler config escapes the application root."
|
|
15263
15234
|
);
|
|
15264
15235
|
}
|
|
15265
|
-
await
|
|
15236
|
+
await access3(resolved);
|
|
15266
15237
|
return resolved;
|
|
15267
15238
|
}
|
|
15268
15239
|
for (const name of ["wrangler.jsonc", "wrangler.json"]) {
|
|
15269
|
-
const path =
|
|
15240
|
+
const path = join4(root, name);
|
|
15270
15241
|
try {
|
|
15271
|
-
await
|
|
15242
|
+
await access3(path);
|
|
15272
15243
|
return path;
|
|
15273
15244
|
} catch {
|
|
15274
15245
|
}
|
|
@@ -15278,11 +15249,26 @@ async function findWranglerConfig(root, configuredPath) {
|
|
|
15278
15249
|
"A wrangler.jsonc or wrangler.json file is required."
|
|
15279
15250
|
);
|
|
15280
15251
|
}
|
|
15252
|
+
var OPEN_NEXT_LAYOUT = {
|
|
15253
|
+
outputRoot: ".open-next",
|
|
15254
|
+
bundleDir: ".open-next/worker-bundle"
|
|
15255
|
+
};
|
|
15256
|
+
var listFiles = (root) => listArtifactFiles(root);
|
|
15281
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) {
|
|
15282
15265
|
try {
|
|
15283
|
-
const configPath = await findWranglerConfig(
|
|
15266
|
+
const configPath = await findWranglerConfig(
|
|
15267
|
+
appRoot,
|
|
15268
|
+
layout.wranglerConfigPath
|
|
15269
|
+
);
|
|
15284
15270
|
const metadata = parseJsonc(
|
|
15285
|
-
await
|
|
15271
|
+
await readFile4(configPath, "utf8")
|
|
15286
15272
|
);
|
|
15287
15273
|
if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
|
|
15288
15274
|
throw new DeploymentError(
|
|
@@ -15290,43 +15276,50 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
15290
15276
|
"Wrangler metadata must declare main and compatibility_date."
|
|
15291
15277
|
);
|
|
15292
15278
|
}
|
|
15293
|
-
const
|
|
15279
|
+
const configDir = dirname(configPath);
|
|
15280
|
+
const outputRoot = confinedPath(appRoot, layout.outputRoot, "Output root");
|
|
15294
15281
|
const entrypointPath = confinedPath(
|
|
15295
15282
|
appRoot,
|
|
15296
15283
|
metadata.main,
|
|
15297
|
-
"Worker entrypoint"
|
|
15284
|
+
"Worker entrypoint",
|
|
15285
|
+
configDir
|
|
15298
15286
|
);
|
|
15299
15287
|
const entryRelation = relative(outputRoot, entrypointPath);
|
|
15300
15288
|
if (entryRelation === ".." || entryRelation.startsWith(`..${sep}`) || isAbsolute(entryRelation)) {
|
|
15301
15289
|
throw new DeploymentError(
|
|
15302
15290
|
"ARTIFACT_INVALID",
|
|
15303
|
-
|
|
15291
|
+
`Worker entrypoint must be inside ${layout.outputRoot}.`
|
|
15304
15292
|
);
|
|
15305
15293
|
}
|
|
15306
|
-
await
|
|
15307
|
-
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");
|
|
15308
15301
|
const assetsRelation = relative(outputRoot, configuredAssets);
|
|
15309
15302
|
if (assetsRelation === ".." || assetsRelation.startsWith(`..${sep}`) || isAbsolute(assetsRelation)) {
|
|
15310
15303
|
throw new DeploymentError(
|
|
15311
15304
|
"ARTIFACT_INVALID",
|
|
15312
|
-
|
|
15305
|
+
`Assets directory must be inside ${layout.outputRoot}.`
|
|
15313
15306
|
);
|
|
15314
15307
|
}
|
|
15315
|
-
const bundleRoot =
|
|
15308
|
+
const bundleRoot = confinedPath(appRoot, layout.bundleDir, "Bundle dir");
|
|
15316
15309
|
let bundleFiles = null;
|
|
15317
15310
|
try {
|
|
15318
|
-
await
|
|
15311
|
+
await access3(bundleRoot);
|
|
15319
15312
|
bundleFiles = await listFiles(bundleRoot);
|
|
15320
15313
|
} catch {
|
|
15321
15314
|
}
|
|
15322
15315
|
const outputFiles = bundleFiles ?? await listFiles(outputRoot);
|
|
15323
|
-
const entrypointName = bundleFiles ?
|
|
15316
|
+
const entrypointName = bundleFiles ? basename(entrypointPath) : relative(outputRoot, entrypointPath).split(sep).join("/");
|
|
15324
15317
|
const assetsPrefix = `${relative(outputRoot, configuredAssets).split(sep).join("/")}/`;
|
|
15325
15318
|
const entrypoint = outputFiles.find((file2) => file2.name === entrypointName);
|
|
15326
15319
|
if (!entrypoint)
|
|
15327
15320
|
throw new DeploymentError(
|
|
15328
15321
|
"ARTIFACT_INVALID",
|
|
15329
|
-
"
|
|
15322
|
+
"Worker entrypoint is missing from the build output."
|
|
15330
15323
|
);
|
|
15331
15324
|
return {
|
|
15332
15325
|
...identity,
|
|
@@ -15345,245 +15338,2035 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
15345
15338
|
if (error51 instanceof DeploymentError) throw error51;
|
|
15346
15339
|
throw new DeploymentError(
|
|
15347
15340
|
"ARTIFACT_INVALID",
|
|
15348
|
-
"Could not read the
|
|
15341
|
+
"Could not read the Worker build output.",
|
|
15349
15342
|
{ cause: error51 }
|
|
15350
15343
|
);
|
|
15351
15344
|
}
|
|
15352
15345
|
}
|
|
15353
15346
|
|
|
15354
|
-
// ../../packages/build/src/
|
|
15355
|
-
import {
|
|
15356
|
-
import {
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
var
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
// ../../packages/build/src/process.ts
|
|
15365
|
-
var CommandExecutionError = class extends DeploymentError {
|
|
15366
|
-
exitCode;
|
|
15367
|
-
timedOut;
|
|
15368
|
-
constructor(code, message, options) {
|
|
15369
|
-
super(
|
|
15370
|
-
code,
|
|
15371
|
-
message,
|
|
15372
|
-
options.cause === void 0 ? void 0 : { cause: options.cause }
|
|
15373
|
-
);
|
|
15374
|
-
this.name = "CommandExecutionError";
|
|
15375
|
-
this.exitCode = options.exitCode;
|
|
15376
|
-
this.timedOut = options.timedOut;
|
|
15377
|
-
}
|
|
15378
|
-
};
|
|
15379
|
-
var PASSTHROUGH_ENVIRONMENT = [
|
|
15380
|
-
"PATH",
|
|
15381
|
-
"HOME",
|
|
15382
|
-
"TMPDIR",
|
|
15383
|
-
"TMP",
|
|
15384
|
-
"TEMP",
|
|
15385
|
-
"LANG",
|
|
15386
|
-
"LC_ALL",
|
|
15387
|
-
"CI",
|
|
15388
|
-
"COREPACK_HOME",
|
|
15389
|
-
"XDG_CACHE_HOME"
|
|
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"
|
|
15390
15357
|
];
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
}
|
|
15396
|
-
|
|
15397
|
-
const value = process.env[name];
|
|
15398
|
-
if (value !== void 0) environment[name] = value;
|
|
15358
|
+
async function pathExists(path) {
|
|
15359
|
+
try {
|
|
15360
|
+
await access4(path);
|
|
15361
|
+
return true;
|
|
15362
|
+
} catch {
|
|
15363
|
+
return false;
|
|
15399
15364
|
}
|
|
15400
|
-
return { ...environment, ...overrides };
|
|
15401
15365
|
}
|
|
15402
|
-
async function
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15425
|
-
const outputComplete = Promise.all([
|
|
15426
|
-
consume(child.stdout, "INFO"),
|
|
15427
|
-
consume(child.stderr, "ERROR")
|
|
15428
|
-
]);
|
|
15429
|
-
const kill = (signal) => {
|
|
15430
|
-
if (detached && child.pid) {
|
|
15431
|
-
try {
|
|
15432
|
-
process.kill(-child.pid, signal);
|
|
15433
|
-
return;
|
|
15434
|
-
} catch {
|
|
15435
|
-
}
|
|
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;
|
|
15436
15389
|
}
|
|
15437
|
-
child.kill(signal);
|
|
15438
|
-
};
|
|
15439
|
-
let forceKill;
|
|
15440
|
-
const timeout = setTimeout(() => {
|
|
15441
|
-
timedOut = true;
|
|
15442
|
-
kill("SIGTERM");
|
|
15443
|
-
forceKill = setTimeout(() => kill("SIGKILL"), 2e3);
|
|
15444
|
-
forceKill.unref();
|
|
15445
|
-
}, spec.timeoutMs);
|
|
15446
|
-
const outcome = await new Promise((resolveOutcome) => {
|
|
15447
|
-
child.once("error", (error51) => resolveOutcome({ type: "error", error: error51 }));
|
|
15448
|
-
child.once(
|
|
15449
|
-
"close",
|
|
15450
|
-
(exitCode, signal) => resolveOutcome({ type: "close", exitCode, signal })
|
|
15451
|
-
);
|
|
15452
|
-
});
|
|
15453
|
-
clearTimeout(timeout);
|
|
15454
|
-
if (forceKill) clearTimeout(forceKill);
|
|
15455
|
-
await outputComplete;
|
|
15456
|
-
if (outcome.type === "error") {
|
|
15457
|
-
throw new CommandExecutionError(
|
|
15458
|
-
spec.errorCode,
|
|
15459
|
-
`Could not start ${spec.command}`,
|
|
15460
|
-
{
|
|
15461
|
-
exitCode: null,
|
|
15462
|
-
timedOut,
|
|
15463
|
-
cause: outcome.error
|
|
15464
|
-
}
|
|
15465
|
-
);
|
|
15466
|
-
}
|
|
15467
|
-
if (timedOut) {
|
|
15468
|
-
throw new CommandExecutionError(
|
|
15469
|
-
spec.errorCode,
|
|
15470
|
-
`${spec.command} exceeded its ${spec.timeoutMs}ms time limit`,
|
|
15471
|
-
{ exitCode: outcome.exitCode, timedOut: true }
|
|
15472
|
-
);
|
|
15473
15390
|
}
|
|
15474
|
-
if (
|
|
15475
|
-
|
|
15476
|
-
|
|
15477
|
-
|
|
15478
|
-
{ exitCode: outcome.exitCode, timedOut: false }
|
|
15391
|
+
if (!hasNextConfig) {
|
|
15392
|
+
await writer(
|
|
15393
|
+
join5(appRoot, "next.config.mjs"),
|
|
15394
|
+
'export default { output: "standalone" };\n'
|
|
15479
15395
|
);
|
|
15480
15396
|
}
|
|
15481
|
-
|
|
15482
|
-
|
|
15397
|
+
await writer(
|
|
15398
|
+
openNextConfigPath,
|
|
15399
|
+
`import { defineCloudflareConfig } from ${JSON.stringify(cloudflareModulePath)};
|
|
15483
15400
|
|
|
15484
|
-
|
|
15485
|
-
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
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;
|
|
15491
15462
|
}
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15495
|
-
|
|
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
|
+
}
|
|
15496
15474
|
}
|
|
15497
|
-
|
|
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
|
+
};
|
|
15498
15534
|
}
|
|
15499
|
-
|
|
15500
|
-
|
|
15501
|
-
|
|
15502
|
-
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
|
|
15506
|
-
|
|
15507
|
-
|
|
15508
|
-
|
|
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
|
+
};
|
|
15509
15559
|
}
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
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")
|
|
15515
15603
|
);
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
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;
|
|
15519
15636
|
throw new DeploymentError(
|
|
15520
15637
|
"ARTIFACT_INVALID",
|
|
15521
|
-
"
|
|
15638
|
+
"Could not read the vinext deployment output.",
|
|
15639
|
+
{ cause: error51 }
|
|
15522
15640
|
);
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
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;
|
|
15544
15717
|
}
|
|
15545
15718
|
}
|
|
15719
|
+
return false;
|
|
15720
|
+
}
|
|
15721
|
+
async function readPackageJson(root) {
|
|
15722
|
+
let source;
|
|
15546
15723
|
try {
|
|
15547
|
-
await
|
|
15724
|
+
source = await readFile6(join7(root, "package.json"), "utf8");
|
|
15548
15725
|
} catch (error51) {
|
|
15549
|
-
if (error51
|
|
15726
|
+
if (error51.code === "ENOENT") return null;
|
|
15550
15727
|
throw new DeploymentError(
|
|
15551
|
-
"
|
|
15552
|
-
|
|
15728
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15729
|
+
"Could not read package.json.",
|
|
15553
15730
|
{ cause: error51 }
|
|
15554
15731
|
);
|
|
15555
15732
|
}
|
|
15556
|
-
|
|
15733
|
+
try {
|
|
15734
|
+
return JSON.parse(source);
|
|
15735
|
+
} catch (error51) {
|
|
15557
15736
|
throw new DeploymentError(
|
|
15558
|
-
"
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
if (!assets.some((file2) => file2.name === "index.html"))
|
|
15562
|
-
await onWarn?.(
|
|
15563
|
-
`No index.html in "${outputDirectory}" \u2014 the site root will not serve a page.`
|
|
15737
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15738
|
+
"The selected root does not contain a valid package.json.",
|
|
15739
|
+
{ cause: error51 }
|
|
15564
15740
|
);
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
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(
|
|
15818
|
+
code,
|
|
15819
|
+
message,
|
|
15820
|
+
options.cause === void 0 ? void 0 : { cause: options.cause }
|
|
15821
|
+
);
|
|
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",
|
|
15577
16099
|
assetsConfig: {
|
|
15578
16100
|
notFoundHandling: options.spaFallback ? "single-page-application" : "404-page"
|
|
15579
16101
|
}
|
|
15580
16102
|
};
|
|
15581
16103
|
}
|
|
15582
16104
|
|
|
16105
|
+
// ../../packages/build/src/strategy/fingerprint.ts
|
|
16106
|
+
import { createHash as createHash3 } from "crypto";
|
|
16107
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
16108
|
+
import { join as join9, relative as relative4, sep as sep3 } from "path";
|
|
16109
|
+
var APP_ROOT_FILES = [
|
|
16110
|
+
"package.json",
|
|
16111
|
+
"next.config.js",
|
|
16112
|
+
"next.config.mjs",
|
|
16113
|
+
"next.config.cjs",
|
|
16114
|
+
"next.config.ts",
|
|
16115
|
+
"vite.config.ts",
|
|
16116
|
+
"vite.config.mts",
|
|
16117
|
+
"vite.config.js",
|
|
16118
|
+
"vite.config.mjs",
|
|
16119
|
+
"open-next.config.ts",
|
|
16120
|
+
"open-next.config.js",
|
|
16121
|
+
"open-next.config.mjs",
|
|
16122
|
+
"wrangler.jsonc",
|
|
16123
|
+
"wrangler.json",
|
|
16124
|
+
"wrangler.toml",
|
|
16125
|
+
"vinext.config.ts",
|
|
16126
|
+
"vinext.config.js",
|
|
16127
|
+
"vinext.config.mjs",
|
|
16128
|
+
".vinextrc"
|
|
16129
|
+
];
|
|
16130
|
+
var SOURCE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mjs", "mts"];
|
|
16131
|
+
var SOURCE_FILES = ["middleware", "proxy", "src/middleware", "src/proxy"];
|
|
16132
|
+
var CHECKOUT_FILES = [
|
|
16133
|
+
"package.json",
|
|
16134
|
+
"pnpm-lock.yaml",
|
|
16135
|
+
"package-lock.json",
|
|
16136
|
+
"yarn.lock",
|
|
16137
|
+
"pnpm-workspace.yaml",
|
|
16138
|
+
".npmrc"
|
|
16139
|
+
];
|
|
16140
|
+
function sha256(data) {
|
|
16141
|
+
return createHash3("sha256").update(data).digest("hex");
|
|
16142
|
+
}
|
|
16143
|
+
async function hashFile(path) {
|
|
16144
|
+
try {
|
|
16145
|
+
return sha256(await readFile8(path));
|
|
16146
|
+
} catch {
|
|
16147
|
+
return null;
|
|
16148
|
+
}
|
|
16149
|
+
}
|
|
16150
|
+
async function hashStrategyInputs(checkout, appRoot) {
|
|
16151
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
16152
|
+
for (const name of CHECKOUT_FILES) candidates.add(join9(checkout, name));
|
|
16153
|
+
for (const name of APP_ROOT_FILES) candidates.add(join9(appRoot, name));
|
|
16154
|
+
for (const base2 of SOURCE_FILES) {
|
|
16155
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
16156
|
+
candidates.add(join9(appRoot, `${base2}.${ext}`));
|
|
16157
|
+
}
|
|
16158
|
+
}
|
|
16159
|
+
const entries = [];
|
|
16160
|
+
for (const path of candidates) {
|
|
16161
|
+
const digest = await hashFile(path);
|
|
16162
|
+
if (digest === null) continue;
|
|
16163
|
+
entries.push([relative4(checkout, path).split(sep3).join("/"), digest]);
|
|
16164
|
+
}
|
|
16165
|
+
return entries.toSorted(
|
|
16166
|
+
([left], [right]) => left < right ? -1 : left > right ? 1 : 0
|
|
16167
|
+
);
|
|
16168
|
+
}
|
|
16169
|
+
async function computeStrategyFingerprint(input) {
|
|
16170
|
+
const entries = input.entries ?? await hashStrategyInputs(input.checkout, input.appRoot);
|
|
16171
|
+
const lines = [
|
|
16172
|
+
`next=${input.nextVersion}`,
|
|
16173
|
+
`root=${input.rootDirectory ?? ""}`,
|
|
16174
|
+
`vinext=${input.vinextEnabled ? "on" : "off"}`,
|
|
16175
|
+
...entries.map(([name, digest]) => `${name}\0${digest}`)
|
|
16176
|
+
];
|
|
16177
|
+
return {
|
|
16178
|
+
hash: sha256(lines.join("\n")),
|
|
16179
|
+
inputs: entries.map(([name]) => name)
|
|
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"
|
|
16193
|
+
];
|
|
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
|
|
16265
|
+
};
|
|
16266
|
+
}
|
|
16267
|
+
|
|
16268
|
+
// ../../packages/build/src/strategy/resolver.ts
|
|
16269
|
+
function label(strategy) {
|
|
16270
|
+
return `P${String(strategy.priority)} ${strategy.id}`;
|
|
16271
|
+
}
|
|
16272
|
+
function describeOutcome(outcome) {
|
|
16273
|
+
switch (outcome.status) {
|
|
16274
|
+
case "success":
|
|
16275
|
+
return "succeeded";
|
|
16276
|
+
case "failed":
|
|
16277
|
+
return `failed at ${outcome.stage}: ${outcome.reason}`;
|
|
16278
|
+
case "skipped":
|
|
16279
|
+
return `skipped: ${outcome.reason}`;
|
|
16280
|
+
}
|
|
16281
|
+
}
|
|
16282
|
+
async function evaluateOne(strategy, context, observer) {
|
|
16283
|
+
let outcome;
|
|
16284
|
+
try {
|
|
16285
|
+
outcome = await strategy.evaluate(context, observer);
|
|
16286
|
+
} catch (error51) {
|
|
16287
|
+
outcome = {
|
|
16288
|
+
status: "failed",
|
|
16289
|
+
stage: "build",
|
|
16290
|
+
reason: error51 instanceof Error ? error51.message : String(error51)
|
|
16291
|
+
};
|
|
16292
|
+
}
|
|
16293
|
+
await observer.onLog({
|
|
16294
|
+
level: outcome.status === "success" ? "INFO" : "WARN",
|
|
16295
|
+
stage: "BUILDING",
|
|
16296
|
+
message: `${label(strategy)} ${describeOutcome(outcome)}.`
|
|
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");
|
|
16851
|
+
}
|
|
16852
|
+
function createOpenNextAwsStrategy() {
|
|
16853
|
+
return {
|
|
16854
|
+
id: "opennext-aws",
|
|
16855
|
+
priority: 5,
|
|
16856
|
+
target: "aws",
|
|
16857
|
+
supports(context) {
|
|
16858
|
+
if (!context.targets.aws)
|
|
16859
|
+
return {
|
|
16860
|
+
supported: false,
|
|
16861
|
+
reason: "No AWS deploy target is configured on this control plane (no provider, credentials, or routing exist for it)."
|
|
16862
|
+
};
|
|
16863
|
+
if (!context.toolchain.openNextAws)
|
|
16864
|
+
return {
|
|
16865
|
+
supported: false,
|
|
16866
|
+
reason: "The @opennextjs/aws toolchain is not installed on this build runner."
|
|
16867
|
+
};
|
|
16868
|
+
return { supported: true };
|
|
16869
|
+
},
|
|
16870
|
+
async evaluate(context, observer) {
|
|
16871
|
+
await info(
|
|
16872
|
+
observer,
|
|
16873
|
+
"P5 opennext-aws: building with the OpenNext AWS adapter."
|
|
16874
|
+
);
|
|
16875
|
+
let fork;
|
|
16876
|
+
try {
|
|
16877
|
+
fork = await context.workspace.fork("opennext-aws");
|
|
16878
|
+
} catch (error51) {
|
|
16879
|
+
return failed("prepare", describeError(error51));
|
|
16880
|
+
}
|
|
16881
|
+
const configPath = join12(fork.appRoot, CONFIG_NAME);
|
|
16882
|
+
try {
|
|
16883
|
+
await writeFile6(
|
|
16884
|
+
configPath,
|
|
16885
|
+
smokeConfig(
|
|
16886
|
+
`${context.packageManager.name} run ${context.buildScript}`
|
|
16887
|
+
),
|
|
16888
|
+
"utf8"
|
|
16889
|
+
);
|
|
16890
|
+
} catch (error51) {
|
|
16891
|
+
return failed("prepare", describeError(error51));
|
|
16892
|
+
}
|
|
16893
|
+
const build = await runStep(
|
|
16894
|
+
context,
|
|
16895
|
+
{
|
|
16896
|
+
command: context.toolchain.openNextAws ?? "open-next",
|
|
16897
|
+
args: ["build", "--config-path", configPath],
|
|
16898
|
+
cwd: fork.appRoot,
|
|
16899
|
+
env: {
|
|
16900
|
+
...context.env,
|
|
16901
|
+
NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
|
|
16902
|
+
},
|
|
16903
|
+
timeoutMs: 20 * 6e4,
|
|
16904
|
+
stage: "BUILDING",
|
|
16905
|
+
errorCode: "BUILD_FAILED"
|
|
16906
|
+
},
|
|
16907
|
+
observer
|
|
16908
|
+
);
|
|
16909
|
+
if (!build.ok) return failed("build", describeFailure(build));
|
|
16910
|
+
const serverFunction = join12(fork.appRoot, SERVER_FUNCTION);
|
|
16911
|
+
if (!await pathExists2(serverFunction))
|
|
16912
|
+
return failed("bundle", `OpenNext AWS produced no ${SERVER_FUNCTION}.`);
|
|
16913
|
+
const smoke = await context.ports.smoke.run(
|
|
16914
|
+
{
|
|
16915
|
+
boot: (port) => ({
|
|
16916
|
+
command: process.execPath,
|
|
16917
|
+
args: [serverFunction],
|
|
16918
|
+
cwd: fork.appRoot,
|
|
16919
|
+
env: { ...context.env, PORT: String(port), NODE_ENV: "production" }
|
|
16920
|
+
}),
|
|
16921
|
+
probes: defaultSmokeProbes(context.facts.matcherRoutes),
|
|
16922
|
+
stage: "BUILDING",
|
|
16923
|
+
errorCode: "BUILD_FAILED"
|
|
16924
|
+
},
|
|
16925
|
+
observer
|
|
16926
|
+
);
|
|
16927
|
+
if (!smoke.ok) return failed("smoke", smoke.reason);
|
|
16928
|
+
return {
|
|
16929
|
+
status: "success",
|
|
16930
|
+
artifact: {
|
|
16931
|
+
kind: "opennext-aws",
|
|
16932
|
+
outputRoot: join12(fork.appRoot, ".open-next"),
|
|
16933
|
+
serverFunction
|
|
16934
|
+
},
|
|
16935
|
+
notes: [
|
|
16936
|
+
"Verified with the Node wrapper; an AWS deploy needs a Lambda-wrapper build against provisioned resources."
|
|
16937
|
+
]
|
|
16938
|
+
};
|
|
16939
|
+
}
|
|
16940
|
+
};
|
|
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
|
+
)
|
|
16955
|
+
);
|
|
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,
|
|
16978
|
+
{
|
|
16979
|
+
command: context.packageManager.name,
|
|
16980
|
+
args: ["run", context.buildScript],
|
|
16981
|
+
cwd: roots.appRoot,
|
|
16982
|
+
env: {
|
|
16983
|
+
...context.env,
|
|
16984
|
+
NEXT_PRIVATE_STANDALONE: "true",
|
|
16985
|
+
NPM_CONFIG_USERCONFIG: platform.packageManagerConfigPath
|
|
16986
|
+
},
|
|
16987
|
+
timeoutMs: 15 * 6e4,
|
|
16988
|
+
stage: "BUILDING",
|
|
16989
|
+
errorCode: "BUILD_FAILED"
|
|
16990
|
+
},
|
|
16991
|
+
observer
|
|
16992
|
+
);
|
|
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
|
|
17019
|
+
);
|
|
17020
|
+
if (!openNext.ok) {
|
|
17021
|
+
const nodeMiddleware = openNext.output.some((line) => NODE_MIDDLEWARE_REFUSAL.test(line)) || await buildDeclaresNodeMiddleware(roots.appRoot);
|
|
17022
|
+
return {
|
|
17023
|
+
ok: false,
|
|
17024
|
+
nodeMiddleware,
|
|
17025
|
+
outcome: failed(
|
|
17026
|
+
"build",
|
|
17027
|
+
nodeMiddleware ? "OpenNext Cloudflare does not support Node.js middleware (Next.js 16 proxy.ts)." : describeFailure(openNext)
|
|
17028
|
+
)
|
|
17029
|
+
};
|
|
17030
|
+
}
|
|
17031
|
+
const layout = {
|
|
17032
|
+
appRoot: roots.appRoot,
|
|
17033
|
+
wranglerConfigPath: platform.wranglerConfigPath,
|
|
17034
|
+
...OPEN_NEXT_LAYOUT
|
|
17035
|
+
};
|
|
17036
|
+
const bundle = await bundleWorker(
|
|
17037
|
+
context,
|
|
17038
|
+
layout,
|
|
17039
|
+
observer,
|
|
17040
|
+
"OPENNEXT_FAILED"
|
|
17041
|
+
);
|
|
17042
|
+
if (!bundle.ok) {
|
|
17043
|
+
return {
|
|
17044
|
+
ok: false,
|
|
17045
|
+
nodeMiddleware: false,
|
|
17046
|
+
outcome: failed("bundle", describeFailure(bundle))
|
|
17047
|
+
};
|
|
17048
|
+
}
|
|
17049
|
+
let artifact;
|
|
17050
|
+
try {
|
|
17051
|
+
artifact = await options.readOpenNextArtifact(
|
|
17052
|
+
roots.appRoot,
|
|
17053
|
+
context.identity,
|
|
17054
|
+
platform.wranglerConfigPath
|
|
17055
|
+
);
|
|
17056
|
+
} catch (error51) {
|
|
17057
|
+
return {
|
|
17058
|
+
ok: false,
|
|
17059
|
+
nodeMiddleware: false,
|
|
17060
|
+
outcome: failed("bundle", describeError(error51))
|
|
17061
|
+
};
|
|
17062
|
+
}
|
|
17063
|
+
const smoke = await smokeWorker(context, layout, observer);
|
|
17064
|
+
if (!smoke.ok) {
|
|
17065
|
+
return {
|
|
17066
|
+
ok: false,
|
|
17067
|
+
nodeMiddleware: false,
|
|
17068
|
+
outcome: failed("smoke", smoke.reason)
|
|
17069
|
+
};
|
|
17070
|
+
}
|
|
17071
|
+
return { ok: true, artifact };
|
|
17072
|
+
}
|
|
17073
|
+
return {
|
|
17074
|
+
id: "opennext-cloudflare",
|
|
17075
|
+
priority: 2,
|
|
17076
|
+
target: "cloudflare",
|
|
17077
|
+
supports: () => ({ supported: true }),
|
|
17078
|
+
async evaluate(context, observer) {
|
|
17079
|
+
const rememberedRewrite = context.pinnedVariant === PROXY_TO_MIDDLEWARE_VARIANT && context.facts.proxyFile !== null;
|
|
17080
|
+
if (rememberedRewrite) {
|
|
17081
|
+
await info(
|
|
17082
|
+
observer,
|
|
17083
|
+
`P2 opennext-cloudflare: the stored strategy already needed ${context.facts.proxyFile ?? "proxy"} rewritten to Edge middleware; building that way directly.`
|
|
17084
|
+
);
|
|
17085
|
+
} else {
|
|
17086
|
+
await info(
|
|
17087
|
+
observer,
|
|
17088
|
+
"P2 opennext-cloudflare: building the project as written."
|
|
17089
|
+
);
|
|
17090
|
+
const first = await attempt(context, context.workspace, observer);
|
|
17091
|
+
if (first.ok) {
|
|
17092
|
+
return {
|
|
17093
|
+
status: "success",
|
|
17094
|
+
artifact: { kind: "cloudflare-worker", artifact: first.artifact },
|
|
17095
|
+
notes: []
|
|
17096
|
+
};
|
|
17097
|
+
}
|
|
17098
|
+
if (!first.nodeMiddleware || !context.facts.proxyFile)
|
|
17099
|
+
return first.outcome;
|
|
17100
|
+
await warn(
|
|
17101
|
+
observer,
|
|
17102
|
+
`P2 opennext-cloudflare: OpenNext refused the Node.js middleware compiled from ${context.facts.proxyFile}. Retrying in a temporary workspace with it rewritten to Edge middleware.`
|
|
17103
|
+
);
|
|
17104
|
+
}
|
|
17105
|
+
let fork;
|
|
17106
|
+
try {
|
|
17107
|
+
fork = await context.workspace.fork("opennext-proxy-to-middleware");
|
|
17108
|
+
} catch (error51) {
|
|
17109
|
+
return failed("prepare", describeError(error51));
|
|
17110
|
+
}
|
|
17111
|
+
const conversion = await options.convertProxyToMiddleware(fork.appRoot);
|
|
17112
|
+
if (!conversion.converted) {
|
|
17113
|
+
return failed(
|
|
17114
|
+
"prepare",
|
|
17115
|
+
`Could not rewrite ${context.facts.proxyFile} to middleware (a middleware file already exists).`
|
|
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
|
+
};
|
|
17134
|
+
}
|
|
17135
|
+
|
|
17136
|
+
// ../../packages/build/src/strategy/strategies/unavailable.ts
|
|
17137
|
+
function createUnavailableStrategy(options) {
|
|
17138
|
+
return {
|
|
17139
|
+
id: options.id,
|
|
17140
|
+
priority: options.priority,
|
|
17141
|
+
target: options.target,
|
|
17142
|
+
supports: () => ({ supported: false, reason: options.reason }),
|
|
17143
|
+
evaluate: async () => ({ status: "skipped", reason: options.reason })
|
|
17144
|
+
};
|
|
17145
|
+
}
|
|
17146
|
+
function createSplitCloudflareStrategy() {
|
|
17147
|
+
return createUnavailableStrategy({
|
|
17148
|
+
id: "split-cloudflare",
|
|
17149
|
+
priority: 3,
|
|
17150
|
+
target: "cloudflare-split",
|
|
17151
|
+
reason: "Split Cloudflare deployment needs the dispatcher to chain a middleware Worker in front of the app Worker; the runtime plane routes one Worker per hostname today."
|
|
17152
|
+
});
|
|
17153
|
+
}
|
|
17154
|
+
function createAwsMiddlewareCloudflareAppStrategy() {
|
|
17155
|
+
return createUnavailableStrategy({
|
|
17156
|
+
id: "aws-middleware-cloudflare-app",
|
|
17157
|
+
priority: 4,
|
|
17158
|
+
target: "aws-hybrid",
|
|
17159
|
+
reason: "Hybrid AWS middleware + Cloudflare app needs an AWS deploy target and cross-cloud request chaining; neither exists on this control plane."
|
|
17160
|
+
});
|
|
17161
|
+
}
|
|
17162
|
+
|
|
17163
|
+
// ../../packages/build/src/strategy/strategies/vinext-cloudflare.ts
|
|
17164
|
+
import { join as join14 } from "path";
|
|
17165
|
+
var VINEXT_MIN_NEXT_MAJOR = 16;
|
|
17166
|
+
var VINEXT_BUILD_SCRIPTS = ["esbuild", "workerd"];
|
|
17167
|
+
var VINEXT_OUTPUT = {
|
|
17168
|
+
outputRoot: "dist",
|
|
17169
|
+
bundleDir: "dist/.naro-bundle"
|
|
17170
|
+
};
|
|
17171
|
+
async function runCheck(context, observer) {
|
|
17172
|
+
if (!context.toolchain.vinext) return null;
|
|
17173
|
+
const held = [];
|
|
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
|
+
}
|
|
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);
|
|
17193
|
+
}
|
|
17194
|
+
return report;
|
|
17195
|
+
} catch (error51) {
|
|
17196
|
+
for (const log2 of held) await observer.onLog(log2);
|
|
17197
|
+
await observer.onLog({
|
|
17198
|
+
level: "WARN",
|
|
17199
|
+
stage: "BUILDING",
|
|
17200
|
+
message: `vinext check could not run: ${describeError(error51)}`
|
|
17201
|
+
});
|
|
17202
|
+
return null;
|
|
17203
|
+
}
|
|
17204
|
+
}
|
|
17205
|
+
function createVinextCloudflareStrategy(options) {
|
|
17206
|
+
const toolchain = options.toolchain ?? VINEXT_TOOLCHAIN;
|
|
17207
|
+
return {
|
|
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 };
|
|
17228
|
+
},
|
|
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
|
+
};
|
|
17337
|
+
}
|
|
17338
|
+
};
|
|
17339
|
+
}
|
|
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
|
+
|
|
15583
17362
|
// ../../packages/build/src/local-build-executor.ts
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
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 }),
|
|
15587
17370
|
runCommand,
|
|
15588
17371
|
detectPackageManager,
|
|
15589
17372
|
resolveAppRoot,
|
|
@@ -15595,8 +17378,65 @@ var defaultDependencies = {
|
|
|
15595
17378
|
openNextCommand: "opennextjs-cloudflare",
|
|
15596
17379
|
wranglerCommand: "wrangler",
|
|
15597
17380
|
cacheFetch: (input, init) => fetch(input, init),
|
|
15598
|
-
convertProxyToMiddleware
|
|
17381
|
+
convertProxyToMiddleware,
|
|
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()
|
|
15599
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
|
+
}
|
|
17414
|
+
async function hasCommand(command) {
|
|
17415
|
+
try {
|
|
17416
|
+
await runCommand(
|
|
17417
|
+
{
|
|
17418
|
+
command,
|
|
17419
|
+
args: ["--version"],
|
|
17420
|
+
cwd: tmpdir(),
|
|
17421
|
+
timeoutMs: 1e4,
|
|
17422
|
+
stage: "CLONING",
|
|
17423
|
+
errorCode: "CLONE_FAILED"
|
|
17424
|
+
},
|
|
17425
|
+
{ onLog: () => void 0 }
|
|
17426
|
+
);
|
|
17427
|
+
return true;
|
|
17428
|
+
} catch {
|
|
17429
|
+
return false;
|
|
17430
|
+
}
|
|
17431
|
+
}
|
|
17432
|
+
async function pathExists3(path) {
|
|
17433
|
+
try {
|
|
17434
|
+
await access8(path);
|
|
17435
|
+
return true;
|
|
17436
|
+
} catch {
|
|
17437
|
+
return false;
|
|
17438
|
+
}
|
|
17439
|
+
}
|
|
15600
17440
|
function sanitizeCacheKey(value) {
|
|
15601
17441
|
return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 128);
|
|
15602
17442
|
}
|
|
@@ -15626,26 +17466,54 @@ async function trySave(scope, key, sourceDir, ports, observer) {
|
|
|
15626
17466
|
async function notifyStatus(observer, status) {
|
|
15627
17467
|
await observer.onStatus?.(status);
|
|
15628
17468
|
}
|
|
15629
|
-
async function
|
|
17469
|
+
async function info2(observer, stage, message) {
|
|
15630
17470
|
await observer.onLog({ level: "INFO", stage, message });
|
|
15631
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
|
+
}
|
|
15632
17488
|
var LocalBuildExecutor = class {
|
|
15633
|
-
constructor(dependencies =
|
|
17489
|
+
constructor(dependencies = defaultLocalBuildDependencies) {
|
|
15634
17490
|
this.dependencies = dependencies;
|
|
15635
17491
|
}
|
|
15636
17492
|
async build(input, observer, cache) {
|
|
15637
17493
|
const workspace = await this.dependencies.createWorkspace();
|
|
15638
|
-
const checkout =
|
|
15639
|
-
const pmCacheDir =
|
|
17494
|
+
const checkout = join16(workspace, "checkout");
|
|
17495
|
+
const pmCacheDir = join16(workspace, ".pm-store");
|
|
15640
17496
|
let cacheSeq = 0;
|
|
15641
|
-
const
|
|
17497
|
+
const commandAvailable = this.dependencies.commandAvailable ?? hasCommand;
|
|
17498
|
+
const compressor = "zstd";
|
|
17499
|
+
const canCompress = cache ? await commandAvailable(compressor) : false;
|
|
17500
|
+
const cachePorts = cache && canCompress ? {
|
|
15642
17501
|
transport: cache,
|
|
15643
17502
|
fetch: this.dependencies.cacheFetch ?? ((url2, init) => fetch(url2, init)),
|
|
15644
17503
|
runCommand: this.dependencies.runCommand,
|
|
15645
|
-
tmpFile: (suffix) =>
|
|
17504
|
+
tmpFile: (suffix) => join16(workspace, `cache-${String(cacheSeq++)}${suffix}`),
|
|
17505
|
+
// One latch for the whole build so a disabled cache warns once.
|
|
17506
|
+
report: { unavailable: false }
|
|
15646
17507
|
} : null;
|
|
15647
17508
|
try {
|
|
15648
17509
|
await notifyStatus(observer, "CLONING");
|
|
17510
|
+
if (cache && !canCompress) {
|
|
17511
|
+
await observer.onLog({
|
|
17512
|
+
level: "WARN",
|
|
17513
|
+
stage: "CLONING",
|
|
17514
|
+
message: `Build cache disabled: ${compressor} is not installed. Install it to reuse dependencies between deploys.`
|
|
17515
|
+
});
|
|
17516
|
+
}
|
|
15649
17517
|
await this.dependencies.runCommand(
|
|
15650
17518
|
{
|
|
15651
17519
|
command: "git",
|
|
@@ -15710,27 +17578,31 @@ var LocalBuildExecutor = class {
|
|
|
15710
17578
|
buildCommand: input.buildCommand ?? null,
|
|
15711
17579
|
outputDirectory: input.outputDirectory ?? null
|
|
15712
17580
|
});
|
|
15713
|
-
await
|
|
17581
|
+
await info2(observer, "CLONING", `Detected ${resolved.description}`);
|
|
15714
17582
|
const isNext = resolved.preset.id === "next";
|
|
15715
|
-
|
|
15716
|
-
|
|
15717
|
-
|
|
15718
|
-
|
|
15719
|
-
|
|
15720
|
-
|
|
15721
|
-
|
|
15722
|
-
stage: "CLONING",
|
|
15723
|
-
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.`
|
|
15724
|
-
});
|
|
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
|
+
);
|
|
15725
17590
|
}
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
);
|
|
15730
|
-
packageManagerConfigPath = platformConfig.packageManagerConfigPath;
|
|
15731
|
-
} else if (resolved.buildScript !== null) {
|
|
17591
|
+
}
|
|
17592
|
+
let packageManagerConfigPath = null;
|
|
17593
|
+
if (resolved.buildScript !== null) {
|
|
15732
17594
|
packageManagerConfigPath = await this.dependencies.createPackageManagerConfig(appRoot);
|
|
15733
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
|
+
};
|
|
15734
17606
|
if (resolved.buildScript !== null) {
|
|
15735
17607
|
await notifyStatus(observer, "INSTALLING");
|
|
15736
17608
|
const packageManager = await this.dependencies.detectPackageManager(checkout);
|
|
@@ -15754,28 +17626,57 @@ var LocalBuildExecutor = class {
|
|
|
15754
17626
|
);
|
|
15755
17627
|
}
|
|
15756
17628
|
}
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
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"
|
|
15770
17647
|
},
|
|
15771
|
-
|
|
15772
|
-
|
|
15773
|
-
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
|
|
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
|
+
}
|
|
15777
17678
|
await notifyStatus(observer, "BUILDING");
|
|
15778
|
-
const buildCacheDir = resolved.preset.cachePath ?
|
|
17679
|
+
const buildCacheDir = resolved.preset.cachePath ? join16(appRoot, resolved.preset.cachePath) : null;
|
|
15779
17680
|
const buildCacheKey = sanitizeCacheKey(input.branch);
|
|
15780
17681
|
if (cachePorts && buildCacheDir) {
|
|
15781
17682
|
await tryRestore(
|
|
@@ -15786,59 +17687,97 @@ var LocalBuildExecutor = class {
|
|
|
15786
17687
|
observer
|
|
15787
17688
|
);
|
|
15788
17689
|
}
|
|
15789
|
-
await this.dependencies.runCommand(
|
|
15790
|
-
{
|
|
15791
|
-
command: packageManager.name,
|
|
15792
|
-
args: ["run", resolved.buildScript],
|
|
15793
|
-
cwd: appRoot,
|
|
15794
|
-
env: {
|
|
15795
|
-
...input.env,
|
|
15796
|
-
...isNext ? { NEXT_PRIVATE_STANDALONE: "true" } : {},
|
|
15797
|
-
NPM_CONFIG_USERCONFIG: packageManagerConfigPath
|
|
15798
|
-
},
|
|
15799
|
-
timeoutMs: 15 * 6e4,
|
|
15800
|
-
stage: "BUILDING",
|
|
15801
|
-
errorCode: "BUILD_FAILED"
|
|
15802
|
-
},
|
|
15803
|
-
observer
|
|
15804
|
-
);
|
|
15805
17690
|
if (isNext) {
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
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 } : {}
|
|
15821
17740
|
},
|
|
15822
|
-
|
|
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
|
+
}
|
|
15823
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 {
|
|
15824
17769
|
await this.dependencies.runCommand(
|
|
15825
17770
|
{
|
|
15826
|
-
command:
|
|
15827
|
-
args: [
|
|
15828
|
-
"deploy",
|
|
15829
|
-
"--dry-run",
|
|
15830
|
-
"--config",
|
|
15831
|
-
platformConfig.wranglerConfigPath,
|
|
15832
|
-
"--outdir",
|
|
15833
|
-
".open-next/worker-bundle"
|
|
15834
|
-
],
|
|
17771
|
+
command: packageManager.name,
|
|
17772
|
+
args: ["run", resolved.buildScript],
|
|
15835
17773
|
cwd: appRoot,
|
|
15836
17774
|
env: {
|
|
15837
|
-
|
|
17775
|
+
...input.env,
|
|
17776
|
+
NPM_CONFIG_USERCONFIG: packageManagerConfigPath
|
|
15838
17777
|
},
|
|
15839
|
-
timeoutMs:
|
|
17778
|
+
timeoutMs: 15 * 6e4,
|
|
15840
17779
|
stage: "BUILDING",
|
|
15841
|
-
errorCode: "
|
|
17780
|
+
errorCode: "BUILD_FAILED"
|
|
15842
17781
|
},
|
|
15843
17782
|
observer
|
|
15844
17783
|
);
|
|
@@ -15847,7 +17786,7 @@ var LocalBuildExecutor = class {
|
|
|
15847
17786
|
if (depsKey && !depsRestored) {
|
|
15848
17787
|
await trySave("deps", depsKey, pmCacheDir, cachePorts, observer);
|
|
15849
17788
|
}
|
|
15850
|
-
if (buildCacheDir) {
|
|
17789
|
+
if (buildCacheDir && await pathExists3(buildCacheDir)) {
|
|
15851
17790
|
await trySave(
|
|
15852
17791
|
"next",
|
|
15853
17792
|
buildCacheKey,
|
|
@@ -15859,27 +17798,101 @@ var LocalBuildExecutor = class {
|
|
|
15859
17798
|
}
|
|
15860
17799
|
}
|
|
15861
17800
|
await notifyStatus(observer, "DEPLOYING");
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
17801
|
+
artifact ??= await this.readStatic(appRoot, resolved, identity, observer);
|
|
17802
|
+
return {
|
|
17803
|
+
gitCommitSha,
|
|
17804
|
+
artifact,
|
|
17805
|
+
...strategyRecord ? { strategy: strategyRecord } : {}
|
|
15865
17806
|
};
|
|
15866
|
-
const artifact = isNext ? await this.dependencies.readOpenNextArtifact(
|
|
15867
|
-
appRoot,
|
|
15868
|
-
identity,
|
|
15869
|
-
platformConfig.wranglerConfigPath
|
|
15870
|
-
) : await this.dependencies.readStaticArtifact(
|
|
15871
|
-
appRoot,
|
|
15872
|
-
resolved.outputDirectory,
|
|
15873
|
-
identity,
|
|
15874
|
-
{ spaFallback: resolved.preset.spaFallback },
|
|
15875
|
-
(message) => observer.onLog({ level: "WARN", stage: "DEPLOYING", message })
|
|
15876
|
-
);
|
|
15877
|
-
return { gitCommitSha, artifact };
|
|
15878
17807
|
} finally {
|
|
15879
17808
|
await this.dependencies.removeWorkspace(workspace);
|
|
15880
17809
|
}
|
|
15881
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
|
+
}
|
|
15882
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
|
+
}
|
|
15883
17896
|
|
|
15884
17897
|
// ../../packages/sdk/src/auth.ts
|
|
15885
17898
|
import { homedir as homedir2 } from "os";
|
|
@@ -15924,6 +17937,8 @@ async function errorFromResponse(response, context) {
|
|
|
15924
17937
|
if (body?.error?.fields !== void 0) details.fields = body.error.fields;
|
|
15925
17938
|
if (typeof body?.error?.installUrl === "string")
|
|
15926
17939
|
details.installUrl = body.error.installUrl;
|
|
17940
|
+
if (typeof body?.error?.statementIndex === "number")
|
|
17941
|
+
details.statementIndex = body.error.statementIndex;
|
|
15927
17942
|
return new NaroError(
|
|
15928
17943
|
code,
|
|
15929
17944
|
serverMessage ?? `${context} failed: the control plane returned ${String(response.status)}.`,
|
|
@@ -15937,12 +17952,12 @@ async function errorFromResponse(response, context) {
|
|
|
15937
17952
|
// ../../packages/sdk/src/session.ts
|
|
15938
17953
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
15939
17954
|
import { homedir } from "os";
|
|
15940
|
-
import { join as
|
|
17955
|
+
import { join as join17 } from "path";
|
|
15941
17956
|
function credentialsDir(home = homedir()) {
|
|
15942
|
-
return
|
|
17957
|
+
return join17(home, ".naro");
|
|
15943
17958
|
}
|
|
15944
17959
|
function credentialsFile(home = homedir()) {
|
|
15945
|
-
return
|
|
17960
|
+
return join17(credentialsDir(home), "credentials.json");
|
|
15946
17961
|
}
|
|
15947
17962
|
function saveSession(session, home = homedir()) {
|
|
15948
17963
|
mkdirSync(credentialsDir(home), { recursive: true, mode: 448 });
|
|
@@ -15983,17 +17998,158 @@ function controlPlaneUrl(env = process.env) {
|
|
|
15983
17998
|
}
|
|
15984
17999
|
|
|
15985
18000
|
// ../../packages/sdk/src/callback-server.ts
|
|
15986
|
-
import { createServer } from "http";
|
|
15987
|
-
|
|
15988
|
-
|
|
18001
|
+
import { createServer as createServer2 } from "http";
|
|
18002
|
+
|
|
18003
|
+
// ../../packages/sdk/src/callback-page.ts
|
|
18004
|
+
function escapeHtml(value) {
|
|
18005
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
18006
|
+
}
|
|
18007
|
+
var FAVICON = `data:image/svg+xml,${encodeURIComponent(
|
|
18008
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#ededed"/><text x="16" y="23" text-anchor="middle" fill="#000" font-family="system-ui,sans-serif" font-size="20" font-weight="700">n</text></svg>'
|
|
18009
|
+
)}`;
|
|
18010
|
+
var CHECK_ICON = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12.5 10 17.5 19 7.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
18011
|
+
var CROSS_ICON = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M7 7 17 17M17 7 7 17" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
18012
|
+
var STYLES = `
|
|
18013
|
+
*{box-sizing:border-box}
|
|
18014
|
+
:root{
|
|
18015
|
+
--bg:#000;--surface:#0a0a0a;--surface-2:#111;--border:#232323;
|
|
18016
|
+
--text:#ededed;--muted:#a1a1a1;--faint:#6f6f6f;
|
|
18017
|
+
--sans:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
18018
|
+
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
|
18019
|
+
}
|
|
18020
|
+
html{color-scheme:dark;-webkit-font-smoothing:antialiased}
|
|
18021
|
+
body{
|
|
18022
|
+
margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;
|
|
18023
|
+
background:var(--bg);color:var(--text);font-family:var(--sans);
|
|
18024
|
+
font-size:14px;line-height:1.5;letter-spacing:-.006em;
|
|
18025
|
+
}
|
|
18026
|
+
body::before{
|
|
18027
|
+
content:"";position:fixed;inset:0;pointer-events:none;
|
|
18028
|
+
background:radial-gradient(62% 46% at 50% 0%,var(--glow),transparent 72%);
|
|
18029
|
+
}
|
|
18030
|
+
.card{
|
|
18031
|
+
position:relative;width:100%;max-width:400px;padding:34px 32px 28px;
|
|
18032
|
+
background:var(--surface);border:1px solid var(--border);border-radius:14px;
|
|
18033
|
+
text-align:center;animation:rise .4s cubic-bezier(.2,.7,.3,1) both;
|
|
18034
|
+
}
|
|
18035
|
+
.brand{
|
|
18036
|
+
display:inline-flex;align-items:center;gap:8px;
|
|
18037
|
+
font-size:14px;font-weight:600;letter-spacing:-.02em;color:var(--muted);
|
|
18038
|
+
}
|
|
18039
|
+
.brand-mark{
|
|
18040
|
+
display:inline-grid;width:22px;height:22px;place-items:center;
|
|
18041
|
+
background:var(--text);color:#000;border-radius:6px;
|
|
18042
|
+
font-size:12px;font-weight:700;line-height:1;
|
|
18043
|
+
}
|
|
18044
|
+
.glyph{
|
|
18045
|
+
width:52px;height:52px;margin:26px auto 20px;display:grid;place-items:center;
|
|
18046
|
+
border-radius:50%;color:var(--tone);
|
|
18047
|
+
background:var(--tone-soft);border:1px solid var(--tone-edge);
|
|
18048
|
+
}
|
|
18049
|
+
.glyph svg{width:26px;height:26px}
|
|
18050
|
+
.glyph path{stroke-dasharray:32;stroke-dashoffset:32;animation:draw .5s .15s cubic-bezier(.2,.7,.3,1) forwards}
|
|
18051
|
+
h1{margin:0 0 8px;font-size:19px;font-weight:600;letter-spacing:-.02em}
|
|
18052
|
+
p{margin:0 auto;max-width:32ch;color:var(--muted)}
|
|
18053
|
+
.detail{
|
|
18054
|
+
margin-top:18px;padding:10px 12px;text-align:left;word-break:break-word;
|
|
18055
|
+
background:var(--surface-2);border:1px solid var(--border);border-radius:8px;
|
|
18056
|
+
font-family:var(--mono);font-size:12.5px;color:var(--muted);
|
|
18057
|
+
}
|
|
18058
|
+
.hint{
|
|
18059
|
+
display:flex;align-items:center;gap:10px;margin-top:22px;padding:10px 12px;
|
|
18060
|
+
background:var(--surface-2);border:1px solid var(--border);border-radius:8px;text-align:left;
|
|
18061
|
+
}
|
|
18062
|
+
.hint-label{
|
|
18063
|
+
flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;color:var(--faint);
|
|
18064
|
+
}
|
|
18065
|
+
.hint code{font-family:var(--mono);font-size:13px;color:var(--text)}
|
|
18066
|
+
.hint code::before{content:"$ ";color:var(--faint)}
|
|
18067
|
+
footer{margin-top:20px;font-size:12.5px;color:var(--faint)}
|
|
18068
|
+
@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
|
18069
|
+
@keyframes draw{to{stroke-dashoffset:0}}
|
|
18070
|
+
@media (prefers-reduced-motion:reduce){
|
|
18071
|
+
.card{animation:none}
|
|
18072
|
+
.glyph path{animation:none;stroke-dashoffset:0}
|
|
18073
|
+
}
|
|
18074
|
+
`;
|
|
18075
|
+
var TONES = {
|
|
18076
|
+
success: {
|
|
18077
|
+
color: "#4ade80",
|
|
18078
|
+
soft: "rgba(74,222,128,.12)",
|
|
18079
|
+
edge: "rgba(74,222,128,.3)",
|
|
18080
|
+
glow: "rgba(74,222,128,.09)",
|
|
18081
|
+
icon: CHECK_ICON
|
|
18082
|
+
},
|
|
18083
|
+
failure: {
|
|
18084
|
+
color: "#ff5c5c",
|
|
18085
|
+
soft: "rgba(255,92,92,.12)",
|
|
18086
|
+
edge: "rgba(255,92,92,.3)",
|
|
18087
|
+
glow: "rgba(255,92,92,.09)",
|
|
18088
|
+
icon: CROSS_ICON
|
|
18089
|
+
}
|
|
18090
|
+
};
|
|
18091
|
+
function page(options) {
|
|
18092
|
+
const tone = TONES[options.tone];
|
|
18093
|
+
const title = escapeHtml(options.title);
|
|
18094
|
+
const detail = options.detail ? `<div class="detail">${escapeHtml(options.detail)}</div>` : "";
|
|
18095
|
+
return `<!doctype html>
|
|
18096
|
+
<html lang="en">
|
|
18097
|
+
<head>
|
|
18098
|
+
<meta charset="utf-8">
|
|
18099
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
18100
|
+
<title>${title} \xB7 naro</title>
|
|
18101
|
+
<link rel="icon" href="${FAVICON}">
|
|
18102
|
+
<style>:root{--tone:${tone.color};--tone-soft:${tone.soft};--tone-edge:${tone.edge};--glow:${tone.glow}}${STYLES}</style>
|
|
18103
|
+
</head>
|
|
18104
|
+
<body>
|
|
18105
|
+
<main class="card">
|
|
18106
|
+
<span class="brand"><span class="brand-mark">n</span>naro</span>
|
|
18107
|
+
<div class="glyph">${tone.icon}</div>
|
|
18108
|
+
<h1>${title}</h1>
|
|
18109
|
+
<p>${escapeHtml(options.message)}</p>
|
|
18110
|
+
${detail}
|
|
18111
|
+
<div class="hint">
|
|
18112
|
+
<span class="hint-label">${escapeHtml(options.hint.label)}</span>
|
|
18113
|
+
<code>${escapeHtml(options.hint.command)}</code>
|
|
18114
|
+
</div>
|
|
18115
|
+
<footer>You can close this tab.</footer>
|
|
18116
|
+
</main>
|
|
18117
|
+
</body>
|
|
18118
|
+
</html>`;
|
|
18119
|
+
}
|
|
18120
|
+
function successPage() {
|
|
18121
|
+
return page({
|
|
18122
|
+
tone: "success",
|
|
18123
|
+
title: "You're signed in",
|
|
18124
|
+
message: "The CLI saved your session. Head back to the terminal to keep going.",
|
|
18125
|
+
hint: { label: "NEXT", command: "naro deploy" }
|
|
18126
|
+
});
|
|
18127
|
+
}
|
|
18128
|
+
function failurePage(reason) {
|
|
18129
|
+
return page({
|
|
18130
|
+
tone: "failure",
|
|
18131
|
+
title: "Sign-in failed",
|
|
18132
|
+
message: "Nothing was saved. Return to the terminal and try again.",
|
|
18133
|
+
detail: reason,
|
|
18134
|
+
hint: { label: "RETRY", command: "naro login" }
|
|
18135
|
+
});
|
|
18136
|
+
}
|
|
18137
|
+
|
|
18138
|
+
// ../../packages/sdk/src/callback-server.ts
|
|
18139
|
+
function sendPage(res, status, html) {
|
|
18140
|
+
res.statusCode = status;
|
|
18141
|
+
res.setHeader("content-type", "text/html; charset=utf-8");
|
|
18142
|
+
res.setHeader("cache-control", "no-store");
|
|
18143
|
+
res.end(html);
|
|
18144
|
+
}
|
|
15989
18145
|
async function startCallbackServer() {
|
|
15990
18146
|
let resolveCode;
|
|
15991
18147
|
let rejectCode;
|
|
15992
|
-
const codePromise = new Promise((
|
|
15993
|
-
resolveCode =
|
|
18148
|
+
const codePromise = new Promise((resolve10, reject) => {
|
|
18149
|
+
resolveCode = resolve10;
|
|
15994
18150
|
rejectCode = reject;
|
|
15995
18151
|
});
|
|
15996
|
-
const server =
|
|
18152
|
+
const server = createServer2((req, res) => {
|
|
15997
18153
|
const url2 = new URL(req.url ?? "/", "http://localhost");
|
|
15998
18154
|
if (url2.pathname !== "/callback") {
|
|
15999
18155
|
res.statusCode = 404;
|
|
@@ -16003,21 +18159,23 @@ async function startCallbackServer() {
|
|
|
16003
18159
|
const error51 = url2.searchParams.get("error");
|
|
16004
18160
|
const code = url2.searchParams.get("code");
|
|
16005
18161
|
if (error51) {
|
|
16006
|
-
|
|
16007
|
-
res
|
|
18162
|
+
const description = url2.searchParams.get("error_description");
|
|
18163
|
+
sendPage(res, 200, failurePage(description ?? error51));
|
|
16008
18164
|
rejectCode(new Error(`GitHub authorization failed: ${error51}`));
|
|
16009
18165
|
return;
|
|
16010
18166
|
}
|
|
16011
18167
|
if (!code) {
|
|
16012
|
-
|
|
16013
|
-
|
|
18168
|
+
sendPage(
|
|
18169
|
+
res,
|
|
18170
|
+
400,
|
|
18171
|
+
failurePage("The callback carried no authorization code.")
|
|
18172
|
+
);
|
|
16014
18173
|
return;
|
|
16015
18174
|
}
|
|
16016
|
-
res
|
|
16017
|
-
res.end(DONE_HTML);
|
|
18175
|
+
sendPage(res, 200, successPage());
|
|
16018
18176
|
resolveCode(code);
|
|
16019
18177
|
});
|
|
16020
|
-
await new Promise((
|
|
18178
|
+
await new Promise((resolve10) => server.listen(0, "127.0.0.1", resolve10));
|
|
16021
18179
|
const port = server.address().port;
|
|
16022
18180
|
return {
|
|
16023
18181
|
port,
|
|
@@ -16242,10 +18400,12 @@ function buildLogEntry(row) {
|
|
|
16242
18400
|
}
|
|
16243
18401
|
|
|
16244
18402
|
// ../../packages/sdk/src/client.ts
|
|
18403
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 45e3;
|
|
16245
18404
|
var NaroClient = class {
|
|
16246
18405
|
baseUrl;
|
|
16247
18406
|
getAccessToken;
|
|
16248
18407
|
doFetch;
|
|
18408
|
+
requestTimeoutMs;
|
|
16249
18409
|
teams;
|
|
16250
18410
|
projects;
|
|
16251
18411
|
github;
|
|
@@ -16253,10 +18413,12 @@ var NaroClient = class {
|
|
|
16253
18413
|
env;
|
|
16254
18414
|
domains;
|
|
16255
18415
|
logs;
|
|
18416
|
+
db;
|
|
16256
18417
|
constructor(options) {
|
|
16257
18418
|
this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
|
|
16258
18419
|
this.getAccessToken = options.getAccessToken;
|
|
16259
18420
|
this.doFetch = options.fetch ?? fetch;
|
|
18421
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
16260
18422
|
this.teams = new TeamsApi(this);
|
|
16261
18423
|
this.projects = new ProjectsApi(this);
|
|
16262
18424
|
this.github = new GithubApi(this);
|
|
@@ -16264,6 +18426,7 @@ var NaroClient = class {
|
|
|
16264
18426
|
this.env = new EnvApi(this);
|
|
16265
18427
|
this.domains = new DomainsApi(this);
|
|
16266
18428
|
this.logs = new LogsApi(this);
|
|
18429
|
+
this.db = new DbApi(this);
|
|
16267
18430
|
}
|
|
16268
18431
|
async request(path, options) {
|
|
16269
18432
|
const token = await this.getAccessToken();
|
|
@@ -16275,9 +18438,17 @@ var NaroClient = class {
|
|
|
16275
18438
|
Authorization: `Bearer ${token}`,
|
|
16276
18439
|
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
16277
18440
|
},
|
|
16278
|
-
...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)
|
|
16279
18443
|
});
|
|
16280
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
|
+
}
|
|
16281
18452
|
throw new NaroError(
|
|
16282
18453
|
"NETWORK_ERROR",
|
|
16283
18454
|
`${options.context} failed: could not reach ${this.baseUrl}.`,
|
|
@@ -16445,11 +18616,23 @@ var DeploymentsApi = class {
|
|
|
16445
18616
|
);
|
|
16446
18617
|
return deploymentInfo(body.deployment);
|
|
16447
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
|
+
}
|
|
16448
18631
|
/** Polls a deployment until READY/FAILED, streaming new build logs. */
|
|
16449
18632
|
async wait(deploymentId, options = {}) {
|
|
16450
18633
|
const intervalMs = options.intervalMs ?? 2500;
|
|
16451
18634
|
const timeoutMs = options.timeoutMs ?? 15 * 6e4;
|
|
16452
|
-
const
|
|
18635
|
+
const sleep2 = options.sleep ?? ((ms) => new Promise((resolve10) => setTimeout(resolve10, ms)));
|
|
16453
18636
|
const startedAt = Date.now();
|
|
16454
18637
|
let lastStatus = null;
|
|
16455
18638
|
let lastSequence = -1;
|
|
@@ -16473,7 +18656,7 @@ var DeploymentsApi = class {
|
|
|
16473
18656
|
{ details: { deploymentId } }
|
|
16474
18657
|
);
|
|
16475
18658
|
}
|
|
16476
|
-
await
|
|
18659
|
+
await sleep2(intervalMs);
|
|
16477
18660
|
}
|
|
16478
18661
|
}
|
|
16479
18662
|
};
|
|
@@ -16529,6 +18712,9 @@ var EnvApi = class {
|
|
|
16529
18712
|
return matches.length;
|
|
16530
18713
|
}
|
|
16531
18714
|
};
|
|
18715
|
+
function normalizeHostname(hostname3) {
|
|
18716
|
+
return hostname3.trim().toLowerCase().replace(/\.+$/, "");
|
|
18717
|
+
}
|
|
16532
18718
|
var DomainsApi = class {
|
|
16533
18719
|
constructor(client) {
|
|
16534
18720
|
this.client = client;
|
|
@@ -16540,12 +18726,27 @@ var DomainsApi = class {
|
|
|
16540
18726
|
);
|
|
16541
18727
|
return body.domains;
|
|
16542
18728
|
}
|
|
16543
|
-
|
|
16544
|
-
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16548
|
-
|
|
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;
|
|
16549
18750
|
}
|
|
16550
18751
|
async refresh(projectId, domainId) {
|
|
16551
18752
|
const body = await this.client.request(
|
|
@@ -16554,6 +18755,38 @@ var DomainsApi = class {
|
|
|
16554
18755
|
);
|
|
16555
18756
|
return body.domain;
|
|
16556
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
|
+
}
|
|
16557
18790
|
async removeById(projectId, domainId) {
|
|
16558
18791
|
await this.client.request(
|
|
16559
18792
|
`/api/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domainId)}`,
|
|
@@ -16562,8 +18795,19 @@ var DomainsApi = class {
|
|
|
16562
18795
|
}
|
|
16563
18796
|
/** Removes a domain by hostname. */
|
|
16564
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);
|
|
16565
18807
|
const domains = await this.list(projectId);
|
|
16566
|
-
const match = domains.find(
|
|
18808
|
+
const match = domains.find(
|
|
18809
|
+
(domain2) => normalizeHostname(domain2.hostname) === wanted
|
|
18810
|
+
);
|
|
16567
18811
|
if (!match) {
|
|
16568
18812
|
throw new NaroError(
|
|
16569
18813
|
"DOMAIN_NOT_FOUND",
|
|
@@ -16571,7 +18815,7 @@ var DomainsApi = class {
|
|
|
16571
18815
|
{ details: { hostname: hostname3 } }
|
|
16572
18816
|
);
|
|
16573
18817
|
}
|
|
16574
|
-
|
|
18818
|
+
return match;
|
|
16575
18819
|
}
|
|
16576
18820
|
};
|
|
16577
18821
|
var LogsApi = class {
|
|
@@ -16589,10 +18833,113 @@ var LogsApi = class {
|
|
|
16589
18833
|
return body.entries;
|
|
16590
18834
|
}
|
|
16591
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
|
+
}
|
|
16592
18939
|
|
|
16593
18940
|
// ../../packages/sdk/src/dotenv.ts
|
|
16594
18941
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
16595
|
-
import { join as
|
|
18942
|
+
import { join as join18 } from "path";
|
|
16596
18943
|
function parseDotenv(text) {
|
|
16597
18944
|
const out = {};
|
|
16598
18945
|
for (const line of text.split(/\r?\n/)) {
|
|
@@ -16610,7 +18957,7 @@ function parseDotenv(text) {
|
|
|
16610
18957
|
}
|
|
16611
18958
|
function readProjectEnv(dir) {
|
|
16612
18959
|
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
16613
|
-
const path =
|
|
18960
|
+
const path = join18(dir, name);
|
|
16614
18961
|
if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
|
|
16615
18962
|
}
|
|
16616
18963
|
return {};
|
|
@@ -16625,8 +18972,8 @@ function serializeDotenv(vars) {
|
|
|
16625
18972
|
|
|
16626
18973
|
// ../../packages/sdk/src/env-scan.ts
|
|
16627
18974
|
import { existsSync as existsSync2, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
16628
|
-
import { join as
|
|
16629
|
-
var
|
|
18975
|
+
import { join as join19 } from "path";
|
|
18976
|
+
var SOURCE_EXTENSIONS3 = /* @__PURE__ */ new Set([
|
|
16630
18977
|
".ts",
|
|
16631
18978
|
".tsx",
|
|
16632
18979
|
".js",
|
|
@@ -16680,7 +19027,7 @@ function collect(dir, out) {
|
|
|
16680
19027
|
for (const entry of entries) {
|
|
16681
19028
|
if (out.length >= MAX_FILES) return;
|
|
16682
19029
|
if (entry.isSymbolicLink()) continue;
|
|
16683
|
-
const path =
|
|
19030
|
+
const path = join19(dir, entry.name);
|
|
16684
19031
|
if (entry.isDirectory()) {
|
|
16685
19032
|
if (entry.name.startsWith(".") || SKIP_DIRECTORIES.has(entry.name))
|
|
16686
19033
|
continue;
|
|
@@ -16689,7 +19036,7 @@ function collect(dir, out) {
|
|
|
16689
19036
|
}
|
|
16690
19037
|
if (!entry.isFile()) continue;
|
|
16691
19038
|
const dot = entry.name.lastIndexOf(".");
|
|
16692
|
-
if (dot < 0 || !
|
|
19039
|
+
if (dot < 0 || !SOURCE_EXTENSIONS3.has(entry.name.slice(dot))) continue;
|
|
16693
19040
|
out.push(path);
|
|
16694
19041
|
}
|
|
16695
19042
|
}
|
|
@@ -16712,7 +19059,7 @@ function scanEnvReferences(dir) {
|
|
|
16712
19059
|
}
|
|
16713
19060
|
}
|
|
16714
19061
|
for (const name of EXAMPLE_FILES) {
|
|
16715
|
-
const path =
|
|
19062
|
+
const path = join19(dir, name);
|
|
16716
19063
|
if (!existsSync2(path)) continue;
|
|
16717
19064
|
let parsed;
|
|
16718
19065
|
try {
|
|
@@ -16730,11 +19077,11 @@ function scanEnvReferences(dir) {
|
|
|
16730
19077
|
|
|
16731
19078
|
// ../../packages/sdk/src/framework.ts
|
|
16732
19079
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
16733
|
-
import { join as
|
|
19080
|
+
import { join as join20 } from "path";
|
|
16734
19081
|
function readPackageJson2(dir) {
|
|
16735
19082
|
try {
|
|
16736
19083
|
return JSON.parse(
|
|
16737
|
-
readFileSync4(
|
|
19084
|
+
readFileSync4(join20(dir, "package.json"), "utf8")
|
|
16738
19085
|
);
|
|
16739
19086
|
} catch {
|
|
16740
19087
|
return null;
|
|
@@ -16745,7 +19092,7 @@ function hasDependency(pkg, name) {
|
|
|
16745
19092
|
}
|
|
16746
19093
|
function hasConfig(dir, base2, evidence) {
|
|
16747
19094
|
for (const ext of ["js", "mjs", "cjs", "ts", "mts"]) {
|
|
16748
|
-
if (existsSync3(
|
|
19095
|
+
if (existsSync3(join20(dir, `${base2}.${ext}`))) {
|
|
16749
19096
|
evidence.push(`${base2}.${ext}`);
|
|
16750
19097
|
return true;
|
|
16751
19098
|
}
|
|
@@ -16757,8 +19104,8 @@ function detectFramework2(dir) {
|
|
|
16757
19104
|
const pkg = readPackageJson2(dir);
|
|
16758
19105
|
if (hasDependency(pkg, "next") || hasConfig(dir, "next.config", evidence)) {
|
|
16759
19106
|
if (hasDependency(pkg, "next")) evidence.push("package.json: next");
|
|
16760
|
-
if (existsSync3(
|
|
16761
|
-
if (existsSync3(
|
|
19107
|
+
if (existsSync3(join20(dir, "app"))) evidence.push("app/");
|
|
19108
|
+
if (existsSync3(join20(dir, "pages"))) evidence.push("pages/");
|
|
16762
19109
|
return {
|
|
16763
19110
|
framework: "nextjs",
|
|
16764
19111
|
name: "Next.js",
|
|
@@ -16812,7 +19159,7 @@ function detectFramework2(dir) {
|
|
|
16812
19159
|
deployable: true
|
|
16813
19160
|
};
|
|
16814
19161
|
}
|
|
16815
|
-
if (!pkg && existsSync3(
|
|
19162
|
+
if (!pkg && existsSync3(join20(dir, "index.html"))) {
|
|
16816
19163
|
evidence.push("index.html (no package.json)");
|
|
16817
19164
|
return {
|
|
16818
19165
|
framework: "static",
|
|
@@ -16833,6 +19180,71 @@ function detectFramework2(dir) {
|
|
|
16833
19180
|
};
|
|
16834
19181
|
}
|
|
16835
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
|
+
|
|
16836
19248
|
// ../../packages/sdk/src/link.ts
|
|
16837
19249
|
import {
|
|
16838
19250
|
existsSync as existsSync4,
|
|
@@ -16841,12 +19253,12 @@ import {
|
|
|
16841
19253
|
rmSync as rmSync2,
|
|
16842
19254
|
writeFileSync as writeFileSync2
|
|
16843
19255
|
} from "fs";
|
|
16844
|
-
import { join as
|
|
19256
|
+
import { join as join21 } from "path";
|
|
16845
19257
|
function linkDir(dir) {
|
|
16846
|
-
return
|
|
19258
|
+
return join21(dir, ".naro");
|
|
16847
19259
|
}
|
|
16848
19260
|
function linkFile(dir) {
|
|
16849
|
-
return
|
|
19261
|
+
return join21(linkDir(dir), "project.json");
|
|
16850
19262
|
}
|
|
16851
19263
|
function readProjectLink(dir) {
|
|
16852
19264
|
try {
|
|
@@ -16868,7 +19280,7 @@ function writeProjectLink(dir, link) {
|
|
|
16868
19280
|
writeFileSync2(linkFile(dir), `${JSON.stringify(link, null, 2)}
|
|
16869
19281
|
`);
|
|
16870
19282
|
writeFileSync2(
|
|
16871
|
-
|
|
19283
|
+
join21(linkDir(dir), "README.txt"),
|
|
16872
19284
|
"This directory links the project to Naro and should not be committed.\n"
|
|
16873
19285
|
);
|
|
16874
19286
|
}
|
|
@@ -16878,9 +19290,9 @@ function removeProjectLink(dir) {
|
|
|
16878
19290
|
return true;
|
|
16879
19291
|
}
|
|
16880
19292
|
function ensureGitignoreHasNaro(dir) {
|
|
16881
|
-
const gitignorePath =
|
|
19293
|
+
const gitignorePath = join21(dir, ".gitignore");
|
|
16882
19294
|
const hasGitignore = existsSync4(gitignorePath);
|
|
16883
|
-
if (!hasGitignore && !existsSync4(
|
|
19295
|
+
if (!hasGitignore && !existsSync4(join21(dir, ".git"))) return false;
|
|
16884
19296
|
const current = hasGitignore ? readFileSync5(gitignorePath, "utf8") : "";
|
|
16885
19297
|
const covered = current.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".naro" || line === ".naro/" || line === "/.naro");
|
|
16886
19298
|
if (covered) return false;
|
|
@@ -16890,9 +19302,94 @@ function ensureGitignoreHasNaro(dir) {
|
|
|
16890
19302
|
return true;
|
|
16891
19303
|
}
|
|
16892
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
|
+
|
|
16893
19390
|
// src/env-consent.ts
|
|
16894
|
-
import { existsSync as
|
|
16895
|
-
import { join as
|
|
19391
|
+
import { existsSync as existsSync6 } from "fs";
|
|
19392
|
+
import { join as join22 } from "path";
|
|
16896
19393
|
var DOTENV_FILES = [".env.local", ".env.production", ".env"];
|
|
16897
19394
|
var PUBLIC_PREFIXES = ["NEXT_PUBLIC_", "VITE_", "REACT_APP_", "PUBLIC_"];
|
|
16898
19395
|
function maskEnvValue(key, value) {
|
|
@@ -16901,7 +19398,7 @@ function maskEnvValue(key, value) {
|
|
|
16901
19398
|
return value.length <= 8 ? "****" : `${value.slice(0, 4)}****`;
|
|
16902
19399
|
}
|
|
16903
19400
|
function findDotenvFile(dir) {
|
|
16904
|
-
for (const name of DOTENV_FILES) if (
|
|
19401
|
+
for (const name of DOTENV_FILES) if (existsSync6(join22(dir, name))) return name;
|
|
16905
19402
|
return null;
|
|
16906
19403
|
}
|
|
16907
19404
|
function envAddGuidance(keys) {
|
|
@@ -16958,10 +19455,10 @@ var SUPPORTED_REMOTE = [
|
|
|
16958
19455
|
/^ssh:\/\/[^\s]+$/,
|
|
16959
19456
|
/^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
|
|
16960
19457
|
];
|
|
16961
|
-
var defaultExec = (args, cwd) => new Promise((
|
|
19458
|
+
var defaultExec = (args, cwd) => new Promise((resolve10, reject) => {
|
|
16962
19459
|
execFile("git", args, { cwd }, (error51, stdout) => {
|
|
16963
19460
|
if (error51) reject(error51);
|
|
16964
|
-
else
|
|
19461
|
+
else resolve10(stdout);
|
|
16965
19462
|
});
|
|
16966
19463
|
});
|
|
16967
19464
|
async function tryExec(exec, args, cwd) {
|
|
@@ -17062,7 +19559,7 @@ function manifest(artifact) {
|
|
|
17062
19559
|
modules: [artifact.entrypoint, ...artifact.modules].map(meta3),
|
|
17063
19560
|
assets: artifact.assets.map((file2) => ({
|
|
17064
19561
|
...meta3(file2),
|
|
17065
|
-
hash: hashAsset(file2.data)
|
|
19562
|
+
hash: hashAsset(file2.data, file2.contentType)
|
|
17066
19563
|
})),
|
|
17067
19564
|
compatibilityDate: artifact.compatibilityDate,
|
|
17068
19565
|
compatibilityFlags: artifact.compatibilityFlags,
|
|
@@ -17093,8 +19590,8 @@ function parseDotenv2(text) {
|
|
|
17093
19590
|
function readProjectEnv2(dir) {
|
|
17094
19591
|
return readProjectEnv(dir);
|
|
17095
19592
|
}
|
|
17096
|
-
function eventsObserver(
|
|
17097
|
-
const post = (body) => fetchImpl(`${
|
|
19593
|
+
function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUrl) {
|
|
19594
|
+
const post = (body) => fetchImpl(`${cp3}/api/internal/deployments/${deploymentId}/events`, {
|
|
17098
19595
|
method: "POST",
|
|
17099
19596
|
headers: {
|
|
17100
19597
|
"Content-Type": "application/json",
|
|
@@ -17111,18 +19608,48 @@ function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
|
|
|
17111
19608
|
} catch (error51) {
|
|
17112
19609
|
lastError = error51;
|
|
17113
19610
|
await new Promise(
|
|
17114
|
-
(
|
|
19611
|
+
(resolve10) => setTimeout(resolve10, 500 * (attempt + 1))
|
|
17115
19612
|
);
|
|
17116
19613
|
}
|
|
17117
19614
|
}
|
|
17118
19615
|
throw lastError;
|
|
17119
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
|
+
});
|
|
17120
19641
|
return {
|
|
17121
|
-
onStatus: (status) =>
|
|
17122
|
-
|
|
19642
|
+
onStatus: async (status) => {
|
|
19643
|
+
await flushLogs();
|
|
19644
|
+
await postStatus({ type: "status", status });
|
|
19645
|
+
},
|
|
19646
|
+
onLog: (log2) => batcher.add(log2).catch(() => {
|
|
17123
19647
|
}),
|
|
17124
|
-
onMetadata:
|
|
17125
|
-
|
|
19648
|
+
onMetadata: async (metadata) => {
|
|
19649
|
+
await flushLogs();
|
|
19650
|
+
await post({ type: "metadata", metadata }).catch(() => {
|
|
19651
|
+
});
|
|
19652
|
+
}
|
|
17126
19653
|
};
|
|
17127
19654
|
}
|
|
17128
19655
|
function slugify2(value) {
|
|
@@ -17132,20 +19659,20 @@ function slugify2(value) {
|
|
|
17132
19659
|
async function projectName(sourceDir) {
|
|
17133
19660
|
try {
|
|
17134
19661
|
const pkg = JSON.parse(
|
|
17135
|
-
await
|
|
19662
|
+
await readFile13(join23(sourceDir, "package.json"), "utf8")
|
|
17136
19663
|
);
|
|
17137
19664
|
if (pkg.name) return pkg.name;
|
|
17138
19665
|
} catch {
|
|
17139
19666
|
}
|
|
17140
|
-
return
|
|
19667
|
+
return basename3(sourceDir);
|
|
17141
19668
|
}
|
|
17142
19669
|
var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo|\.naro)([\\/]|$)/;
|
|
17143
19670
|
function resolveBin(name) {
|
|
17144
|
-
let dir =
|
|
19671
|
+
let dir = dirname4(fileURLToPath(import.meta.url));
|
|
17145
19672
|
for (; ; ) {
|
|
17146
|
-
const candidate =
|
|
17147
|
-
if (
|
|
17148
|
-
const parent =
|
|
19673
|
+
const candidate = join23(dir, "node_modules", ".bin", name);
|
|
19674
|
+
if (existsSync7(candidate)) return candidate;
|
|
19675
|
+
const parent = dirname4(dir);
|
|
17149
19676
|
if (parent === dir) return name;
|
|
17150
19677
|
dir = parent;
|
|
17151
19678
|
}
|
|
@@ -17162,20 +19689,26 @@ async function detectPackageManagerLenient(root) {
|
|
|
17162
19689
|
"--no-audit",
|
|
17163
19690
|
"--no-fund",
|
|
17164
19691
|
"--legacy-peer-deps"
|
|
17165
|
-
]
|
|
19692
|
+
],
|
|
19693
|
+
addArgs: (specs) => ["npm", "install", "--no-save", ...specs],
|
|
19694
|
+
cacheArgs: (dir) => ["--cache", dir]
|
|
17166
19695
|
};
|
|
17167
19696
|
}
|
|
17168
19697
|
}
|
|
17169
19698
|
function localDependencies(sourceDir) {
|
|
17170
19699
|
return {
|
|
17171
|
-
createWorkspace: () => mkdtemp2(
|
|
17172
|
-
removeWorkspace: (path) =>
|
|
19700
|
+
createWorkspace: () => mkdtemp2(join23(tmpdir2(), "naro-build-")),
|
|
19701
|
+
removeWorkspace: (path) => rm3(path, { recursive: true, force: true }),
|
|
17173
19702
|
async runCommand(spec, observer) {
|
|
17174
19703
|
if (spec.command === "git" && spec.args[0] === "clone") {
|
|
17175
19704
|
const checkout = spec.args.at(-1);
|
|
17176
19705
|
if (!checkout) throw new Error("Missing checkout path.");
|
|
17177
|
-
await
|
|
19706
|
+
await cp2(sourceDir, checkout, {
|
|
17178
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,
|
|
17179
19712
|
filter: (src) => !COPY_SKIP.test(src)
|
|
17180
19713
|
});
|
|
17181
19714
|
return { stdout: [], stderr: [] };
|
|
@@ -17196,13 +19729,16 @@ function localDependencies(sourceDir) {
|
|
|
17196
19729
|
createPlatformBuildConfig,
|
|
17197
19730
|
readOpenNextArtifact,
|
|
17198
19731
|
openNextCommand: resolveBin("opennextjs-cloudflare"),
|
|
17199
|
-
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")
|
|
17200
19736
|
};
|
|
17201
19737
|
}
|
|
17202
19738
|
async function deployDirectory(sourceDir, options, log2) {
|
|
17203
19739
|
const cpUrl = controlPlaneUrl();
|
|
17204
19740
|
const production = options.production ?? false;
|
|
17205
|
-
const appRoot = options.rootDirectory ?
|
|
19741
|
+
const appRoot = options.rootDirectory ? resolve9(sourceDir, options.rootDirectory) : sourceDir;
|
|
17206
19742
|
const framework = detectFramework2(appRoot);
|
|
17207
19743
|
if (!framework.deployable && !options.force) {
|
|
17208
19744
|
throw new NaroError(
|
|
@@ -17275,7 +19811,8 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
17275
19811
|
cpUrl,
|
|
17276
19812
|
ctx.deploymentId,
|
|
17277
19813
|
ctx.callbackToken,
|
|
17278
|
-
options.fetch
|
|
19814
|
+
options.fetch,
|
|
19815
|
+
ctx.logIngestUrl
|
|
17279
19816
|
);
|
|
17280
19817
|
let artifact;
|
|
17281
19818
|
try {
|
|
@@ -17286,9 +19823,17 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
17286
19823
|
repositoryUrl: sourceDir,
|
|
17287
19824
|
branch: ctx.branch,
|
|
17288
19825
|
rootDirectory: options.rootDirectory ?? null,
|
|
17289
|
-
env: { ...ctx.env, ...envToSend }
|
|
19826
|
+
env: { ...ctx.env, ...envToSend },
|
|
19827
|
+
strategy: ctx.strategy ?? null,
|
|
19828
|
+
...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
|
|
17290
19829
|
},
|
|
17291
|
-
observer
|
|
19830
|
+
observer,
|
|
19831
|
+
createCacheTransport({
|
|
19832
|
+
deploymentId: ctx.deploymentId,
|
|
19833
|
+
controlPlaneUrl: cpUrl,
|
|
19834
|
+
callbackToken: ctx.callbackToken,
|
|
19835
|
+
...options.fetch ? { fetch: options.fetch } : { fetch }
|
|
19836
|
+
})
|
|
17292
19837
|
);
|
|
17293
19838
|
artifact = result.artifact;
|
|
17294
19839
|
} catch (error51) {
|
|
@@ -17360,11 +19905,18 @@ export {
|
|
|
17360
19905
|
openBrowser,
|
|
17361
19906
|
isTerminalStatus,
|
|
17362
19907
|
NaroClient,
|
|
19908
|
+
renderRows,
|
|
19909
|
+
summarizeStatement,
|
|
17363
19910
|
serializeDotenv,
|
|
19911
|
+
generateTypes,
|
|
17364
19912
|
readProjectLink,
|
|
17365
19913
|
writeProjectLink,
|
|
17366
19914
|
removeProjectLink,
|
|
17367
19915
|
ensureGitignoreHasNaro,
|
|
19916
|
+
resolveMigrationsDir,
|
|
19917
|
+
listMigrationFiles,
|
|
19918
|
+
nextMigrationFileName,
|
|
19919
|
+
compareMigrations,
|
|
17368
19920
|
promptForMissingEnv,
|
|
17369
19921
|
detectGitInfo,
|
|
17370
19922
|
parseDotenv2 as parseDotenv,
|