naro-cli 0.2.1 → 0.3.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 +112 -4
- package/dist/artifact-types.d.ts +3 -0
- package/dist/{chunk-IC4R5EYE.js → chunk-B6F7QIW7.js} +1336 -402
- package/dist/deploy.d.ts +39 -26
- package/dist/deploy.js +4 -10
- package/dist/index.js +1235 -64
- package/package.json +3 -2
- package/dist/callback-server.d.ts +0 -10
- package/dist/index.d.ts +0 -7
- package/dist/login.d.ts +0 -10
- package/dist/session.d.ts +0 -16
- package/dist/supabase-auth.d.ts +0 -11
|
@@ -6,12 +6,63 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/deploy.ts
|
|
9
|
-
import { existsSync
|
|
10
|
-
import { cp, mkdtemp as mkdtemp2, readFile as
|
|
11
|
-
import {
|
|
12
|
-
import { basename, dirname, join as
|
|
9
|
+
import { existsSync as existsSync4 } from "fs";
|
|
10
|
+
import { cp, mkdtemp as mkdtemp2, readFile as readFile7, rm as rm2 } from "fs/promises";
|
|
11
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
12
|
+
import { basename, dirname, join as join13 } from "path";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
|
|
15
|
+
// ../../packages/build/src/artifact-upload.ts
|
|
16
|
+
function artifactUploadEntries(artifact) {
|
|
17
|
+
return [
|
|
18
|
+
...[artifact.entrypoint, ...artifact.modules].map(
|
|
19
|
+
(file2) => ({ kind: "module", file: file2 })
|
|
20
|
+
),
|
|
21
|
+
...artifact.assets.map((file2) => ({
|
|
22
|
+
kind: "asset",
|
|
23
|
+
file: file2
|
|
24
|
+
}))
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
function artifactUrlKey(kind, name) {
|
|
28
|
+
return `${kind}/${name}`;
|
|
29
|
+
}
|
|
30
|
+
var ARTIFACT_URL_BATCH_SIZE = 100;
|
|
31
|
+
function parseArtifactPutUrls(payload) {
|
|
32
|
+
if (typeof payload !== "object" || payload === null) return null;
|
|
33
|
+
const { urls, disabled } = payload;
|
|
34
|
+
if (disabled === true) return null;
|
|
35
|
+
if (typeof urls !== "object" || urls === null) return null;
|
|
36
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const [key, value] of Object.entries(urls)) {
|
|
38
|
+
if (typeof value === "string") resolved.set(key, value);
|
|
39
|
+
}
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
async function uploadArtifactFiles(artifact, ports) {
|
|
43
|
+
const entries = artifactUploadEntries(artifact);
|
|
44
|
+
let proxied = 0;
|
|
45
|
+
for (let index = 0; index < entries.length; index += ARTIFACT_URL_BATCH_SIZE) {
|
|
46
|
+
const batch = entries.slice(index, index + ARTIFACT_URL_BATCH_SIZE);
|
|
47
|
+
const urls = await ports.resolvePutUrls(batch);
|
|
48
|
+
for (const entry of batch) {
|
|
49
|
+
const url2 = urls?.get(artifactUrlKey(entry.kind, entry.file.name));
|
|
50
|
+
if (!url2) {
|
|
51
|
+
proxied += 1;
|
|
52
|
+
await ports.putProxied(entry);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
await ports.putSigned(url2, entry);
|
|
57
|
+
} catch {
|
|
58
|
+
proxied += 1;
|
|
59
|
+
await ports.putProxied(entry);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { proxied, total: entries.length };
|
|
64
|
+
}
|
|
65
|
+
|
|
15
66
|
// ../../packages/build/src/cache.ts
|
|
16
67
|
import { createHash } from "crypto";
|
|
17
68
|
import { mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
@@ -116,7 +167,7 @@ function hashAsset(data) {
|
|
|
116
167
|
// ../../packages/build/src/local-build-executor.ts
|
|
117
168
|
import { mkdtemp, rm } from "fs/promises";
|
|
118
169
|
import { tmpdir } from "os";
|
|
119
|
-
import { join as
|
|
170
|
+
import { join as join8 } from "path";
|
|
120
171
|
|
|
121
172
|
// ../../packages/core/src/artifact-manifest.ts
|
|
122
173
|
var MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
@@ -14843,6 +14894,16 @@ async function pathExists(path) {
|
|
|
14843
14894
|
return false;
|
|
14844
14895
|
}
|
|
14845
14896
|
}
|
|
14897
|
+
async function createPackageManagerConfig(appRoot, writer = async (path, contents) => {
|
|
14898
|
+
await writeFile3(path, contents, "utf8");
|
|
14899
|
+
}) {
|
|
14900
|
+
const packageManagerConfigPath = join4(appRoot, ".next-deploy-user.npmrc");
|
|
14901
|
+
await writer(
|
|
14902
|
+
packageManagerConfigPath,
|
|
14903
|
+
"# Host package-manager credentials are intentionally unavailable.\n"
|
|
14904
|
+
);
|
|
14905
|
+
return packageManagerConfigPath;
|
|
14906
|
+
}
|
|
14846
14907
|
async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
|
|
14847
14908
|
await writeFile3(path, contents, "utf8");
|
|
14848
14909
|
}, cloudflareModulePath = createRequire(import.meta.url).resolve(
|
|
@@ -14850,7 +14911,6 @@ async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (
|
|
|
14850
14911
|
), exists3 = pathExists) {
|
|
14851
14912
|
const openNextConfigPath = join4(appRoot, ".next-deploy-open-next.config.mjs");
|
|
14852
14913
|
const wranglerConfigPath = join4(appRoot, ".next-deploy-wrangler.json");
|
|
14853
|
-
const packageManagerConfigPath = join4(appRoot, ".next-deploy-user.npmrc");
|
|
14854
14914
|
const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
|
|
14855
14915
|
let hasNextConfig = false;
|
|
14856
14916
|
for (const name of NEXT_CONFIG_FILES) {
|
|
@@ -14890,10 +14950,7 @@ export default defineCloudflareConfig({});
|
|
|
14890
14950
|
)}
|
|
14891
14951
|
`
|
|
14892
14952
|
);
|
|
14893
|
-
await writer
|
|
14894
|
-
packageManagerConfigPath,
|
|
14895
|
-
"# Host package-manager credentials are intentionally unavailable.\n"
|
|
14896
|
-
);
|
|
14953
|
+
const packageManagerConfigPath = await createPackageManagerConfig(appRoot, writer);
|
|
14897
14954
|
return {
|
|
14898
14955
|
openNextConfigPath,
|
|
14899
14956
|
wranglerConfigPath,
|
|
@@ -14901,9 +14958,129 @@ export default defineCloudflareConfig({});
|
|
|
14901
14958
|
};
|
|
14902
14959
|
}
|
|
14903
14960
|
|
|
14961
|
+
// ../../packages/build/src/frameworks.ts
|
|
14962
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
14963
|
+
import { join as join5 } from "path";
|
|
14964
|
+
var FRAMEWORK_PRESETS = {
|
|
14965
|
+
next: {
|
|
14966
|
+
id: "next",
|
|
14967
|
+
detectDependency: "next",
|
|
14968
|
+
defaultBuildScript: "build",
|
|
14969
|
+
defaultOutputDirectory: ".open-next",
|
|
14970
|
+
needsServerRuntime: true,
|
|
14971
|
+
spaFallback: false,
|
|
14972
|
+
cachePath: ".next/cache"
|
|
14973
|
+
},
|
|
14974
|
+
vite: {
|
|
14975
|
+
id: "vite",
|
|
14976
|
+
detectDependency: "vite",
|
|
14977
|
+
defaultBuildScript: "build",
|
|
14978
|
+
defaultOutputDirectory: "dist",
|
|
14979
|
+
needsServerRuntime: false,
|
|
14980
|
+
spaFallback: true,
|
|
14981
|
+
cachePath: "node_modules/.vite"
|
|
14982
|
+
},
|
|
14983
|
+
cra: {
|
|
14984
|
+
id: "cra",
|
|
14985
|
+
detectDependency: "react-scripts",
|
|
14986
|
+
defaultBuildScript: "build",
|
|
14987
|
+
defaultOutputDirectory: "build",
|
|
14988
|
+
needsServerRuntime: false,
|
|
14989
|
+
spaFallback: true,
|
|
14990
|
+
cachePath: "node_modules/.cache"
|
|
14991
|
+
},
|
|
14992
|
+
static: {
|
|
14993
|
+
id: "static",
|
|
14994
|
+
detectDependency: null,
|
|
14995
|
+
defaultBuildScript: null,
|
|
14996
|
+
defaultOutputDirectory: ".",
|
|
14997
|
+
needsServerRuntime: false,
|
|
14998
|
+
spaFallback: false,
|
|
14999
|
+
cachePath: null
|
|
15000
|
+
}
|
|
15001
|
+
};
|
|
15002
|
+
var DETECTION_ORDER = ["next", "vite", "cra"];
|
|
15003
|
+
var FRAMEWORK_LABELS = {
|
|
15004
|
+
next: "Next.js",
|
|
15005
|
+
vite: "Vite",
|
|
15006
|
+
cra: "Create React App",
|
|
15007
|
+
static: "static files"
|
|
15008
|
+
};
|
|
15009
|
+
function dependencyVersion(packageJson, name) {
|
|
15010
|
+
const version2 = packageJson.dependencies?.[name] ?? packageJson.devDependencies?.[name];
|
|
15011
|
+
return typeof version2 === "string" && version2.trim() !== "" ? version2 : null;
|
|
15012
|
+
}
|
|
15013
|
+
async function readPackageJson(root) {
|
|
15014
|
+
let source;
|
|
15015
|
+
try {
|
|
15016
|
+
source = await readFile4(join5(root, "package.json"), "utf8");
|
|
15017
|
+
} catch (error51) {
|
|
15018
|
+
if (error51.code === "ENOENT") return null;
|
|
15019
|
+
throw new DeploymentError(
|
|
15020
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15021
|
+
"Could not read package.json.",
|
|
15022
|
+
{ cause: error51 }
|
|
15023
|
+
);
|
|
15024
|
+
}
|
|
15025
|
+
try {
|
|
15026
|
+
return JSON.parse(source);
|
|
15027
|
+
} catch (error51) {
|
|
15028
|
+
throw new DeploymentError(
|
|
15029
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15030
|
+
"The selected root does not contain a valid package.json.",
|
|
15031
|
+
{ cause: error51 }
|
|
15032
|
+
);
|
|
15033
|
+
}
|
|
15034
|
+
}
|
|
15035
|
+
async function detectFramework(root, overrides = {}) {
|
|
15036
|
+
const packageJson = await readPackageJson(root);
|
|
15037
|
+
let preset;
|
|
15038
|
+
if (overrides.framework) {
|
|
15039
|
+
const candidate = FRAMEWORK_PRESETS[overrides.framework] ?? null;
|
|
15040
|
+
if (!candidate || candidate.id !== overrides.framework)
|
|
15041
|
+
throw new DeploymentError(
|
|
15042
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15043
|
+
`Unknown framework "${overrides.framework}".`
|
|
15044
|
+
);
|
|
15045
|
+
preset = candidate;
|
|
15046
|
+
} else if (!packageJson) {
|
|
15047
|
+
preset = FRAMEWORK_PRESETS.static;
|
|
15048
|
+
} else {
|
|
15049
|
+
const detected = DETECTION_ORDER.map((id) => FRAMEWORK_PRESETS[id]).find(
|
|
15050
|
+
(candidate) => candidate.detectDependency !== null && dependencyVersion(packageJson, candidate.detectDependency) !== null
|
|
15051
|
+
);
|
|
15052
|
+
if (!detected)
|
|
15053
|
+
throw new DeploymentError(
|
|
15054
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15055
|
+
"Could not detect the project framework from package.json. Set a framework (and build command / output directory) in the project settings."
|
|
15056
|
+
);
|
|
15057
|
+
preset = detected;
|
|
15058
|
+
}
|
|
15059
|
+
const buildScript = overrides.buildCommand ?? preset.defaultBuildScript;
|
|
15060
|
+
if (buildScript !== null) {
|
|
15061
|
+
if (!packageJson)
|
|
15062
|
+
throw new DeploymentError(
|
|
15063
|
+
"FRAMEWORK_DETECTION_FAILED",
|
|
15064
|
+
"A build command requires a package.json."
|
|
15065
|
+
);
|
|
15066
|
+
if (typeof packageJson.scripts?.[buildScript] !== "string")
|
|
15067
|
+
throw new DeploymentError(
|
|
15068
|
+
"BUILD_FAILED",
|
|
15069
|
+
`The project must declare a "${buildScript}" script.`
|
|
15070
|
+
);
|
|
15071
|
+
}
|
|
15072
|
+
const version2 = packageJson && preset.detectDependency ? dependencyVersion(packageJson, preset.detectDependency) : null;
|
|
15073
|
+
return {
|
|
15074
|
+
preset,
|
|
15075
|
+
buildScript,
|
|
15076
|
+
outputDirectory: overrides.outputDirectory ?? preset.defaultOutputDirectory,
|
|
15077
|
+
description: version2 ? `${FRAMEWORK_LABELS[preset.id]} ${version2}` : FRAMEWORK_LABELS[preset.id]
|
|
15078
|
+
};
|
|
15079
|
+
}
|
|
15080
|
+
|
|
14904
15081
|
// ../../packages/build/src/open-next.ts
|
|
14905
|
-
import { access as access4, readdir, readFile as
|
|
14906
|
-
import { extname, isAbsolute, join as
|
|
15082
|
+
import { access as access4, readdir, readFile as readFile5 } from "fs/promises";
|
|
15083
|
+
import { extname, isAbsolute, join as join6, relative, resolve, sep } from "path";
|
|
14907
15084
|
function parseJsonc(source) {
|
|
14908
15085
|
let output = "";
|
|
14909
15086
|
let index = 0;
|
|
@@ -14998,7 +15175,7 @@ async function listFiles(root) {
|
|
|
14998
15175
|
for (const entry of entries.sort(
|
|
14999
15176
|
(left, right) => left.name.localeCompare(right.name)
|
|
15000
15177
|
)) {
|
|
15001
|
-
const path =
|
|
15178
|
+
const path = join6(directory, entry.name);
|
|
15002
15179
|
if (entry.isSymbolicLink()) {
|
|
15003
15180
|
throw new DeploymentError(
|
|
15004
15181
|
"ARTIFACT_INVALID",
|
|
@@ -15009,7 +15186,7 @@ async function listFiles(root) {
|
|
|
15009
15186
|
else if (entry.isFile()) {
|
|
15010
15187
|
files.push({
|
|
15011
15188
|
name: relative(root, path).split(sep).join("/"),
|
|
15012
|
-
data: await
|
|
15189
|
+
data: await readFile5(path),
|
|
15013
15190
|
contentType: contentType(path)
|
|
15014
15191
|
});
|
|
15015
15192
|
}
|
|
@@ -15032,7 +15209,7 @@ async function findWranglerConfig(root, configuredPath) {
|
|
|
15032
15209
|
return resolved;
|
|
15033
15210
|
}
|
|
15034
15211
|
for (const name of ["wrangler.jsonc", "wrangler.json"]) {
|
|
15035
|
-
const path =
|
|
15212
|
+
const path = join6(root, name);
|
|
15036
15213
|
try {
|
|
15037
15214
|
await access4(path);
|
|
15038
15215
|
return path;
|
|
@@ -15048,7 +15225,7 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
15048
15225
|
try {
|
|
15049
15226
|
const configPath = await findWranglerConfig(appRoot, wranglerConfigPath);
|
|
15050
15227
|
const metadata = parseJsonc(
|
|
15051
|
-
await
|
|
15228
|
+
await readFile5(configPath, "utf8")
|
|
15052
15229
|
);
|
|
15053
15230
|
if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
|
|
15054
15231
|
throw new DeploymentError(
|
|
@@ -15070,7 +15247,7 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
15070
15247
|
);
|
|
15071
15248
|
}
|
|
15072
15249
|
await access4(entrypointPath);
|
|
15073
|
-
const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(appRoot, metadata.assets.directory, "Assets directory") :
|
|
15250
|
+
const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(appRoot, metadata.assets.directory, "Assets directory") : join6(outputRoot, "assets");
|
|
15074
15251
|
const assetsRelation = relative(outputRoot, configuredAssets);
|
|
15075
15252
|
if (assetsRelation === ".." || assetsRelation.startsWith(`..${sep}`) || isAbsolute(assetsRelation)) {
|
|
15076
15253
|
throw new DeploymentError(
|
|
@@ -15078,7 +15255,7 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
15078
15255
|
"Assets directory must be inside .open-next."
|
|
15079
15256
|
);
|
|
15080
15257
|
}
|
|
15081
|
-
const bundleRoot =
|
|
15258
|
+
const bundleRoot = join6(outputRoot, "worker-bundle");
|
|
15082
15259
|
let bundleFiles = null;
|
|
15083
15260
|
try {
|
|
15084
15261
|
await access4(bundleRoot);
|
|
@@ -15263,48 +15440,99 @@ function resolveAppRoot(checkout, configuredRoot) {
|
|
|
15263
15440
|
return appRoot;
|
|
15264
15441
|
}
|
|
15265
15442
|
|
|
15266
|
-
// ../../packages/build/src/
|
|
15267
|
-
import { readFile as
|
|
15268
|
-
import { join as
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15443
|
+
// ../../packages/build/src/static-artifact.ts
|
|
15444
|
+
import { readdir as readdir2, readFile as readFile6 } from "fs/promises";
|
|
15445
|
+
import { isAbsolute as isAbsolute3, join as join7, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
|
|
15446
|
+
var SHIM_WORKER_SOURCE = "export default { fetch: (request, env) => env.ASSETS.fetch(request) };\n";
|
|
15447
|
+
var EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules"]);
|
|
15448
|
+
var ALLOWED_DOTFILE = ".well-known";
|
|
15449
|
+
function excluded(name) {
|
|
15450
|
+
if (name === ALLOWED_DOTFILE) return false;
|
|
15451
|
+
return name.startsWith(".") || EXCLUDED_DIRECTORIES.has(name);
|
|
15452
|
+
}
|
|
15453
|
+
async function readStaticArtifact(appRoot, outputDirectory, identity, options, onWarn) {
|
|
15454
|
+
if (isAbsolute3(outputDirectory))
|
|
15455
|
+
throw new DeploymentError(
|
|
15456
|
+
"ARTIFACT_INVALID",
|
|
15457
|
+
"Output directory must be relative to the application root."
|
|
15274
15458
|
);
|
|
15275
|
-
|
|
15459
|
+
const outputRoot = resolve3(appRoot, outputDirectory);
|
|
15460
|
+
const relation = relative3(appRoot, outputRoot);
|
|
15461
|
+
if (relation === ".." || relation.startsWith(`..${sep2}`) || isAbsolute3(relation))
|
|
15276
15462
|
throw new DeploymentError(
|
|
15277
|
-
"
|
|
15278
|
-
"
|
|
15279
|
-
{
|
|
15280
|
-
cause: error51
|
|
15281
|
-
}
|
|
15463
|
+
"ARTIFACT_INVALID",
|
|
15464
|
+
"Output directory escapes the application root."
|
|
15282
15465
|
);
|
|
15466
|
+
const assets = [];
|
|
15467
|
+
async function visit(directory) {
|
|
15468
|
+
const entries = await readdir2(directory, { withFileTypes: true });
|
|
15469
|
+
for (const entry of entries.sort(
|
|
15470
|
+
(left, right) => left.name.localeCompare(right.name)
|
|
15471
|
+
)) {
|
|
15472
|
+
if (excluded(entry.name)) continue;
|
|
15473
|
+
const path = join7(directory, entry.name);
|
|
15474
|
+
if (entry.isSymbolicLink())
|
|
15475
|
+
throw new DeploymentError(
|
|
15476
|
+
"ARTIFACT_INVALID",
|
|
15477
|
+
"Static output cannot contain symbolic links."
|
|
15478
|
+
);
|
|
15479
|
+
if (entry.isDirectory()) await visit(path);
|
|
15480
|
+
else if (entry.isFile()) {
|
|
15481
|
+
assets.push({
|
|
15482
|
+
name: relative3(outputRoot, path).split(sep2).join("/"),
|
|
15483
|
+
data: await readFile6(path),
|
|
15484
|
+
contentType: contentType(path)
|
|
15485
|
+
});
|
|
15486
|
+
}
|
|
15487
|
+
}
|
|
15283
15488
|
}
|
|
15284
|
-
|
|
15285
|
-
|
|
15489
|
+
try {
|
|
15490
|
+
await visit(outputRoot);
|
|
15491
|
+
} catch (error51) {
|
|
15492
|
+
if (error51 instanceof DeploymentError) throw error51;
|
|
15286
15493
|
throw new DeploymentError(
|
|
15287
|
-
"
|
|
15288
|
-
|
|
15494
|
+
"ARTIFACT_INVALID",
|
|
15495
|
+
`Could not read the build output directory "${outputDirectory}".`,
|
|
15496
|
+
{ cause: error51 }
|
|
15289
15497
|
);
|
|
15290
15498
|
}
|
|
15291
|
-
if (
|
|
15499
|
+
if (assets.length === 0)
|
|
15292
15500
|
throw new DeploymentError(
|
|
15293
|
-
"
|
|
15294
|
-
|
|
15501
|
+
"ARTIFACT_INVALID",
|
|
15502
|
+
`The build output directory "${outputDirectory}" contains no files.`
|
|
15295
15503
|
);
|
|
15296
|
-
|
|
15297
|
-
|
|
15504
|
+
if (!assets.some((file2) => file2.name === "index.html"))
|
|
15505
|
+
await onWarn?.(
|
|
15506
|
+
`No index.html in "${outputDirectory}" \u2014 the site root will not serve a page.`
|
|
15507
|
+
);
|
|
15508
|
+
return {
|
|
15509
|
+
...identity,
|
|
15510
|
+
entrypoint: {
|
|
15511
|
+
name: "worker.js",
|
|
15512
|
+
data: new TextEncoder().encode(SHIM_WORKER_SOURCE),
|
|
15513
|
+
contentType: "application/javascript"
|
|
15514
|
+
},
|
|
15515
|
+
modules: [],
|
|
15516
|
+
assets,
|
|
15517
|
+
compatibilityDate: COMPATIBILITY_DATE,
|
|
15518
|
+
compatibilityFlags: [],
|
|
15519
|
+
assetsBinding: "ASSETS",
|
|
15520
|
+
assetsConfig: {
|
|
15521
|
+
notFoundHandling: options.spaFallback ? "single-page-application" : "404-page"
|
|
15522
|
+
}
|
|
15523
|
+
};
|
|
15298
15524
|
}
|
|
15299
15525
|
|
|
15300
15526
|
// ../../packages/build/src/local-build-executor.ts
|
|
15301
15527
|
var defaultDependencies = {
|
|
15302
|
-
createWorkspace: () => mkdtemp(
|
|
15528
|
+
createWorkspace: () => mkdtemp(join8(tmpdir(), "next-deploy-build-")),
|
|
15303
15529
|
removeWorkspace: (path) => rm(path, { recursive: true, force: true }),
|
|
15304
15530
|
runCommand,
|
|
15305
15531
|
detectPackageManager,
|
|
15306
15532
|
resolveAppRoot,
|
|
15307
|
-
|
|
15533
|
+
detectFramework,
|
|
15534
|
+
readStaticArtifact,
|
|
15535
|
+
createPackageManagerConfig,
|
|
15308
15536
|
createPlatformBuildConfig,
|
|
15309
15537
|
readOpenNextArtifact,
|
|
15310
15538
|
openNextCommand: "opennextjs-cloudflare",
|
|
@@ -15350,14 +15578,14 @@ var LocalBuildExecutor = class {
|
|
|
15350
15578
|
}
|
|
15351
15579
|
async build(input, observer, cache) {
|
|
15352
15580
|
const workspace = await this.dependencies.createWorkspace();
|
|
15353
|
-
const checkout =
|
|
15354
|
-
const pmCacheDir =
|
|
15581
|
+
const checkout = join8(workspace, "checkout");
|
|
15582
|
+
const pmCacheDir = join8(workspace, ".pm-store");
|
|
15355
15583
|
let cacheSeq = 0;
|
|
15356
15584
|
const cachePorts = cache ? {
|
|
15357
15585
|
transport: cache,
|
|
15358
15586
|
fetch: this.dependencies.cacheFetch ?? ((url2, init) => fetch(url2, init)),
|
|
15359
15587
|
runCommand: this.dependencies.runCommand,
|
|
15360
|
-
tmpFile: (suffix) =>
|
|
15588
|
+
tmpFile: (suffix) => join8(workspace, `cache-${String(cacheSeq++)}${suffix}`)
|
|
15361
15589
|
} : null;
|
|
15362
15590
|
try {
|
|
15363
15591
|
await notifyStatus(observer, "CLONING");
|
|
@@ -15420,141 +15648,174 @@ var LocalBuildExecutor = class {
|
|
|
15420
15648
|
checkout,
|
|
15421
15649
|
input.rootDirectory
|
|
15422
15650
|
);
|
|
15423
|
-
const
|
|
15424
|
-
|
|
15425
|
-
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
);
|
|
15429
|
-
const
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
|
|
15447
|
-
|
|
15651
|
+
const resolved = await this.dependencies.detectFramework(appRoot, {
|
|
15652
|
+
framework: input.framework ?? null,
|
|
15653
|
+
buildCommand: input.buildCommand ?? null,
|
|
15654
|
+
outputDirectory: input.outputDirectory ?? null
|
|
15655
|
+
});
|
|
15656
|
+
await info(observer, "CLONING", `Detected ${resolved.description}`);
|
|
15657
|
+
const isNext = resolved.preset.id === "next";
|
|
15658
|
+
let platformConfig = null;
|
|
15659
|
+
let packageManagerConfigPath = null;
|
|
15660
|
+
if (isNext) {
|
|
15661
|
+
const conversion = await (this.dependencies.convertProxyToMiddleware ?? convertProxyToMiddleware)(appRoot);
|
|
15662
|
+
if (conversion.converted) {
|
|
15663
|
+
await observer.onLog({
|
|
15664
|
+
level: "WARN",
|
|
15665
|
+
stage: "CLONING",
|
|
15666
|
+
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.`
|
|
15667
|
+
});
|
|
15668
|
+
}
|
|
15669
|
+
platformConfig = await this.dependencies.createPlatformBuildConfig(
|
|
15670
|
+
appRoot,
|
|
15671
|
+
input.deploymentId
|
|
15672
|
+
);
|
|
15673
|
+
packageManagerConfigPath = platformConfig.packageManagerConfigPath;
|
|
15674
|
+
} else if (resolved.buildScript !== null) {
|
|
15675
|
+
packageManagerConfigPath = await this.dependencies.createPackageManagerConfig(appRoot);
|
|
15676
|
+
}
|
|
15677
|
+
if (resolved.buildScript !== null) {
|
|
15678
|
+
await notifyStatus(observer, "INSTALLING");
|
|
15679
|
+
const packageManager = await this.dependencies.detectPackageManager(checkout);
|
|
15680
|
+
const [installCommand, ...installArgs] = packageManager.install;
|
|
15681
|
+
if (!installCommand)
|
|
15682
|
+
throw new DeploymentError(
|
|
15683
|
+
"UNSUPPORTED_PACKAGE_MANAGER",
|
|
15684
|
+
"Install command is empty."
|
|
15685
|
+
);
|
|
15686
|
+
let depsKey = null;
|
|
15687
|
+
let depsRestored = false;
|
|
15688
|
+
if (cachePorts) {
|
|
15689
|
+
depsKey = await computeDepsCacheKey(checkout, packageManager.name);
|
|
15690
|
+
if (depsKey) {
|
|
15691
|
+
depsRestored = await tryRestore(
|
|
15692
|
+
"deps",
|
|
15693
|
+
depsKey,
|
|
15694
|
+
pmCacheDir,
|
|
15695
|
+
cachePorts,
|
|
15696
|
+
observer
|
|
15697
|
+
);
|
|
15698
|
+
}
|
|
15699
|
+
}
|
|
15700
|
+
await this.dependencies.runCommand(
|
|
15701
|
+
{
|
|
15702
|
+
command: installCommand,
|
|
15703
|
+
args: installArgs,
|
|
15704
|
+
cwd: checkout,
|
|
15705
|
+
env: {
|
|
15706
|
+
NODE_ENV: "development",
|
|
15707
|
+
NPM_CONFIG_USERCONFIG: packageManagerConfigPath,
|
|
15708
|
+
...cachePorts ? {
|
|
15709
|
+
npm_config_store_dir: pmCacheDir,
|
|
15710
|
+
npm_config_cache: pmCacheDir,
|
|
15711
|
+
YARN_CACHE_FOLDER: pmCacheDir
|
|
15712
|
+
} : {}
|
|
15713
|
+
},
|
|
15714
|
+
timeoutMs: 10 * 6e4,
|
|
15715
|
+
stage: "INSTALLING",
|
|
15716
|
+
errorCode: "INSTALL_FAILED"
|
|
15717
|
+
},
|
|
15718
|
+
observer
|
|
15448
15719
|
);
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15453
|
-
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
pmCacheDir,
|
|
15720
|
+
await notifyStatus(observer, "BUILDING");
|
|
15721
|
+
const buildCacheDir = resolved.preset.cachePath ? join8(appRoot, resolved.preset.cachePath) : null;
|
|
15722
|
+
const buildCacheKey = sanitizeCacheKey(input.branch);
|
|
15723
|
+
if (cachePorts && buildCacheDir) {
|
|
15724
|
+
await tryRestore(
|
|
15725
|
+
"next",
|
|
15726
|
+
buildCacheKey,
|
|
15727
|
+
buildCacheDir,
|
|
15458
15728
|
cachePorts,
|
|
15459
15729
|
observer
|
|
15460
15730
|
);
|
|
15461
15731
|
}
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
} : {}
|
|
15476
|
-
},
|
|
15477
|
-
timeoutMs: 10 * 6e4,
|
|
15478
|
-
stage: "INSTALLING",
|
|
15479
|
-
errorCode: "INSTALL_FAILED"
|
|
15480
|
-
},
|
|
15481
|
-
observer
|
|
15482
|
-
);
|
|
15483
|
-
await notifyStatus(observer, "BUILDING");
|
|
15484
|
-
const nextCacheDir = join7(appRoot, ".next", "cache");
|
|
15485
|
-
const nextKey = sanitizeCacheKey(input.branch);
|
|
15486
|
-
if (cachePorts) {
|
|
15487
|
-
await tryRestore("next", nextKey, nextCacheDir, cachePorts, observer);
|
|
15488
|
-
}
|
|
15489
|
-
await this.dependencies.runCommand(
|
|
15490
|
-
{
|
|
15491
|
-
command: packageManager.name,
|
|
15492
|
-
args: ["run", project.buildScript],
|
|
15493
|
-
cwd: appRoot,
|
|
15494
|
-
env: {
|
|
15495
|
-
...input.env ?? {},
|
|
15496
|
-
NEXT_PRIVATE_STANDALONE: "true",
|
|
15497
|
-
NPM_CONFIG_USERCONFIG: platformConfig.packageManagerConfigPath
|
|
15498
|
-
},
|
|
15499
|
-
timeoutMs: 15 * 6e4,
|
|
15500
|
-
stage: "BUILDING",
|
|
15501
|
-
errorCode: "BUILD_FAILED"
|
|
15502
|
-
},
|
|
15503
|
-
observer
|
|
15504
|
-
);
|
|
15505
|
-
await this.dependencies.runCommand(
|
|
15506
|
-
{
|
|
15507
|
-
command: this.dependencies.openNextCommand,
|
|
15508
|
-
args: [
|
|
15509
|
-
"build",
|
|
15510
|
-
"--skip-next-build",
|
|
15511
|
-
"--openNextConfigPath",
|
|
15512
|
-
platformConfig.openNextConfigPath,
|
|
15513
|
-
"--config",
|
|
15514
|
-
platformConfig.wranglerConfigPath
|
|
15515
|
-
],
|
|
15516
|
-
cwd: appRoot,
|
|
15517
|
-
timeoutMs: 15 * 6e4,
|
|
15518
|
-
stage: "BUILDING",
|
|
15519
|
-
errorCode: "OPENNEXT_FAILED"
|
|
15520
|
-
},
|
|
15521
|
-
observer
|
|
15522
|
-
);
|
|
15523
|
-
await this.dependencies.runCommand(
|
|
15524
|
-
{
|
|
15525
|
-
command: this.dependencies.wranglerCommand,
|
|
15526
|
-
args: [
|
|
15527
|
-
"deploy",
|
|
15528
|
-
"--dry-run",
|
|
15529
|
-
"--config",
|
|
15530
|
-
platformConfig.wranglerConfigPath,
|
|
15531
|
-
"--outdir",
|
|
15532
|
-
".open-next/worker-bundle"
|
|
15533
|
-
],
|
|
15534
|
-
cwd: appRoot,
|
|
15535
|
-
env: {
|
|
15536
|
-
WRANGLER_LOG_PATH: join7(appRoot, ".open-next", "wrangler.log")
|
|
15732
|
+
await this.dependencies.runCommand(
|
|
15733
|
+
{
|
|
15734
|
+
command: packageManager.name,
|
|
15735
|
+
args: ["run", resolved.buildScript],
|
|
15736
|
+
cwd: appRoot,
|
|
15737
|
+
env: {
|
|
15738
|
+
...input.env ?? {},
|
|
15739
|
+
...isNext ? { NEXT_PRIVATE_STANDALONE: "true" } : {},
|
|
15740
|
+
NPM_CONFIG_USERCONFIG: packageManagerConfigPath
|
|
15741
|
+
},
|
|
15742
|
+
timeoutMs: 15 * 6e4,
|
|
15743
|
+
stage: "BUILDING",
|
|
15744
|
+
errorCode: "BUILD_FAILED"
|
|
15537
15745
|
},
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15746
|
+
observer
|
|
15747
|
+
);
|
|
15748
|
+
if (isNext) {
|
|
15749
|
+
await this.dependencies.runCommand(
|
|
15750
|
+
{
|
|
15751
|
+
command: this.dependencies.openNextCommand,
|
|
15752
|
+
args: [
|
|
15753
|
+
"build",
|
|
15754
|
+
"--skip-next-build",
|
|
15755
|
+
"--openNextConfigPath",
|
|
15756
|
+
platformConfig.openNextConfigPath,
|
|
15757
|
+
"--config",
|
|
15758
|
+
platformConfig.wranglerConfigPath
|
|
15759
|
+
],
|
|
15760
|
+
cwd: appRoot,
|
|
15761
|
+
timeoutMs: 15 * 6e4,
|
|
15762
|
+
stage: "BUILDING",
|
|
15763
|
+
errorCode: "OPENNEXT_FAILED"
|
|
15764
|
+
},
|
|
15765
|
+
observer
|
|
15766
|
+
);
|
|
15767
|
+
await this.dependencies.runCommand(
|
|
15768
|
+
{
|
|
15769
|
+
command: this.dependencies.wranglerCommand,
|
|
15770
|
+
args: [
|
|
15771
|
+
"deploy",
|
|
15772
|
+
"--dry-run",
|
|
15773
|
+
"--config",
|
|
15774
|
+
platformConfig.wranglerConfigPath,
|
|
15775
|
+
"--outdir",
|
|
15776
|
+
".open-next/worker-bundle"
|
|
15777
|
+
],
|
|
15778
|
+
cwd: appRoot,
|
|
15779
|
+
env: {
|
|
15780
|
+
WRANGLER_LOG_PATH: join8(appRoot, ".open-next", "wrangler.log")
|
|
15781
|
+
},
|
|
15782
|
+
timeoutMs: 5 * 6e4,
|
|
15783
|
+
stage: "BUILDING",
|
|
15784
|
+
errorCode: "OPENNEXT_FAILED"
|
|
15785
|
+
},
|
|
15786
|
+
observer
|
|
15787
|
+
);
|
|
15788
|
+
}
|
|
15789
|
+
if (cachePorts) {
|
|
15790
|
+
if (depsKey && !depsRestored) {
|
|
15791
|
+
await trySave("deps", depsKey, pmCacheDir, cachePorts, observer);
|
|
15792
|
+
}
|
|
15793
|
+
if (buildCacheDir) {
|
|
15794
|
+
await trySave(
|
|
15795
|
+
"next",
|
|
15796
|
+
buildCacheKey,
|
|
15797
|
+
buildCacheDir,
|
|
15798
|
+
cachePorts,
|
|
15799
|
+
observer
|
|
15800
|
+
);
|
|
15801
|
+
}
|
|
15547
15802
|
}
|
|
15548
|
-
await trySave("next", nextKey, nextCacheDir, cachePorts, observer);
|
|
15549
15803
|
}
|
|
15550
15804
|
await notifyStatus(observer, "DEPLOYING");
|
|
15551
|
-
const
|
|
15805
|
+
const identity = {
|
|
15806
|
+
deploymentId: input.deploymentId,
|
|
15807
|
+
projectId: input.projectId
|
|
15808
|
+
};
|
|
15809
|
+
const artifact = isNext ? await this.dependencies.readOpenNextArtifact(
|
|
15552
15810
|
appRoot,
|
|
15553
|
-
|
|
15554
|
-
deploymentId: input.deploymentId,
|
|
15555
|
-
projectId: input.projectId
|
|
15556
|
-
},
|
|
15811
|
+
identity,
|
|
15557
15812
|
platformConfig.wranglerConfigPath
|
|
15813
|
+
) : await this.dependencies.readStaticArtifact(
|
|
15814
|
+
appRoot,
|
|
15815
|
+
resolved.outputDirectory,
|
|
15816
|
+
identity,
|
|
15817
|
+
{ spaFallback: resolved.preset.spaFallback },
|
|
15818
|
+
(message) => observer.onLog({ level: "WARN", stage: "DEPLOYING", message })
|
|
15558
15819
|
);
|
|
15559
15820
|
return { gitCommitSha, artifact };
|
|
15560
15821
|
} finally {
|
|
@@ -15563,53 +15824,66 @@ var LocalBuildExecutor = class {
|
|
|
15563
15824
|
}
|
|
15564
15825
|
};
|
|
15565
15826
|
|
|
15566
|
-
// src/
|
|
15567
|
-
import {
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15827
|
+
// ../../packages/sdk/src/auth.ts
|
|
15828
|
+
import { homedir as homedir2 } from "os";
|
|
15829
|
+
|
|
15830
|
+
// ../../packages/sdk/src/errors.ts
|
|
15831
|
+
var NaroError = class extends Error {
|
|
15832
|
+
code;
|
|
15833
|
+
/** HTTP status of the control plane response, when one was received. */
|
|
15834
|
+
status;
|
|
15835
|
+
/** Extra structured context (validation fields, deployment id...). */
|
|
15836
|
+
details;
|
|
15837
|
+
constructor(code, message, options) {
|
|
15838
|
+
super(
|
|
15839
|
+
message,
|
|
15840
|
+
options?.cause !== void 0 ? { cause: options.cause } : {}
|
|
15841
|
+
);
|
|
15842
|
+
this.name = "NaroError";
|
|
15843
|
+
this.code = code;
|
|
15844
|
+
if (options?.status !== void 0) this.status = options.status;
|
|
15845
|
+
if (options?.details !== void 0) this.details = options.details;
|
|
15585
15846
|
}
|
|
15847
|
+
};
|
|
15848
|
+
function isNaroError(error51) {
|
|
15849
|
+
return error51 instanceof NaroError || error51 instanceof Error && error51.name === "NaroError" && typeof error51.code === "string";
|
|
15586
15850
|
}
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
const
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15851
|
+
function toNaroError(error51, fallbackCode = "INTERNAL_ERROR") {
|
|
15852
|
+
if (error51 instanceof NaroError) return error51;
|
|
15853
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
15854
|
+
return new NaroError(fallbackCode, message, { cause: error51 });
|
|
15855
|
+
}
|
|
15856
|
+
async function errorFromResponse(response, context) {
|
|
15857
|
+
let body = null;
|
|
15858
|
+
try {
|
|
15859
|
+
body = await response.json();
|
|
15860
|
+
} catch {
|
|
15861
|
+
body = null;
|
|
15862
|
+
}
|
|
15863
|
+
const serverCode = typeof body?.error?.code === "string" ? body.error.code : null;
|
|
15864
|
+
const serverMessage = typeof body?.error?.message === "string" ? body.error.message : null;
|
|
15865
|
+
const code = serverCode ?? (response.status === 401 ? "UNAUTHORIZED" : response.status === 404 ? "NOT_FOUND" : response.status === 429 ? "RATE_LIMITED" : "INTERNAL_ERROR");
|
|
15866
|
+
const details = {};
|
|
15867
|
+
if (body?.error?.fields !== void 0) details.fields = body.error.fields;
|
|
15868
|
+
return new NaroError(
|
|
15869
|
+
code,
|
|
15870
|
+
serverMessage ?? `${context} failed: the control plane returned ${String(response.status)}.`,
|
|
15871
|
+
{
|
|
15872
|
+
status: response.status,
|
|
15873
|
+
...Object.keys(details).length > 0 ? { details } : {}
|
|
15874
|
+
}
|
|
15593
15875
|
);
|
|
15594
|
-
return {
|
|
15595
|
-
repositoryUrl: remote !== null && SUPPORTED_REMOTE.some((re) => re.test(remote)) ? remote : null,
|
|
15596
|
-
branch: branch !== null && branch !== "HEAD" ? branch : null
|
|
15597
|
-
};
|
|
15598
15876
|
}
|
|
15599
15877
|
|
|
15600
|
-
// src/
|
|
15601
|
-
import { spawn as spawn2 } from "child_process";
|
|
15602
|
-
import { homedir as homedir2 } from "os";
|
|
15603
|
-
|
|
15604
|
-
// src/session.ts
|
|
15878
|
+
// ../../packages/sdk/src/session.ts
|
|
15605
15879
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
15606
15880
|
import { homedir } from "os";
|
|
15607
|
-
import { join as
|
|
15881
|
+
import { join as join9 } from "path";
|
|
15608
15882
|
function credentialsDir(home = homedir()) {
|
|
15609
|
-
return
|
|
15883
|
+
return join9(home, ".naro");
|
|
15610
15884
|
}
|
|
15611
15885
|
function credentialsFile(home = homedir()) {
|
|
15612
|
-
return
|
|
15886
|
+
return join9(credentialsDir(home), "credentials.json");
|
|
15613
15887
|
}
|
|
15614
15888
|
function saveSession(session, home = homedir()) {
|
|
15615
15889
|
mkdirSync(credentialsDir(home), { recursive: true, mode: 448 });
|
|
@@ -15633,20 +15907,31 @@ function isExpired(session, skewSeconds = 60) {
|
|
|
15633
15907
|
return session.expires_at * 1e3 <= Date.now() + skewSeconds * 1e3;
|
|
15634
15908
|
}
|
|
15635
15909
|
|
|
15636
|
-
// src/supabase-auth.ts
|
|
15910
|
+
// ../../packages/sdk/src/supabase-auth.ts
|
|
15637
15911
|
import {
|
|
15638
15912
|
createClient
|
|
15639
15913
|
} from "@supabase/supabase-js";
|
|
15640
15914
|
|
|
15641
|
-
// src/
|
|
15915
|
+
// ../../packages/sdk/src/config.ts
|
|
15916
|
+
var DEFAULT_CONTROL_PLANE_URL = "https://naro.sh";
|
|
15917
|
+
var DEFAULT_SUPABASE_URL = "https://bymwzjpqfmwsdhtcpgjg.supabase.co";
|
|
15918
|
+
var DEFAULT_SUPABASE_KEY = "sb_publishable_qjM6NZsHnGZ1t6iwuTDwcQ_LQbq4aCX";
|
|
15919
|
+
function controlPlaneUrl(env = process.env) {
|
|
15920
|
+
return (env.NARO_CONTROL_PLANE_URL ?? DEFAULT_CONTROL_PLANE_URL).replace(
|
|
15921
|
+
/\/$/,
|
|
15922
|
+
""
|
|
15923
|
+
);
|
|
15924
|
+
}
|
|
15925
|
+
|
|
15926
|
+
// ../../packages/sdk/src/callback-server.ts
|
|
15642
15927
|
import { createServer } from "http";
|
|
15643
15928
|
var DONE_HTML = "<html><body><p>\u2705 Logged in. You can close this tab and return to the terminal.</p></body></html>";
|
|
15644
15929
|
var FAIL_HTML = "<html><body><p>Login failed. You can close this tab and return to the terminal.</p></body></html>";
|
|
15645
15930
|
async function startCallbackServer() {
|
|
15646
15931
|
let resolveCode;
|
|
15647
15932
|
let rejectCode;
|
|
15648
|
-
const codePromise = new Promise((
|
|
15649
|
-
resolveCode =
|
|
15933
|
+
const codePromise = new Promise((resolve4, reject) => {
|
|
15934
|
+
resolveCode = resolve4;
|
|
15650
15935
|
rejectCode = reject;
|
|
15651
15936
|
});
|
|
15652
15937
|
const server = createServer((req, res) => {
|
|
@@ -15673,7 +15958,7 @@ async function startCallbackServer() {
|
|
|
15673
15958
|
res.end(DONE_HTML);
|
|
15674
15959
|
resolveCode(code);
|
|
15675
15960
|
});
|
|
15676
|
-
await new Promise((
|
|
15961
|
+
await new Promise((resolve4) => server.listen(0, "127.0.0.1", resolve4));
|
|
15677
15962
|
const port = server.address().port;
|
|
15678
15963
|
return {
|
|
15679
15964
|
port,
|
|
@@ -15696,12 +15981,10 @@ async function startCallbackServer() {
|
|
|
15696
15981
|
};
|
|
15697
15982
|
}
|
|
15698
15983
|
|
|
15699
|
-
// src/supabase-auth.ts
|
|
15700
|
-
var DEFAULT_URL = "https://bymwzjpqfmwsdhtcpgjg.supabase.co";
|
|
15701
|
-
var DEFAULT_KEY = "sb_publishable_qjM6NZsHnGZ1t6iwuTDwcQ_LQbq4aCX";
|
|
15984
|
+
// ../../packages/sdk/src/supabase-auth.ts
|
|
15702
15985
|
function createAuthClient(env = process.env) {
|
|
15703
|
-
const url2 = env.NARO_SUPABASE_URL ??
|
|
15704
|
-
const key = env.NARO_SUPABASE_KEY ??
|
|
15986
|
+
const url2 = env.NARO_SUPABASE_URL ?? DEFAULT_SUPABASE_URL;
|
|
15987
|
+
const key = env.NARO_SUPABASE_KEY ?? DEFAULT_SUPABASE_KEY;
|
|
15705
15988
|
const store = /* @__PURE__ */ new Map();
|
|
15706
15989
|
return createClient(url2, key, {
|
|
15707
15990
|
auth: {
|
|
@@ -15774,70 +16057,660 @@ async function login(deps) {
|
|
|
15774
16057
|
}
|
|
15775
16058
|
}
|
|
15776
16059
|
|
|
15777
|
-
// src/
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
};
|
|
16060
|
+
// ../../packages/sdk/src/auth.ts
|
|
16061
|
+
async function resolveAuth(options = {}) {
|
|
16062
|
+
const env = options.env ?? process.env;
|
|
16063
|
+
const home = options.home ?? homedir2();
|
|
16064
|
+
if (options.explicitToken) {
|
|
16065
|
+
const token = options.explicitToken;
|
|
16066
|
+
return { getAccessToken: async () => token, source: "flag" };
|
|
16067
|
+
}
|
|
16068
|
+
if (env.NARO_TOKEN) {
|
|
16069
|
+
const token = env.NARO_TOKEN;
|
|
16070
|
+
return { getAccessToken: async () => token, source: "env" };
|
|
16071
|
+
}
|
|
16072
|
+
const refresh = options.refresh ?? refreshStoredSession;
|
|
16073
|
+
let session = loadStoredSession(home);
|
|
16074
|
+
if (!session || isExpired(session)) {
|
|
16075
|
+
if (session) {
|
|
16076
|
+
try {
|
|
16077
|
+
session = await refresh(session.refresh_token);
|
|
16078
|
+
saveSession(session, home);
|
|
16079
|
+
} catch {
|
|
16080
|
+
session = null;
|
|
16081
|
+
}
|
|
16082
|
+
}
|
|
16083
|
+
if (!session) {
|
|
16084
|
+
const interactive = options.interactiveLogin;
|
|
16085
|
+
if (!interactive) {
|
|
16086
|
+
throw new NaroError(
|
|
16087
|
+
"NOT_LOGGED_IN",
|
|
16088
|
+
"Not logged in. Run `naro login`, or set NARO_TOKEN."
|
|
16089
|
+
);
|
|
16090
|
+
}
|
|
16091
|
+
interactive.log(
|
|
16092
|
+
"Not logged in \u2014 opening your browser to sign in with GitHub\u2026"
|
|
16093
|
+
);
|
|
16094
|
+
const loginFlow = options.loginFlow ?? ((openBrowser2) => login({ openBrowser: openBrowser2 }));
|
|
16095
|
+
session = await loginFlow((url2) => {
|
|
16096
|
+
interactive.log(`If your browser did not open, visit:
|
|
16097
|
+
${url2}`);
|
|
16098
|
+
interactive.openBrowser(url2);
|
|
16099
|
+
});
|
|
16100
|
+
saveSession(session, home);
|
|
16101
|
+
}
|
|
16102
|
+
}
|
|
16103
|
+
const auth = {
|
|
16104
|
+
source: "session",
|
|
16105
|
+
user: session.user,
|
|
16106
|
+
getAccessToken: async () => {
|
|
16107
|
+
const current = loadStoredSession(home);
|
|
16108
|
+
if (!current) {
|
|
16109
|
+
throw new NaroError(
|
|
16110
|
+
"NOT_LOGGED_IN",
|
|
16111
|
+
"The session was logged out. Run `naro login` again."
|
|
16112
|
+
);
|
|
16113
|
+
}
|
|
16114
|
+
if (!isExpired(current)) return current.access_token;
|
|
16115
|
+
const refreshed = await refresh(current.refresh_token);
|
|
16116
|
+
saveSession(refreshed, home);
|
|
16117
|
+
return refreshed.access_token;
|
|
16118
|
+
}
|
|
16119
|
+
};
|
|
16120
|
+
return auth;
|
|
16121
|
+
}
|
|
16122
|
+
|
|
16123
|
+
// ../../packages/sdk/src/browser.ts
|
|
16124
|
+
import { spawn as spawn2 } from "child_process";
|
|
15784
16125
|
function openBrowser(url2) {
|
|
15785
16126
|
const platform = process.platform;
|
|
15786
16127
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
15787
16128
|
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
15788
|
-
spawn2(command, args, { stdio: "ignore", detached: true })
|
|
15789
|
-
|
|
15790
|
-
async function runLogin(io = stdio) {
|
|
15791
|
-
io.out("Opening your browser to sign in with GitHub...");
|
|
15792
|
-
const session = await login({
|
|
15793
|
-
openBrowser: (url2) => {
|
|
15794
|
-
io.out(`If your browser did not open, visit:
|
|
15795
|
-
${url2}`);
|
|
15796
|
-
openBrowser(url2);
|
|
15797
|
-
}
|
|
16129
|
+
const child = spawn2(command, args, { stdio: "ignore", detached: true });
|
|
16130
|
+
child.on("error", () => {
|
|
15798
16131
|
});
|
|
15799
|
-
|
|
15800
|
-
io.out(`\u2705 Logged in as ${session.user.email ?? session.user.id}`);
|
|
16132
|
+
child.unref();
|
|
15801
16133
|
}
|
|
15802
|
-
|
|
15803
|
-
|
|
15804
|
-
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
15810
|
-
try {
|
|
15811
|
-
session = await refresh(session.refresh_token);
|
|
15812
|
-
saveSession(session, home);
|
|
15813
|
-
} catch {
|
|
15814
|
-
io.err("Session expired. Run `naro login`.");
|
|
15815
|
-
process.exitCode = 1;
|
|
15816
|
-
return;
|
|
15817
|
-
}
|
|
15818
|
-
}
|
|
15819
|
-
const handle = session.user.github ? ` (${session.user.github})` : "";
|
|
15820
|
-
io.out(`Logged in as ${session.user.email ?? session.user.id}${handle}`);
|
|
16134
|
+
|
|
16135
|
+
// ../../packages/sdk/src/types.ts
|
|
16136
|
+
var TERMINAL_DEPLOYMENT_STATUSES = [
|
|
16137
|
+
"READY",
|
|
16138
|
+
"FAILED"
|
|
16139
|
+
];
|
|
16140
|
+
function isTerminalStatus(status) {
|
|
16141
|
+
return TERMINAL_DEPLOYMENT_STATUSES.includes(status);
|
|
15821
16142
|
}
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
16143
|
+
function projectInfo(row) {
|
|
16144
|
+
return {
|
|
16145
|
+
id: row.id,
|
|
16146
|
+
name: row.name,
|
|
16147
|
+
slug: row.slug,
|
|
16148
|
+
subdomain: row.subdomain,
|
|
16149
|
+
teamId: row.team_id,
|
|
16150
|
+
defaultBranch: row.default_branch,
|
|
16151
|
+
rootDirectory: row.root_directory,
|
|
16152
|
+
repositoryUrl: row.repository_url,
|
|
16153
|
+
repositoryFullName: row.repository_full_name ?? null,
|
|
16154
|
+
createdAt: row.created_at,
|
|
16155
|
+
updatedAt: row.updated_at
|
|
16156
|
+
};
|
|
16157
|
+
}
|
|
16158
|
+
function deploymentInfo(row) {
|
|
16159
|
+
return {
|
|
16160
|
+
id: row.id,
|
|
16161
|
+
projectId: row.project_id,
|
|
16162
|
+
status: row.status,
|
|
16163
|
+
branch: row.branch,
|
|
16164
|
+
url: row.preview_url,
|
|
16165
|
+
artifactKey: row.artifact_key ?? null,
|
|
16166
|
+
errorCode: row.error_code,
|
|
16167
|
+
errorMessage: row.error_message,
|
|
16168
|
+
gitCommitSha: row.git_commit_sha,
|
|
16169
|
+
gitCommitMessage: row.git_commit_message,
|
|
16170
|
+
createdAt: row.created_at,
|
|
16171
|
+
startedAt: row.started_at,
|
|
16172
|
+
finishedAt: row.finished_at
|
|
16173
|
+
};
|
|
16174
|
+
}
|
|
16175
|
+
function buildLogEntry(row) {
|
|
16176
|
+
return {
|
|
16177
|
+
sequence: row.sequence,
|
|
16178
|
+
level: row.level,
|
|
16179
|
+
stage: row.stage,
|
|
16180
|
+
message: row.message,
|
|
16181
|
+
createdAt: row.created_at
|
|
16182
|
+
};
|
|
15829
16183
|
}
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
16184
|
+
|
|
16185
|
+
// ../../packages/sdk/src/client.ts
|
|
16186
|
+
var NaroClient = class {
|
|
16187
|
+
baseUrl;
|
|
16188
|
+
getAccessToken;
|
|
16189
|
+
doFetch;
|
|
16190
|
+
teams;
|
|
16191
|
+
projects;
|
|
16192
|
+
deployments;
|
|
16193
|
+
env;
|
|
16194
|
+
domains;
|
|
16195
|
+
logs;
|
|
16196
|
+
constructor(options) {
|
|
16197
|
+
this.baseUrl = (options.baseUrl ?? controlPlaneUrl()).replace(/\/$/, "");
|
|
16198
|
+
this.getAccessToken = options.getAccessToken;
|
|
16199
|
+
this.doFetch = options.fetch ?? fetch;
|
|
16200
|
+
this.teams = new TeamsApi(this);
|
|
16201
|
+
this.projects = new ProjectsApi(this);
|
|
16202
|
+
this.deployments = new DeploymentsApi(this);
|
|
16203
|
+
this.env = new EnvApi(this);
|
|
16204
|
+
this.domains = new DomainsApi(this);
|
|
16205
|
+
this.logs = new LogsApi(this);
|
|
16206
|
+
}
|
|
16207
|
+
async request(path, options) {
|
|
16208
|
+
const token = await this.getAccessToken();
|
|
16209
|
+
let response;
|
|
15833
16210
|
try {
|
|
15834
|
-
await
|
|
15835
|
-
|
|
16211
|
+
response = await this.doFetch(`${this.baseUrl}${path}`, {
|
|
16212
|
+
method: options.method ?? "GET",
|
|
16213
|
+
headers: {
|
|
16214
|
+
Authorization: `Bearer ${token}`,
|
|
16215
|
+
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
16216
|
+
},
|
|
16217
|
+
...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
|
|
16218
|
+
});
|
|
16219
|
+
} catch (error51) {
|
|
16220
|
+
throw new NaroError(
|
|
16221
|
+
"NETWORK_ERROR",
|
|
16222
|
+
`${options.context} failed: could not reach ${this.baseUrl}.`,
|
|
16223
|
+
{ cause: error51 }
|
|
16224
|
+
);
|
|
15836
16225
|
}
|
|
16226
|
+
if (!response.ok) throw await errorFromResponse(response, options.context);
|
|
16227
|
+
return await response.json();
|
|
15837
16228
|
}
|
|
15838
|
-
|
|
15839
|
-
|
|
15840
|
-
|
|
16229
|
+
};
|
|
16230
|
+
var TeamsApi = class {
|
|
16231
|
+
constructor(client) {
|
|
16232
|
+
this.client = client;
|
|
16233
|
+
}
|
|
16234
|
+
async list() {
|
|
16235
|
+
const body = await this.client.request(
|
|
16236
|
+
"/api/teams",
|
|
16237
|
+
{ context: "List teams" }
|
|
16238
|
+
);
|
|
16239
|
+
return body.teams;
|
|
16240
|
+
}
|
|
16241
|
+
};
|
|
16242
|
+
var ProjectsApi = class {
|
|
16243
|
+
constructor(client) {
|
|
16244
|
+
this.client = client;
|
|
16245
|
+
}
|
|
16246
|
+
async list(teamSlug) {
|
|
16247
|
+
const body = await this.client.request(
|
|
16248
|
+
`/api/projects?team=${encodeURIComponent(teamSlug)}`,
|
|
16249
|
+
{ context: "List projects" }
|
|
16250
|
+
);
|
|
16251
|
+
return body.projects.map(projectInfo);
|
|
16252
|
+
}
|
|
16253
|
+
async get(projectId) {
|
|
16254
|
+
const body = await this.client.request(
|
|
16255
|
+
`/api/projects/${encodeURIComponent(projectId)}`,
|
|
16256
|
+
{ context: "Read project" }
|
|
16257
|
+
);
|
|
16258
|
+
return projectInfo(body.project);
|
|
16259
|
+
}
|
|
16260
|
+
};
|
|
16261
|
+
var DeploymentsApi = class {
|
|
16262
|
+
constructor(client) {
|
|
16263
|
+
this.client = client;
|
|
16264
|
+
}
|
|
16265
|
+
async list(projectId) {
|
|
16266
|
+
const body = await this.client.request(`/api/projects/${encodeURIComponent(projectId)}/deployments`, {
|
|
16267
|
+
context: "List deployments"
|
|
16268
|
+
});
|
|
16269
|
+
return body.deployments.map(deploymentInfo);
|
|
16270
|
+
}
|
|
16271
|
+
async get(deploymentId) {
|
|
16272
|
+
const body = await this.client.request(`/api/deployments/${encodeURIComponent(deploymentId)}`, {
|
|
16273
|
+
context: "Read deployment"
|
|
16274
|
+
});
|
|
16275
|
+
return {
|
|
16276
|
+
deployment: deploymentInfo(body.deployment),
|
|
16277
|
+
logs: body.logs.map(buildLogEntry)
|
|
16278
|
+
};
|
|
16279
|
+
}
|
|
16280
|
+
/**
|
|
16281
|
+
* Starts a CLI deployment: the control plane creates/loads the project,
|
|
16282
|
+
* records the deployment and returns a callback token; the caller builds
|
|
16283
|
+
* locally and uploads the artifact.
|
|
16284
|
+
*/
|
|
16285
|
+
async createCli(input) {
|
|
16286
|
+
return this.client.request("/api/cli/deployments", {
|
|
16287
|
+
method: "POST",
|
|
16288
|
+
body: input,
|
|
16289
|
+
context: "Create deployment"
|
|
16290
|
+
});
|
|
16291
|
+
}
|
|
16292
|
+
/**
|
|
16293
|
+
* Queues a server-side git build (GitHub-connected projects): clone,
|
|
16294
|
+
* build and deploy run on the platform.
|
|
16295
|
+
*/
|
|
16296
|
+
async createRemote(projectId, options = {}) {
|
|
16297
|
+
const body = await this.client.request(
|
|
16298
|
+
`/api/projects/${encodeURIComponent(projectId)}/deployments`,
|
|
16299
|
+
{
|
|
16300
|
+
method: "POST",
|
|
16301
|
+
// createDeploymentSchema accepts string | undefined — never null.
|
|
16302
|
+
body: options.branch ? { branch: options.branch } : {},
|
|
16303
|
+
context: "Create deployment"
|
|
16304
|
+
}
|
|
16305
|
+
);
|
|
16306
|
+
return deploymentInfo(body.deployment);
|
|
16307
|
+
}
|
|
16308
|
+
/**
|
|
16309
|
+
* Picks the deployment `naro rollback` restores: the newest READY
|
|
16310
|
+
* production deployment serving a DIFFERENT artifact than the current one.
|
|
16311
|
+
* Comparing artifacts (not row order) keeps repeated rollbacks from
|
|
16312
|
+
* ping-ponging between the same two rows — promoted rows share their
|
|
16313
|
+
* source's artifact.
|
|
16314
|
+
*/
|
|
16315
|
+
async rollbackTarget(projectId, defaultBranch) {
|
|
16316
|
+
const deployments = await this.list(projectId);
|
|
16317
|
+
const production = deployments.filter(
|
|
16318
|
+
(deployment) => deployment.status === "READY" && deployment.branch === defaultBranch
|
|
16319
|
+
);
|
|
16320
|
+
const current = production[0];
|
|
16321
|
+
if (!current) {
|
|
16322
|
+
throw new NaroError(
|
|
16323
|
+
"DEPLOYMENT_NOT_FOUND",
|
|
16324
|
+
"No production deployment to roll back from."
|
|
16325
|
+
);
|
|
16326
|
+
}
|
|
16327
|
+
const differs = (candidate) => current.artifactKey && candidate.artifactKey ? candidate.artifactKey !== current.artifactKey : candidate.id !== current.id;
|
|
16328
|
+
return { current, target: production.slice(1).find(differs) ?? null };
|
|
16329
|
+
}
|
|
16330
|
+
/**
|
|
16331
|
+
* Redeploys an existing READY deployment's artifact to production.
|
|
16332
|
+
* Used by both `naro promote` (preview → production) and `naro rollback`
|
|
16333
|
+
* (older production build → production).
|
|
16334
|
+
*/
|
|
16335
|
+
async promote(projectId, deploymentId) {
|
|
16336
|
+
const body = await this.client.request(
|
|
16337
|
+
`/api/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/promote`,
|
|
16338
|
+
{ method: "POST", context: "Promote deployment" }
|
|
16339
|
+
);
|
|
16340
|
+
return deploymentInfo(body.deployment);
|
|
16341
|
+
}
|
|
16342
|
+
/** Polls a deployment until READY/FAILED, streaming new build logs. */
|
|
16343
|
+
async wait(deploymentId, options = {}) {
|
|
16344
|
+
const intervalMs = options.intervalMs ?? 2500;
|
|
16345
|
+
const timeoutMs = options.timeoutMs ?? 15 * 6e4;
|
|
16346
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
|
|
16347
|
+
const startedAt = Date.now();
|
|
16348
|
+
let lastStatus = null;
|
|
16349
|
+
let lastSequence = -1;
|
|
16350
|
+
for (; ; ) {
|
|
16351
|
+
const detail = await this.get(deploymentId);
|
|
16352
|
+
for (const log2 of detail.logs) {
|
|
16353
|
+
if (log2.sequence > lastSequence) {
|
|
16354
|
+
lastSequence = log2.sequence;
|
|
16355
|
+
options.onLog?.(log2);
|
|
16356
|
+
}
|
|
16357
|
+
}
|
|
16358
|
+
if (detail.deployment.status !== lastStatus) {
|
|
16359
|
+
lastStatus = detail.deployment.status;
|
|
16360
|
+
options.onStatus?.(lastStatus);
|
|
16361
|
+
}
|
|
16362
|
+
if (isTerminalStatus(detail.deployment.status)) return detail;
|
|
16363
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
16364
|
+
throw new NaroError(
|
|
16365
|
+
"TIMEOUT",
|
|
16366
|
+
`Deployment ${deploymentId} did not finish within ${String(Math.round(timeoutMs / 1e3))}s.`,
|
|
16367
|
+
{ details: { deploymentId } }
|
|
16368
|
+
);
|
|
16369
|
+
}
|
|
16370
|
+
await sleep(intervalMs);
|
|
16371
|
+
}
|
|
16372
|
+
}
|
|
16373
|
+
};
|
|
16374
|
+
var EnvApi = class {
|
|
16375
|
+
constructor(client) {
|
|
16376
|
+
this.client = client;
|
|
16377
|
+
}
|
|
16378
|
+
async list(projectId, options = {}) {
|
|
16379
|
+
const query = options.reveal ? "?reveal=true" : "";
|
|
16380
|
+
const body = await this.client.request(
|
|
16381
|
+
`/api/projects/${encodeURIComponent(projectId)}/env${query}`,
|
|
16382
|
+
{ context: "List environment variables" }
|
|
16383
|
+
);
|
|
16384
|
+
return body.envVars;
|
|
16385
|
+
}
|
|
16386
|
+
async set(projectId, entries) {
|
|
16387
|
+
await this.client.request(
|
|
16388
|
+
`/api/projects/${encodeURIComponent(projectId)}/env`,
|
|
16389
|
+
{
|
|
16390
|
+
method: "POST",
|
|
16391
|
+
body: {
|
|
16392
|
+
entries: entries.map((entry) => ({
|
|
16393
|
+
key: entry.key,
|
|
16394
|
+
value: entry.value,
|
|
16395
|
+
target: entry.target ?? "all",
|
|
16396
|
+
isSecret: entry.isSecret ?? true
|
|
16397
|
+
}))
|
|
16398
|
+
},
|
|
16399
|
+
context: "Set environment variable"
|
|
16400
|
+
}
|
|
16401
|
+
);
|
|
16402
|
+
}
|
|
16403
|
+
async removeById(projectId, envVarId) {
|
|
16404
|
+
await this.client.request(
|
|
16405
|
+
`/api/projects/${encodeURIComponent(projectId)}/env/${encodeURIComponent(envVarId)}`,
|
|
16406
|
+
{ method: "DELETE", context: "Remove environment variable" }
|
|
16407
|
+
);
|
|
16408
|
+
}
|
|
16409
|
+
/** Removes every row for a key (optionally narrowed to one target). */
|
|
16410
|
+
async remove(projectId, key, target) {
|
|
16411
|
+
const rows = await this.list(projectId);
|
|
16412
|
+
const matches = rows.filter(
|
|
16413
|
+
(row) => row.key === key && (target === void 0 || row.target === target)
|
|
16414
|
+
);
|
|
16415
|
+
if (matches.length === 0) {
|
|
16416
|
+
throw new NaroError(
|
|
16417
|
+
"ENV_NOT_FOUND",
|
|
16418
|
+
target ? `No ${key} variable found for the ${target} environment.` : `No ${key} variable found.`,
|
|
16419
|
+
{ details: { key, ...target ? { target } : {} } }
|
|
16420
|
+
);
|
|
16421
|
+
}
|
|
16422
|
+
for (const row of matches) await this.removeById(projectId, row.id);
|
|
16423
|
+
return matches.length;
|
|
16424
|
+
}
|
|
16425
|
+
};
|
|
16426
|
+
var DomainsApi = class {
|
|
16427
|
+
constructor(client) {
|
|
16428
|
+
this.client = client;
|
|
16429
|
+
}
|
|
16430
|
+
async list(projectId) {
|
|
16431
|
+
const body = await this.client.request(
|
|
16432
|
+
`/api/projects/${encodeURIComponent(projectId)}/domains`,
|
|
16433
|
+
{ context: "List domains" }
|
|
16434
|
+
);
|
|
16435
|
+
return body.domains;
|
|
16436
|
+
}
|
|
16437
|
+
async add(projectId, hostname3) {
|
|
16438
|
+
const body = await this.client.request(
|
|
16439
|
+
`/api/projects/${encodeURIComponent(projectId)}/domains`,
|
|
16440
|
+
{ method: "POST", body: { hostname: hostname3 }, context: "Add domain" }
|
|
16441
|
+
);
|
|
16442
|
+
return body.domain;
|
|
16443
|
+
}
|
|
16444
|
+
async refresh(projectId, domainId) {
|
|
16445
|
+
const body = await this.client.request(
|
|
16446
|
+
`/api/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domainId)}`,
|
|
16447
|
+
{ method: "PATCH", context: "Refresh domain" }
|
|
16448
|
+
);
|
|
16449
|
+
return body.domain;
|
|
16450
|
+
}
|
|
16451
|
+
async removeById(projectId, domainId) {
|
|
16452
|
+
await this.client.request(
|
|
16453
|
+
`/api/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domainId)}`,
|
|
16454
|
+
{ method: "DELETE", context: "Remove domain" }
|
|
16455
|
+
);
|
|
16456
|
+
}
|
|
16457
|
+
/** Removes a domain by hostname. */
|
|
16458
|
+
async remove(projectId, hostname3) {
|
|
16459
|
+
const domains = await this.list(projectId);
|
|
16460
|
+
const match = domains.find((domain2) => domain2.hostname === hostname3);
|
|
16461
|
+
if (!match) {
|
|
16462
|
+
throw new NaroError(
|
|
16463
|
+
"DOMAIN_NOT_FOUND",
|
|
16464
|
+
`${hostname3} is not attached to this project.`,
|
|
16465
|
+
{ details: { hostname: hostname3 } }
|
|
16466
|
+
);
|
|
16467
|
+
}
|
|
16468
|
+
await this.removeById(projectId, match.id);
|
|
16469
|
+
}
|
|
16470
|
+
};
|
|
16471
|
+
var LogsApi = class {
|
|
16472
|
+
constructor(client) {
|
|
16473
|
+
this.client = client;
|
|
16474
|
+
}
|
|
16475
|
+
async runtime(projectId, options = {}) {
|
|
16476
|
+
const params = new URLSearchParams();
|
|
16477
|
+
params.set("env", options.environment ?? "production");
|
|
16478
|
+
if (options.level) params.set("level", options.level);
|
|
16479
|
+
const body = await this.client.request(
|
|
16480
|
+
`/api/projects/${encodeURIComponent(projectId)}/logs?${params.toString()}`,
|
|
16481
|
+
{ context: "Read runtime logs" }
|
|
16482
|
+
);
|
|
16483
|
+
return body.entries;
|
|
16484
|
+
}
|
|
16485
|
+
};
|
|
16486
|
+
|
|
16487
|
+
// ../../packages/sdk/src/dotenv.ts
|
|
16488
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
16489
|
+
import { join as join10 } from "path";
|
|
16490
|
+
function parseDotenv(text) {
|
|
16491
|
+
const out = {};
|
|
16492
|
+
for (const line of text.split(/\r?\n/)) {
|
|
16493
|
+
const trimmed = line.trim();
|
|
16494
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
16495
|
+
const eq = trimmed.indexOf("=");
|
|
16496
|
+
if (eq <= 0) continue;
|
|
16497
|
+
const key = trimmed.slice(0, eq).trim();
|
|
16498
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
16499
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))
|
|
16500
|
+
value = value.slice(1, -1);
|
|
16501
|
+
out[key] = value;
|
|
16502
|
+
}
|
|
16503
|
+
return out;
|
|
16504
|
+
}
|
|
16505
|
+
function readProjectEnv(dir) {
|
|
16506
|
+
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
16507
|
+
const path = join10(dir, name);
|
|
16508
|
+
if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
|
|
16509
|
+
}
|
|
16510
|
+
return {};
|
|
16511
|
+
}
|
|
16512
|
+
function serializeDotenv(vars) {
|
|
16513
|
+
const needsQuotes = /[\s"'#$`\\]/;
|
|
16514
|
+
return `${Object.entries(vars).map(
|
|
16515
|
+
([key, value]) => needsQuotes.test(value) ? `${key}="${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"` : `${key}=${value}`
|
|
16516
|
+
).join("\n")}
|
|
16517
|
+
`;
|
|
16518
|
+
}
|
|
16519
|
+
|
|
16520
|
+
// ../../packages/sdk/src/framework.ts
|
|
16521
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
16522
|
+
import { join as join11 } from "path";
|
|
16523
|
+
function readPackageJson2(dir) {
|
|
16524
|
+
try {
|
|
16525
|
+
return JSON.parse(
|
|
16526
|
+
readFileSync3(join11(dir, "package.json"), "utf8")
|
|
16527
|
+
);
|
|
16528
|
+
} catch {
|
|
16529
|
+
return null;
|
|
16530
|
+
}
|
|
16531
|
+
}
|
|
16532
|
+
function hasDependency(pkg, name) {
|
|
16533
|
+
return Boolean(pkg?.dependencies?.[name] ?? pkg?.devDependencies?.[name]);
|
|
16534
|
+
}
|
|
16535
|
+
function hasConfig(dir, base2, evidence) {
|
|
16536
|
+
for (const ext of ["js", "mjs", "cjs", "ts", "mts"]) {
|
|
16537
|
+
if (existsSync2(join11(dir, `${base2}.${ext}`))) {
|
|
16538
|
+
evidence.push(`${base2}.${ext}`);
|
|
16539
|
+
return true;
|
|
16540
|
+
}
|
|
16541
|
+
}
|
|
16542
|
+
return false;
|
|
16543
|
+
}
|
|
16544
|
+
function detectFramework2(dir) {
|
|
16545
|
+
const evidence = [];
|
|
16546
|
+
const pkg = readPackageJson2(dir);
|
|
16547
|
+
if (hasDependency(pkg, "next") || hasConfig(dir, "next.config", evidence)) {
|
|
16548
|
+
if (hasDependency(pkg, "next")) evidence.push("package.json: next");
|
|
16549
|
+
if (existsSync2(join11(dir, "app"))) evidence.push("app/");
|
|
16550
|
+
if (existsSync2(join11(dir, "pages"))) evidence.push("pages/");
|
|
16551
|
+
return {
|
|
16552
|
+
framework: "nextjs",
|
|
16553
|
+
name: "Next.js",
|
|
16554
|
+
buildCommand: "next build",
|
|
16555
|
+
outputDirectory: ".next",
|
|
16556
|
+
evidence,
|
|
16557
|
+
deployable: true
|
|
16558
|
+
};
|
|
16559
|
+
}
|
|
16560
|
+
if (hasDependency(pkg, "nuxt") || hasConfig(dir, "nuxt.config", evidence)) {
|
|
16561
|
+
if (hasDependency(pkg, "nuxt")) evidence.push("package.json: nuxt");
|
|
16562
|
+
return {
|
|
16563
|
+
framework: "nuxt",
|
|
16564
|
+
name: "Nuxt",
|
|
16565
|
+
buildCommand: "nuxt build",
|
|
16566
|
+
outputDirectory: ".output",
|
|
16567
|
+
evidence,
|
|
16568
|
+
deployable: false
|
|
16569
|
+
};
|
|
16570
|
+
}
|
|
16571
|
+
if (hasDependency(pkg, "astro") || hasConfig(dir, "astro.config", evidence)) {
|
|
16572
|
+
if (hasDependency(pkg, "astro")) evidence.push("package.json: astro");
|
|
16573
|
+
return {
|
|
16574
|
+
framework: "astro",
|
|
16575
|
+
name: "Astro",
|
|
16576
|
+
buildCommand: "astro build",
|
|
16577
|
+
outputDirectory: "dist",
|
|
16578
|
+
evidence,
|
|
16579
|
+
deployable: false
|
|
16580
|
+
};
|
|
16581
|
+
}
|
|
16582
|
+
if (hasDependency(pkg, "vite") || hasConfig(dir, "vite.config", evidence)) {
|
|
16583
|
+
if (hasDependency(pkg, "vite")) evidence.push("package.json: vite");
|
|
16584
|
+
return {
|
|
16585
|
+
framework: "vite",
|
|
16586
|
+
name: "Vite",
|
|
16587
|
+
buildCommand: "vite build",
|
|
16588
|
+
outputDirectory: "dist",
|
|
16589
|
+
evidence,
|
|
16590
|
+
deployable: true
|
|
16591
|
+
};
|
|
16592
|
+
}
|
|
16593
|
+
if (hasDependency(pkg, "react-scripts")) {
|
|
16594
|
+
evidence.push("package.json: react-scripts");
|
|
16595
|
+
return {
|
|
16596
|
+
framework: "react",
|
|
16597
|
+
name: "Create React App",
|
|
16598
|
+
buildCommand: "react-scripts build",
|
|
16599
|
+
outputDirectory: "build",
|
|
16600
|
+
evidence,
|
|
16601
|
+
deployable: true
|
|
16602
|
+
};
|
|
16603
|
+
}
|
|
16604
|
+
if (!pkg && existsSync2(join11(dir, "index.html"))) {
|
|
16605
|
+
evidence.push("index.html (no package.json)");
|
|
16606
|
+
return {
|
|
16607
|
+
framework: "static",
|
|
16608
|
+
name: "Static site",
|
|
16609
|
+
buildCommand: null,
|
|
16610
|
+
outputDirectory: ".",
|
|
16611
|
+
evidence,
|
|
16612
|
+
deployable: true
|
|
16613
|
+
};
|
|
16614
|
+
}
|
|
16615
|
+
return {
|
|
16616
|
+
framework: "unknown",
|
|
16617
|
+
name: "Unknown",
|
|
16618
|
+
buildCommand: null,
|
|
16619
|
+
outputDirectory: null,
|
|
16620
|
+
evidence,
|
|
16621
|
+
deployable: false
|
|
16622
|
+
};
|
|
16623
|
+
}
|
|
16624
|
+
|
|
16625
|
+
// ../../packages/sdk/src/link.ts
|
|
16626
|
+
import {
|
|
16627
|
+
existsSync as existsSync3,
|
|
16628
|
+
mkdirSync as mkdirSync2,
|
|
16629
|
+
readFileSync as readFileSync4,
|
|
16630
|
+
rmSync as rmSync2,
|
|
16631
|
+
writeFileSync as writeFileSync2
|
|
16632
|
+
} from "fs";
|
|
16633
|
+
import { join as join12 } from "path";
|
|
16634
|
+
function linkDir(dir) {
|
|
16635
|
+
return join12(dir, ".naro");
|
|
16636
|
+
}
|
|
16637
|
+
function linkFile(dir) {
|
|
16638
|
+
return join12(linkDir(dir), "project.json");
|
|
16639
|
+
}
|
|
16640
|
+
function readProjectLink(dir) {
|
|
16641
|
+
try {
|
|
16642
|
+
const parsed = JSON.parse(readFileSync4(linkFile(dir), "utf8"));
|
|
16643
|
+
if (typeof parsed.projectId !== "string" || parsed.projectId.length === 0)
|
|
16644
|
+
return null;
|
|
16645
|
+
return {
|
|
16646
|
+
projectId: parsed.projectId,
|
|
16647
|
+
...typeof parsed.teamSlug === "string" ? { teamSlug: parsed.teamSlug } : {},
|
|
16648
|
+
...typeof parsed.projectName === "string" ? { projectName: parsed.projectName } : {}
|
|
16649
|
+
};
|
|
16650
|
+
} catch {
|
|
16651
|
+
return null;
|
|
16652
|
+
}
|
|
16653
|
+
}
|
|
16654
|
+
function writeProjectLink(dir, link) {
|
|
16655
|
+
mkdirSync2(linkDir(dir), { recursive: true });
|
|
16656
|
+
writeFileSync2(linkFile(dir), `${JSON.stringify(link, null, 2)}
|
|
16657
|
+
`);
|
|
16658
|
+
writeFileSync2(
|
|
16659
|
+
join12(linkDir(dir), "README.txt"),
|
|
16660
|
+
"This directory links the project to Naro and should not be committed.\n"
|
|
16661
|
+
);
|
|
16662
|
+
}
|
|
16663
|
+
function removeProjectLink(dir) {
|
|
16664
|
+
if (!existsSync3(linkDir(dir))) return false;
|
|
16665
|
+
rmSync2(linkDir(dir), { recursive: true, force: true });
|
|
16666
|
+
return true;
|
|
16667
|
+
}
|
|
16668
|
+
function ensureGitignoreHasNaro(dir) {
|
|
16669
|
+
const gitignorePath = join12(dir, ".gitignore");
|
|
16670
|
+
const hasGitignore = existsSync3(gitignorePath);
|
|
16671
|
+
if (!hasGitignore && !existsSync3(join12(dir, ".git"))) return false;
|
|
16672
|
+
const current = hasGitignore ? readFileSync4(gitignorePath, "utf8") : "";
|
|
16673
|
+
const covered = current.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".naro" || line === ".naro/" || line === "/.naro");
|
|
16674
|
+
if (covered) return false;
|
|
16675
|
+
const suffix = current.length === 0 || current.endsWith("\n") ? "" : "\n";
|
|
16676
|
+
writeFileSync2(gitignorePath, `${current}${suffix}.naro
|
|
16677
|
+
`);
|
|
16678
|
+
return true;
|
|
16679
|
+
}
|
|
16680
|
+
|
|
16681
|
+
// src/git-info.ts
|
|
16682
|
+
import { execFile } from "child_process";
|
|
16683
|
+
var SUPPORTED_REMOTE = [
|
|
16684
|
+
/^https:\/\/[^\s]+$/,
|
|
16685
|
+
/^ssh:\/\/[^\s]+$/,
|
|
16686
|
+
/^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
|
|
16687
|
+
];
|
|
16688
|
+
var defaultExec = (args, cwd) => new Promise((resolve4, reject) => {
|
|
16689
|
+
execFile("git", args, { cwd }, (error51, stdout) => {
|
|
16690
|
+
if (error51) reject(error51);
|
|
16691
|
+
else resolve4(stdout);
|
|
16692
|
+
});
|
|
16693
|
+
});
|
|
16694
|
+
async function tryExec(exec, args, cwd) {
|
|
16695
|
+
try {
|
|
16696
|
+
const out = (await exec(args, cwd)).trim();
|
|
16697
|
+
return out.length > 0 ? out : null;
|
|
16698
|
+
} catch {
|
|
16699
|
+
return null;
|
|
16700
|
+
}
|
|
16701
|
+
}
|
|
16702
|
+
async function detectGitInfo(dir, exec = defaultExec) {
|
|
16703
|
+
const remote = await tryExec(exec, ["remote", "get-url", "origin"], dir);
|
|
16704
|
+
const branch = await tryExec(
|
|
16705
|
+
exec,
|
|
16706
|
+
["rev-parse", "--abbrev-ref", "HEAD"],
|
|
16707
|
+
dir
|
|
16708
|
+
);
|
|
16709
|
+
return {
|
|
16710
|
+
repositoryUrl: remote !== null && SUPPORTED_REMOTE.some((re) => re.test(remote)) ? remote : null,
|
|
16711
|
+
branch: branch !== null && branch !== "HEAD" ? branch : null
|
|
16712
|
+
};
|
|
16713
|
+
}
|
|
15841
16714
|
|
|
15842
16715
|
// src/upload.ts
|
|
15843
16716
|
function base(opts) {
|
|
@@ -15854,12 +16727,52 @@ async function authed(opts, path, init) {
|
|
|
15854
16727
|
);
|
|
15855
16728
|
return response;
|
|
15856
16729
|
}
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
16730
|
+
function uploadPorts(opts) {
|
|
16731
|
+
const doFetch = opts.fetch ?? fetch;
|
|
16732
|
+
return {
|
|
16733
|
+
resolvePutUrls: async (entries) => {
|
|
16734
|
+
try {
|
|
16735
|
+
const response = await authed(opts, "/artifacts/urls", {
|
|
16736
|
+
method: "POST",
|
|
16737
|
+
headers: { "Content-Type": "application/json" },
|
|
16738
|
+
body: JSON.stringify({
|
|
16739
|
+
files: entries.map((entry) => ({
|
|
16740
|
+
kind: entry.kind,
|
|
16741
|
+
name: entry.file.name
|
|
16742
|
+
}))
|
|
16743
|
+
})
|
|
16744
|
+
});
|
|
16745
|
+
return parseArtifactPutUrls(await response.json());
|
|
16746
|
+
} catch {
|
|
16747
|
+
return null;
|
|
16748
|
+
}
|
|
16749
|
+
},
|
|
16750
|
+
putSigned: async (url2, entry) => {
|
|
16751
|
+
const response = await doFetch(url2, {
|
|
16752
|
+
method: "PUT",
|
|
16753
|
+
headers: { "Content-Type": entry.file.contentType },
|
|
16754
|
+
body: entry.file.data
|
|
16755
|
+
});
|
|
16756
|
+
if (!response.ok)
|
|
16757
|
+
throw new Error(
|
|
16758
|
+
`Artifact upload returned ${String(response.status)} for ${entry.file.name}`
|
|
16759
|
+
);
|
|
16760
|
+
},
|
|
16761
|
+
putProxied: async (entry) => {
|
|
16762
|
+
await authed(
|
|
16763
|
+
opts,
|
|
16764
|
+
`/artifacts/${entry.kind}/${encodeURIComponent(entry.file.name)}`,
|
|
16765
|
+
{
|
|
16766
|
+
method: "PUT",
|
|
16767
|
+
headers: {
|
|
16768
|
+
"Content-Type": entry.file.contentType,
|
|
16769
|
+
"X-Artifact-Name": entry.file.name
|
|
16770
|
+
},
|
|
16771
|
+
body: entry.file.data
|
|
16772
|
+
}
|
|
16773
|
+
);
|
|
16774
|
+
}
|
|
16775
|
+
};
|
|
15863
16776
|
}
|
|
15864
16777
|
function manifest(artifact) {
|
|
15865
16778
|
const meta3 = ({ name, contentType: contentType2, data }) => ({
|
|
@@ -15878,13 +16791,12 @@ function manifest(artifact) {
|
|
|
15878
16791
|
})),
|
|
15879
16792
|
compatibilityDate: artifact.compatibilityDate,
|
|
15880
16793
|
compatibilityFlags: artifact.compatibilityFlags,
|
|
15881
|
-
assetsBinding: artifact.assetsBinding
|
|
16794
|
+
assetsBinding: artifact.assetsBinding,
|
|
16795
|
+
...artifact.assetsConfig ? { assetsConfig: artifact.assetsConfig } : {}
|
|
15882
16796
|
};
|
|
15883
16797
|
}
|
|
15884
16798
|
async function uploadArtifact(artifact, opts) {
|
|
15885
|
-
|
|
15886
|
-
await put(opts, "module", file2);
|
|
15887
|
-
for (const file2 of artifact.assets) await put(opts, "asset", file2);
|
|
16799
|
+
await uploadArtifactFiles(artifact, uploadPorts(opts));
|
|
15888
16800
|
const response = await authed(opts, "/artifacts/complete", {
|
|
15889
16801
|
method: "POST",
|
|
15890
16802
|
headers: { "Content-Type": "application/json" },
|
|
@@ -15900,63 +16812,11 @@ async function uploadArtifact(artifact, opts) {
|
|
|
15900
16812
|
}
|
|
15901
16813
|
|
|
15902
16814
|
// src/deploy.ts
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
const response = await doFetch(
|
|
15906
|
-
`${opts.controlPlaneUrl.replace(/\/$/, "")}/api/cli/deployments`,
|
|
15907
|
-
{
|
|
15908
|
-
method: "POST",
|
|
15909
|
-
headers: {
|
|
15910
|
-
"Content-Type": "application/json",
|
|
15911
|
-
Authorization: `Bearer ${accessToken}`
|
|
15912
|
-
},
|
|
15913
|
-
body: JSON.stringify({
|
|
15914
|
-
name: opts.name,
|
|
15915
|
-
production: opts.production,
|
|
15916
|
-
...opts.repositoryUrl != null && {
|
|
15917
|
-
repositoryUrl: opts.repositoryUrl
|
|
15918
|
-
},
|
|
15919
|
-
...opts.branch != null && { branch: opts.branch },
|
|
15920
|
-
...opts.rootDirectory != null && {
|
|
15921
|
-
rootDirectory: opts.rootDirectory
|
|
15922
|
-
},
|
|
15923
|
-
...opts.env !== void 0 && { env: opts.env }
|
|
15924
|
-
})
|
|
15925
|
-
}
|
|
15926
|
-
);
|
|
15927
|
-
if (!response.ok) {
|
|
15928
|
-
if (response.status === 401)
|
|
15929
|
-
throw new Error("Session expired or invalid. Run `naro login`.");
|
|
15930
|
-
throw new Error(
|
|
15931
|
-
`Could not start deployment: control plane returned ${String(response.status)}.`
|
|
15932
|
-
);
|
|
15933
|
-
}
|
|
15934
|
-
return await response.json();
|
|
16815
|
+
function parseDotenv2(text) {
|
|
16816
|
+
return parseDotenv(text);
|
|
15935
16817
|
}
|
|
15936
|
-
function
|
|
15937
|
-
return (
|
|
15938
|
-
}
|
|
15939
|
-
async function requireSessionOrLogin(log2, home = homedir3(), loginFlow = () => login({
|
|
15940
|
-
openBrowser: (url2) => {
|
|
15941
|
-
log2(`If your browser did not open, visit:
|
|
15942
|
-
${url2}`);
|
|
15943
|
-
openBrowser(url2);
|
|
15944
|
-
}
|
|
15945
|
-
})) {
|
|
15946
|
-
const stored = loadStoredSession(home);
|
|
15947
|
-
if (stored) {
|
|
15948
|
-
if (!isExpired(stored)) return stored;
|
|
15949
|
-
try {
|
|
15950
|
-
const refreshed = await refreshStoredSession(stored.refresh_token);
|
|
15951
|
-
saveSession(refreshed, home);
|
|
15952
|
-
return refreshed;
|
|
15953
|
-
} catch {
|
|
15954
|
-
}
|
|
15955
|
-
}
|
|
15956
|
-
log2("Not logged in \u2014 opening your browser to sign in with GitHub\u2026");
|
|
15957
|
-
const session = await loginFlow();
|
|
15958
|
-
saveSession(session, home);
|
|
15959
|
-
return session;
|
|
16818
|
+
function readProjectEnv2(dir) {
|
|
16819
|
+
return readProjectEnv(dir);
|
|
15960
16820
|
}
|
|
15961
16821
|
function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
|
|
15962
16822
|
const post = (body) => fetchImpl(`${cp2}/api/internal/deployments/${deploymentId}/events`, {
|
|
@@ -15968,36 +16828,28 @@ function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
|
|
|
15968
16828
|
body: JSON.stringify(body)
|
|
15969
16829
|
}).then(() => {
|
|
15970
16830
|
});
|
|
16831
|
+
const postStatus = async (body) => {
|
|
16832
|
+
let lastError;
|
|
16833
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
16834
|
+
try {
|
|
16835
|
+
return await post(body);
|
|
16836
|
+
} catch (error51) {
|
|
16837
|
+
lastError = error51;
|
|
16838
|
+
await new Promise(
|
|
16839
|
+
(resolve4) => setTimeout(resolve4, 500 * (attempt + 1))
|
|
16840
|
+
);
|
|
16841
|
+
}
|
|
16842
|
+
}
|
|
16843
|
+
throw lastError;
|
|
16844
|
+
};
|
|
15971
16845
|
return {
|
|
15972
|
-
onStatus: (status) =>
|
|
16846
|
+
onStatus: (status) => postStatus({ type: "status", status }),
|
|
15973
16847
|
onLog: (log2) => post({ type: "log", log: log2 }).catch(() => {
|
|
15974
16848
|
}),
|
|
15975
16849
|
onMetadata: (metadata) => post({ type: "metadata", metadata }).catch(() => {
|
|
15976
16850
|
})
|
|
15977
16851
|
};
|
|
15978
16852
|
}
|
|
15979
|
-
function parseDotenv(text) {
|
|
15980
|
-
const out = {};
|
|
15981
|
-
for (const line of text.split(/\r?\n/)) {
|
|
15982
|
-
const trimmed = line.trim();
|
|
15983
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
15984
|
-
const eq = trimmed.indexOf("=");
|
|
15985
|
-
if (eq <= 0) continue;
|
|
15986
|
-
const key = trimmed.slice(0, eq).trim();
|
|
15987
|
-
let value = trimmed.slice(eq + 1).trim();
|
|
15988
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))
|
|
15989
|
-
value = value.slice(1, -1);
|
|
15990
|
-
out[key] = value;
|
|
15991
|
-
}
|
|
15992
|
-
return out;
|
|
15993
|
-
}
|
|
15994
|
-
function readProjectEnv(dir) {
|
|
15995
|
-
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
15996
|
-
const path = join9(dir, name);
|
|
15997
|
-
if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
|
|
15998
|
-
}
|
|
15999
|
-
return {};
|
|
16000
|
-
}
|
|
16001
16853
|
function slugify2(value) {
|
|
16002
16854
|
const slug = value.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 54);
|
|
16003
16855
|
return slug || "app";
|
|
@@ -16005,19 +16857,19 @@ function slugify2(value) {
|
|
|
16005
16857
|
async function projectName(sourceDir) {
|
|
16006
16858
|
try {
|
|
16007
16859
|
const pkg = JSON.parse(
|
|
16008
|
-
await
|
|
16860
|
+
await readFile7(join13(sourceDir, "package.json"), "utf8")
|
|
16009
16861
|
);
|
|
16010
16862
|
if (pkg.name) return pkg.name;
|
|
16011
16863
|
} catch {
|
|
16012
16864
|
}
|
|
16013
16865
|
return basename(sourceDir);
|
|
16014
16866
|
}
|
|
16015
|
-
var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo)([\\/]|$)/;
|
|
16867
|
+
var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo|\.naro)([\\/]|$)/;
|
|
16016
16868
|
function resolveBin(name) {
|
|
16017
16869
|
let dir = dirname(fileURLToPath(import.meta.url));
|
|
16018
16870
|
for (; ; ) {
|
|
16019
|
-
const candidate =
|
|
16020
|
-
if (
|
|
16871
|
+
const candidate = join13(dir, "node_modules", ".bin", name);
|
|
16872
|
+
if (existsSync4(candidate)) return candidate;
|
|
16021
16873
|
const parent = dirname(dir);
|
|
16022
16874
|
if (parent === dir) return name;
|
|
16023
16875
|
dir = parent;
|
|
@@ -16041,7 +16893,7 @@ async function detectPackageManagerLenient(root) {
|
|
|
16041
16893
|
}
|
|
16042
16894
|
function localDependencies(sourceDir) {
|
|
16043
16895
|
return {
|
|
16044
|
-
createWorkspace: () => mkdtemp2(
|
|
16896
|
+
createWorkspace: () => mkdtemp2(join13(tmpdir2(), "naro-build-")),
|
|
16045
16897
|
removeWorkspace: (path) => rm2(path, { recursive: true, force: true }),
|
|
16046
16898
|
async runCommand(spec, observer) {
|
|
16047
16899
|
if (spec.command === "git" && spec.args[0] === "clone") {
|
|
@@ -16063,7 +16915,9 @@ function localDependencies(sourceDir) {
|
|
|
16063
16915
|
},
|
|
16064
16916
|
detectPackageManager: detectPackageManagerLenient,
|
|
16065
16917
|
resolveAppRoot,
|
|
16066
|
-
|
|
16918
|
+
detectFramework,
|
|
16919
|
+
readStaticArtifact,
|
|
16920
|
+
createPackageManagerConfig,
|
|
16067
16921
|
createPlatformBuildConfig,
|
|
16068
16922
|
readOpenNextArtifact,
|
|
16069
16923
|
openNextCommand: resolveBin("opennextjs-cloudflare"),
|
|
@@ -16071,62 +16925,142 @@ function localDependencies(sourceDir) {
|
|
|
16071
16925
|
};
|
|
16072
16926
|
}
|
|
16073
16927
|
async function deployDirectory(sourceDir, options, log2) {
|
|
16074
|
-
const
|
|
16075
|
-
const session = await requireSessionOrLogin(log2);
|
|
16076
|
-
const name = slugify2(options.name ?? await projectName(sourceDir));
|
|
16928
|
+
const cpUrl = controlPlaneUrl();
|
|
16077
16929
|
const production = options.production ?? false;
|
|
16930
|
+
const framework = detectFramework2(sourceDir);
|
|
16931
|
+
if (!framework.deployable && !options.force) {
|
|
16932
|
+
throw new NaroError(
|
|
16933
|
+
"FRAMEWORK_UNSUPPORTED",
|
|
16934
|
+
framework.framework === "unknown" ? "No supported framework detected here. Naro deploys Next.js, Vite, Create React App, and static sites \u2014 run this inside one of those projects." : `${framework.name} projects are not deployable on Naro yet.`,
|
|
16935
|
+
{ details: { framework: framework.framework } }
|
|
16936
|
+
);
|
|
16937
|
+
}
|
|
16938
|
+
log2(`\u25B8 detected ${framework.name}`);
|
|
16939
|
+
const auth = await resolveAuth({
|
|
16940
|
+
explicitToken: options.token,
|
|
16941
|
+
interactiveLogin: options.allowLogin === false ? false : {
|
|
16942
|
+
openBrowser,
|
|
16943
|
+
log: log2
|
|
16944
|
+
}
|
|
16945
|
+
});
|
|
16946
|
+
const client = new NaroClient({
|
|
16947
|
+
baseUrl: cpUrl,
|
|
16948
|
+
getAccessToken: () => auth.getAccessToken(),
|
|
16949
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
16950
|
+
});
|
|
16951
|
+
const name = slugify2(options.name ?? await projectName(sourceDir));
|
|
16078
16952
|
const git = await detectGitInfo(sourceDir);
|
|
16079
16953
|
const branch = options.branch ?? git.branch;
|
|
16080
|
-
if (
|
|
16954
|
+
if (options.projectId) log2(`\u25B8 linked project ${options.projectId}`);
|
|
16955
|
+
else if (git.repositoryUrl) log2(`\u25B8 linking ${git.repositoryUrl}`);
|
|
16081
16956
|
if (options.env && Object.keys(options.env).length > 0)
|
|
16082
|
-
log2(`\u25B8 syncing ${Object.keys(options.env).length} env var(s)`);
|
|
16957
|
+
log2(`\u25B8 syncing ${String(Object.keys(options.env).length)} env var(s)`);
|
|
16083
16958
|
log2("\u25B8 starting deployment");
|
|
16084
|
-
const ctx = await
|
|
16085
|
-
name,
|
|
16959
|
+
const ctx = await client.deployments.createCli({
|
|
16960
|
+
...options.projectId ? { projectId: options.projectId } : { name },
|
|
16086
16961
|
production,
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16962
|
+
...git.repositoryUrl != null && { repositoryUrl: git.repositoryUrl },
|
|
16963
|
+
...branch != null && { branch },
|
|
16964
|
+
...options.rootDirectory != null && {
|
|
16965
|
+
rootDirectory: options.rootDirectory
|
|
16966
|
+
},
|
|
16967
|
+
...options.env !== void 0 && { env: options.env }
|
|
16092
16968
|
});
|
|
16093
16969
|
if (ctx.envSkipped?.length)
|
|
16094
16970
|
log2(`\u26A0 skipped env: ${ctx.envSkipped.join(", ")}`);
|
|
16095
|
-
const executor = new LocalBuildExecutor(localDependencies(sourceDir));
|
|
16096
|
-
const observer = eventsObserver(
|
|
16097
|
-
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
|
|
16101
|
-
repositoryUrl: sourceDir,
|
|
16102
|
-
branch: ctx.branch,
|
|
16103
|
-
rootDirectory: options.rootDirectory ?? ctx.rootDirectory,
|
|
16104
|
-
env: { ...ctx.env, ...options.env ?? {} }
|
|
16105
|
-
},
|
|
16106
|
-
observer
|
|
16971
|
+
const executor = options.executor ?? new LocalBuildExecutor(localDependencies(sourceDir));
|
|
16972
|
+
const observer = eventsObserver(
|
|
16973
|
+
cpUrl,
|
|
16974
|
+
ctx.deploymentId,
|
|
16975
|
+
ctx.callbackToken,
|
|
16976
|
+
options.fetch
|
|
16107
16977
|
);
|
|
16108
|
-
|
|
16109
|
-
|
|
16978
|
+
let artifact;
|
|
16979
|
+
try {
|
|
16980
|
+
const result = await executor.build(
|
|
16981
|
+
{
|
|
16982
|
+
deploymentId: ctx.deploymentId,
|
|
16983
|
+
projectId: ctx.projectId,
|
|
16984
|
+
repositoryUrl: sourceDir,
|
|
16985
|
+
branch: ctx.branch,
|
|
16986
|
+
rootDirectory: options.rootDirectory ?? ctx.rootDirectory,
|
|
16987
|
+
env: { ...ctx.env, ...options.env ?? {} }
|
|
16988
|
+
},
|
|
16989
|
+
observer
|
|
16990
|
+
);
|
|
16991
|
+
artifact = result.artifact;
|
|
16992
|
+
} catch (error51) {
|
|
16993
|
+
throw new NaroError(
|
|
16994
|
+
"BUILD_FAILED",
|
|
16995
|
+
error51 instanceof Error ? error51.message : "The local build failed.",
|
|
16996
|
+
{ details: { deploymentId: ctx.deploymentId }, cause: error51 }
|
|
16997
|
+
);
|
|
16998
|
+
}
|
|
16999
|
+
await uploadArtifact(artifact, {
|
|
17000
|
+
controlPlaneUrl: cpUrl,
|
|
16110
17001
|
deploymentId: ctx.deploymentId,
|
|
16111
|
-
callbackToken: ctx.callbackToken
|
|
17002
|
+
callbackToken: ctx.callbackToken,
|
|
17003
|
+
...options.fetch ? { fetch: options.fetch } : {}
|
|
16112
17004
|
});
|
|
16113
|
-
|
|
17005
|
+
log2("\u25B8 publishing worker");
|
|
17006
|
+
const detail = await waitForFinish(client, ctx, log2);
|
|
17007
|
+
const url2 = deploymentUrl(detail, ctx, production);
|
|
17008
|
+
if (detail.deployment.status === "FAILED") {
|
|
17009
|
+
throw new NaroError(
|
|
17010
|
+
detail.deployment.errorCode ?? "DEPLOY_FAILED",
|
|
17011
|
+
detail.deployment.errorMessage ?? "The deployment failed.",
|
|
17012
|
+
{ details: { deploymentId: ctx.deploymentId } }
|
|
17013
|
+
);
|
|
17014
|
+
}
|
|
16114
17015
|
return {
|
|
16115
|
-
|
|
16116
|
-
|
|
17016
|
+
deploymentId: ctx.deploymentId,
|
|
17017
|
+
projectId: ctx.projectId,
|
|
17018
|
+
status: detail.deployment.status,
|
|
17019
|
+
url: url2,
|
|
17020
|
+
production
|
|
16117
17021
|
};
|
|
16118
17022
|
}
|
|
17023
|
+
async function waitForFinish(client, ctx, log2) {
|
|
17024
|
+
return client.deployments.wait(ctx.deploymentId, {
|
|
17025
|
+
intervalMs: 2500,
|
|
17026
|
+
timeoutMs: 10 * 6e4,
|
|
17027
|
+
onStatus: (status) => {
|
|
17028
|
+
if (status === "READY") log2("\u2713 worker deployed");
|
|
17029
|
+
}
|
|
17030
|
+
});
|
|
17031
|
+
}
|
|
17032
|
+
function deploymentUrl(detail, ctx, production) {
|
|
17033
|
+
if (detail.deployment.url) return detail.deployment.url;
|
|
17034
|
+
const surface = ctx.subdomain ?? ctx.projectId;
|
|
17035
|
+
const host = new URL(ctx.controlPlaneUrl || controlPlaneUrl()).hostname;
|
|
17036
|
+
return `https://${surface}${production ? "" : "-preview"}.${host}`;
|
|
17037
|
+
}
|
|
16119
17038
|
|
|
16120
17039
|
export {
|
|
16121
|
-
|
|
16122
|
-
|
|
16123
|
-
|
|
16124
|
-
|
|
17040
|
+
NaroError,
|
|
17041
|
+
isNaroError,
|
|
17042
|
+
toNaroError,
|
|
17043
|
+
saveSession,
|
|
17044
|
+
loadStoredSession,
|
|
17045
|
+
clearSession,
|
|
17046
|
+
isExpired,
|
|
16125
17047
|
controlPlaneUrl,
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
17048
|
+
createAuthClient,
|
|
17049
|
+
refreshStoredSession,
|
|
17050
|
+
login,
|
|
17051
|
+
resolveAuth,
|
|
17052
|
+
openBrowser,
|
|
17053
|
+
isTerminalStatus,
|
|
17054
|
+
NaroClient,
|
|
16129
17055
|
readProjectEnv,
|
|
17056
|
+
serializeDotenv,
|
|
17057
|
+
readProjectLink,
|
|
17058
|
+
writeProjectLink,
|
|
17059
|
+
removeProjectLink,
|
|
17060
|
+
ensureGitignoreHasNaro,
|
|
17061
|
+
parseDotenv2 as parseDotenv,
|
|
17062
|
+
readProjectEnv2,
|
|
17063
|
+
eventsObserver,
|
|
16130
17064
|
slugify2 as slugify,
|
|
16131
17065
|
deployDirectory
|
|
16132
17066
|
};
|