@farm.js/cli 0.1.0-beta.4 → 0.1.0-beta.40
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 +12 -0
- package/bin/farm.js +181 -9
- package/dist/{add-integration-CdVfiPjG.mjs → add-integration-7hf9WI7e.mjs} +147 -140
- package/dist/add-integration-7hf9WI7e.mjs.map +1 -0
- package/dist/{add-integration-Xc0p-k-m.js → add-integration-Bz7WjUfh.js} +149 -169
- package/dist/add-integration-Bz7WjUfh.js.map +1 -0
- package/dist/add-integration.js +1 -1
- package/dist/add-integration.mjs +1 -1
- package/dist/build.js +5 -3
- package/dist/build.js.map +1 -1
- package/dist/build.mjs +5 -3
- package/dist/build.mjs.map +1 -1
- package/dist/index.js +992 -113
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +972 -114
- package/dist/index.mjs.map +1 -1
- package/dist/rolldown-runtime-VH7oDXx4.js +28 -0
- package/dist/telemetry.js +347 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/telemetry.mjs +337 -0
- package/dist/telemetry.mjs.map +1 -0
- package/package.json +3 -2
- package/dist/add-integration-CdVfiPjG.mjs.map +0 -1
- package/dist/add-integration-Xc0p-k-m.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,52 +1,174 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.js");
|
|
3
|
+
const require_add_integration = require("./add-integration-Bz7WjUfh.js");
|
|
3
4
|
const require_build = require("./build.js");
|
|
5
|
+
const require_telemetry = require("./telemetry.js");
|
|
4
6
|
let _farm_js_core_server = require("@farm.js/core/server");
|
|
5
7
|
let node_fs = require("node:fs");
|
|
6
8
|
let node_fs_promises = require("node:fs/promises");
|
|
7
9
|
let node_path = require("node:path");
|
|
8
|
-
node_path =
|
|
10
|
+
node_path = require_rolldown_runtime.__toESM(node_path);
|
|
9
11
|
let child_process = require("child_process");
|
|
10
12
|
let fs = require("fs");
|
|
11
13
|
let path = require("path");
|
|
12
|
-
path =
|
|
14
|
+
path = require_rolldown_runtime.__toESM(path);
|
|
13
15
|
let _farm_js_core = require("@farm.js/core");
|
|
14
16
|
let node_child_process = require("node:child_process");
|
|
15
17
|
let node_timers_promises = require("node:timers/promises");
|
|
16
18
|
let picocolors = require("picocolors");
|
|
17
|
-
picocolors =
|
|
19
|
+
picocolors = require_rolldown_runtime.__toESM(picocolors);
|
|
18
20
|
let croner = require("croner");
|
|
21
|
+
let node_module = require("node:module");
|
|
22
|
+
let node_url = require("node:url");
|
|
19
23
|
//#region src/deploy.ts
|
|
24
|
+
var FarmDeployError = class extends Error {
|
|
25
|
+
constructor(code, platform, message, options) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "FarmDeployError";
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.platform = platform;
|
|
30
|
+
this.cause = options?.cause;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
20
33
|
/**
|
|
21
34
|
* Deploy Farm.js application
|
|
22
35
|
*/
|
|
23
36
|
async function deployFarm(options = {}) {
|
|
24
|
-
const
|
|
37
|
+
const { plan, deployConfig } = await resolveFarmDeployContext(options);
|
|
38
|
+
if (options.plan) {
|
|
39
|
+
_farm_js_core.logger.info(formatFarmDeployPlan(plan));
|
|
40
|
+
return plan;
|
|
41
|
+
}
|
|
42
|
+
_farm_js_core.logger.info(`🚀 Building with ${plan.preset} preset...`);
|
|
43
|
+
await require_build.buildFarm({
|
|
44
|
+
root: plan.root,
|
|
45
|
+
preset: plan.preset,
|
|
46
|
+
target: plan.target
|
|
47
|
+
});
|
|
48
|
+
if (!(0, fs.existsSync)(plan.outputDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", plan.target, `Build output not found at ${plan.outputDir}. Please run 'farm build' first.`);
|
|
49
|
+
await deployPlatform(plan.target, plan.root, plan.outputDir, deployConfig, options.prod);
|
|
50
|
+
return plan;
|
|
51
|
+
}
|
|
52
|
+
async function createFarmDeployPlan(options = {}) {
|
|
53
|
+
return (await resolveFarmDeployContext(options)).plan;
|
|
54
|
+
}
|
|
55
|
+
function formatFarmDeployPlan(plan) {
|
|
56
|
+
return [
|
|
57
|
+
"FARM / DEPLOY PLAN",
|
|
58
|
+
"",
|
|
59
|
+
`Target: ${plan.target}`,
|
|
60
|
+
`Preset: ${plan.preset}`,
|
|
61
|
+
`Runtime: ${plan.runtime}`,
|
|
62
|
+
`Output: ${plan.outputDir}`,
|
|
63
|
+
`Production: ${plan.production ? "yes" : "no"}`,
|
|
64
|
+
"",
|
|
65
|
+
`1. ${plan.build.command}`,
|
|
66
|
+
` cwd: ${plan.build.cwd}`,
|
|
67
|
+
`2. ${plan.deploy.command}`,
|
|
68
|
+
` cwd: ${plan.deploy.cwd}`,
|
|
69
|
+
...plan.cloudflareAgent ? ["", `Cloudflare Agent config: ${plan.cloudflareAgent.configPath}${plan.cloudflareAgent.generated ? " (generated during build)" : ""}`] : []
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
72
|
+
async function resolveFarmDeployContext(options) {
|
|
73
|
+
const root = path.default.resolve(options.root || process.cwd());
|
|
25
74
|
const mode = "production";
|
|
26
75
|
const userConfig = await (0, _farm_js_core.loadConfig)(root, void 0, mode);
|
|
27
|
-
const config =
|
|
76
|
+
const config = await (0, _farm_js_core.resolveConfig)({
|
|
77
|
+
root,
|
|
78
|
+
...userConfig
|
|
79
|
+
}, mode);
|
|
28
80
|
const cliTarget = options.vercel ? "vercel" : options.cloudflare ? "cloudflare" : options.netlify ? "netlify" : void 0;
|
|
29
|
-
const platform = (0, _farm_js_core.normalizeDeployTarget)(cliTarget || config
|
|
30
|
-
if (platform !== "vercel" && platform !== "cloudflare" && platform !== "netlify")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
81
|
+
const platform = (0, _farm_js_core.normalizeDeployTarget)(cliTarget || config.deploy.target);
|
|
82
|
+
if (platform !== "vercel" && platform !== "cloudflare" && platform !== "netlify") throw new Error("Please specify a deployment target with --vercel, --cloudflare, --netlify, or farm.config deploy.target.");
|
|
83
|
+
const configuredPreset = userConfig?.deploy?.preset || userConfig?.preset;
|
|
84
|
+
const configuredPresetTarget = (0, _farm_js_core.getDeployTargetForPreset)(configuredPreset);
|
|
85
|
+
const configuredTarget = (0, _farm_js_core.normalizeDeployTarget)(userConfig?.deploy?.target);
|
|
86
|
+
const presetMatchesPlatform = Boolean(configuredPreset) && (configuredPresetTarget === platform || !configuredPresetTarget && configuredTarget === platform);
|
|
87
|
+
if (cliTarget && configuredPreset && !presetMatchesPlatform) _farm_js_core.logger.warn(`Configured preset "${configuredPreset}" targets ${configuredPresetTarget || "an unknown platform"}; using the "${(0, _farm_js_core.getPresetForDeployTarget)(platform)}" preset because --${cliTarget} was passed.`);
|
|
34
88
|
const deployConfig = (0, _farm_js_core.resolveDeployConfig)(userConfig || {}, {
|
|
35
89
|
target: platform,
|
|
36
|
-
preset: cliTarget ?
|
|
90
|
+
preset: cliTarget ? presetMatchesPlatform ? configuredPreset : (0, _farm_js_core.getPresetForDeployTarget)(platform) : void 0
|
|
37
91
|
});
|
|
38
92
|
const preset = deployConfig.preset || (0, _farm_js_core.getPresetForDeployTarget)(platform) || "node-server";
|
|
39
|
-
_farm_js_core.logger.info(`🚀 Building with ${preset} preset...`);
|
|
40
|
-
await require_build.buildFarm({
|
|
41
|
-
root,
|
|
42
|
-
preset
|
|
43
|
-
});
|
|
44
93
|
const nitroOutput = (0, _farm_js_core.resolveDeployOutputPath)(root, deployConfig.outputDir);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
94
|
+
const cloudflareAgent = platform === "cloudflare" ? resolveCloudflareAgentDeployPlan(root) || resolveConfiguredCloudflareAgentDeployPlan(root, config.integrations) : void 0;
|
|
95
|
+
const deploy = createDeployCommand(platform, root, nitroOutput, deployConfig, options.prod, cloudflareAgent);
|
|
96
|
+
return {
|
|
97
|
+
plan: {
|
|
98
|
+
root: path.default.resolve(root),
|
|
99
|
+
target: platform,
|
|
100
|
+
preset,
|
|
101
|
+
runtime: (0, _farm_js_core.getFarmPresetRuntime)(preset),
|
|
102
|
+
outputDir: nitroOutput,
|
|
103
|
+
production: platform === "netlify" || Boolean(options.prod),
|
|
104
|
+
build: {
|
|
105
|
+
command: `farm build --preset ${preset}`,
|
|
106
|
+
cwd: path.default.resolve(root)
|
|
107
|
+
},
|
|
108
|
+
deploy,
|
|
109
|
+
...cloudflareAgent ? { cloudflareAgent } : {}
|
|
110
|
+
},
|
|
111
|
+
deployConfig
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createDeployCommand(platform, root, outputDir, deployConfig, prod, cloudflareAgent) {
|
|
115
|
+
if (platform === "vercel") {
|
|
116
|
+
const args = [
|
|
117
|
+
"deploy",
|
|
118
|
+
"--prebuilt",
|
|
119
|
+
"--yes",
|
|
120
|
+
...prod ? ["--prod"] : []
|
|
121
|
+
];
|
|
122
|
+
return {
|
|
123
|
+
command: formatCommand$1("vercel", args),
|
|
124
|
+
cwd: path.default.resolve(root),
|
|
125
|
+
executable: "vercel",
|
|
126
|
+
args
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (platform === "netlify") {
|
|
130
|
+
const args = createNetlifyDeployArgs(deployConfig.netlify?.site);
|
|
131
|
+
return {
|
|
132
|
+
command: formatCommand$1("netlify", args),
|
|
133
|
+
cwd: outputDir,
|
|
134
|
+
executable: "netlify",
|
|
135
|
+
args
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (cloudflareAgent) {
|
|
139
|
+
const args = [
|
|
140
|
+
"deploy",
|
|
141
|
+
"--config",
|
|
142
|
+
cloudflareAgent.configPath,
|
|
143
|
+
...cloudflareAgent.environment ? ["--env", cloudflareAgent.environment] : []
|
|
144
|
+
];
|
|
145
|
+
return {
|
|
146
|
+
command: formatCommand$1("wrangler", args),
|
|
147
|
+
cwd: path.default.resolve(root),
|
|
148
|
+
executable: "wrangler",
|
|
149
|
+
args
|
|
150
|
+
};
|
|
48
151
|
}
|
|
49
|
-
|
|
152
|
+
const args = [
|
|
153
|
+
"pages",
|
|
154
|
+
"deploy",
|
|
155
|
+
".",
|
|
156
|
+
`--project-name=${deployConfig.cloudflare?.projectName || deployConfig.projectName || "farm-app"}`
|
|
157
|
+
];
|
|
158
|
+
return {
|
|
159
|
+
command: formatCommand$1("wrangler", args),
|
|
160
|
+
cwd: outputDir,
|
|
161
|
+
executable: "wrangler",
|
|
162
|
+
args
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function createNetlifyDeployArgs(site) {
|
|
166
|
+
return [
|
|
167
|
+
"deploy",
|
|
168
|
+
"--prod",
|
|
169
|
+
"--dir=.",
|
|
170
|
+
...site ? [`--site=${site}`] : []
|
|
171
|
+
];
|
|
50
172
|
}
|
|
51
173
|
/**
|
|
52
174
|
* Deploy using platform's native CLI (user credentials)
|
|
@@ -68,19 +190,20 @@ async function deployPlatform(platform, root, outputDir, deployConfig, prod) {
|
|
|
68
190
|
async function deployVercel(root, outputDir, prod) {
|
|
69
191
|
_farm_js_core.logger.info("🚀 Deploying to Vercel...");
|
|
70
192
|
try {
|
|
71
|
-
(0, child_process.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
193
|
+
(0, child_process.execFileSync)("vercel", ["--version"], {
|
|
194
|
+
stdio: "ignore",
|
|
195
|
+
cwd: root
|
|
196
|
+
});
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throw new FarmDeployError("CLI_NOT_INSTALLED", "vercel", "Vercel CLI is not installed. Install it with: npm i -g vercel", { cause: error });
|
|
76
199
|
}
|
|
77
200
|
try {
|
|
78
|
-
(0, child_process.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
201
|
+
(0, child_process.execFileSync)("vercel", ["whoami"], {
|
|
202
|
+
stdio: "ignore",
|
|
203
|
+
cwd: root
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
throw new FarmDeployError("CLI_NOT_AUTHENTICATED", "vercel", "Vercel CLI is not authenticated. Run 'vercel login', then retry the deployment.", { cause: error });
|
|
84
207
|
}
|
|
85
208
|
try {
|
|
86
209
|
const { existsSync, statSync, readdirSync } = await import("fs");
|
|
@@ -89,14 +212,8 @@ async function deployVercel(root, outputDir, prod) {
|
|
|
89
212
|
const configFile = path.default.join(outputDir, "config.json");
|
|
90
213
|
const serverIndex = path.default.join(functionsDir, "index.mjs");
|
|
91
214
|
_farm_js_core.logger.info("🔍 Verifying deployment structure...");
|
|
92
|
-
if (!existsSync(functionsDir)) {
|
|
93
|
-
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
if (!existsSync(serverIndex)) {
|
|
97
|
-
_farm_js_core.logger.error(`❌ Server entry point not found at ${serverIndex}`);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
215
|
+
if (!existsSync(functionsDir)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Functions directory not found at ${functionsDir}.`);
|
|
216
|
+
if (!existsSync(serverIndex)) throw new FarmDeployError("INVALID_BUILD_OUTPUT", "vercel", `Server entry point not found at ${serverIndex}.`);
|
|
100
217
|
_farm_js_core.logger.info(`✅ Functions directory: ${functionsDir}`);
|
|
101
218
|
if (!existsSync(staticDir)) _farm_js_core.logger.warn(`⚠️ Static directory not found at ${staticDir}`);
|
|
102
219
|
else {
|
|
@@ -177,14 +294,19 @@ async function deployVercel(root, outputDir, prod) {
|
|
|
177
294
|
_farm_js_core.logger.info(` Static: ${staticDir}`);
|
|
178
295
|
_farm_js_core.logger.info(` Config: ${configFile}`);
|
|
179
296
|
_farm_js_core.logger.info("📤 Uploading to Vercel...");
|
|
180
|
-
(0, child_process.
|
|
297
|
+
(0, child_process.execFileSync)("vercel", [
|
|
298
|
+
"deploy",
|
|
299
|
+
"--prebuilt",
|
|
300
|
+
"--yes",
|
|
301
|
+
...prod ? ["--prod"] : []
|
|
302
|
+
], {
|
|
181
303
|
stdio: "inherit",
|
|
182
304
|
cwd: root
|
|
183
305
|
});
|
|
184
306
|
_farm_js_core.logger.success("✅ Deployed to Vercel successfully!");
|
|
185
307
|
} catch (error) {
|
|
186
|
-
|
|
187
|
-
|
|
308
|
+
if (error instanceof FarmDeployError) throw error;
|
|
309
|
+
throw new FarmDeployError("DEPLOY_FAILED", "vercel", `Failed to deploy to Vercel: ${getErrorMessage(error)}`, { cause: error });
|
|
188
310
|
}
|
|
189
311
|
}
|
|
190
312
|
/** Deploy to a composed Worker or fall back to Cloudflare Pages. */
|
|
@@ -205,8 +327,7 @@ async function deployCloudflare(root, outputDir, projectName) {
|
|
|
205
327
|
});
|
|
206
328
|
_farm_js_core.logger.success("✅ Deployed Farm and Cloudflare Agents successfully!");
|
|
207
329
|
} catch (error) {
|
|
208
|
-
|
|
209
|
-
process.exit(1);
|
|
330
|
+
throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
|
|
210
331
|
}
|
|
211
332
|
return;
|
|
212
333
|
}
|
|
@@ -224,8 +345,7 @@ async function deployCloudflare(root, outputDir, projectName) {
|
|
|
224
345
|
});
|
|
225
346
|
_farm_js_core.logger.success("✅ Deployed to Cloudflare Pages successfully!");
|
|
226
347
|
} catch (error) {
|
|
227
|
-
|
|
228
|
-
process.exit(1);
|
|
348
|
+
throw new FarmDeployError("DEPLOY_FAILED", "cloudflare", `Failed to deploy to Cloudflare: ${getErrorMessage(error)}`, { cause: error });
|
|
229
349
|
}
|
|
230
350
|
}
|
|
231
351
|
/** Read the trusted Workers deployment handoff emitted by @farm.js/cf-agent. */
|
|
@@ -251,16 +371,34 @@ function resolveCloudflareAgentDeployPlan(root) {
|
|
|
251
371
|
...typeof environment === "string" ? { environment: environment.trim() } : {}
|
|
252
372
|
};
|
|
253
373
|
}
|
|
374
|
+
function resolveConfiguredCloudflareAgentDeployPlan(root, integrations) {
|
|
375
|
+
const integration = Object.values(integrations || {}).find((value) => isRecord(value) && value.category === "agent" && value.type === "cloudflare" && value.serverRuntime === false);
|
|
376
|
+
if (!isRecord(integration) || !isRecord(integration.instance)) return void 0;
|
|
377
|
+
const configuredPath = integration.instance.config;
|
|
378
|
+
if (typeof configuredPath !== "string" || !configuredPath.trim()) return void 0;
|
|
379
|
+
const projectRoot = path.default.resolve(root);
|
|
380
|
+
const sourceConfigPath = path.default.resolve(projectRoot, configuredPath);
|
|
381
|
+
assertPathInsideProject(projectRoot, sourceConfigPath, "Cloudflare Agents source config");
|
|
382
|
+
const configPath = path.default.join(path.default.dirname(sourceConfigPath), ".farm-cf-agent.wrangler.jsonc");
|
|
383
|
+
const environment = integration.instance.environment;
|
|
384
|
+
return {
|
|
385
|
+
configPath,
|
|
386
|
+
...typeof environment === "string" && environment.trim() ? { environment: environment.trim() } : {},
|
|
387
|
+
generated: true
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function assertPathInsideProject(projectRoot, candidate, label) {
|
|
391
|
+
const relativePath = path.default.relative(projectRoot, candidate);
|
|
392
|
+
if (relativePath === ".." || relativePath.startsWith(`..${path.default.sep}`) || path.default.isAbsolute(relativePath)) throw new Error(`${label} must stay inside the Farm project root.`);
|
|
393
|
+
}
|
|
254
394
|
function assertWranglerInstalled(root) {
|
|
255
395
|
try {
|
|
256
396
|
(0, child_process.execFileSync)("wrangler", ["--version"], {
|
|
257
397
|
stdio: "ignore",
|
|
258
398
|
cwd: root
|
|
259
399
|
});
|
|
260
|
-
} catch {
|
|
261
|
-
|
|
262
|
-
_farm_js_core.logger.info("💡 Install it in this project with: npm i -D wrangler");
|
|
263
|
-
process.exit(1);
|
|
400
|
+
} catch (error) {
|
|
401
|
+
throw new FarmDeployError("CLI_NOT_INSTALLED", "cloudflare", "Wrangler CLI is not installed. Install it in this project with: npm i -D wrangler", { cause: error });
|
|
264
402
|
}
|
|
265
403
|
}
|
|
266
404
|
function isRecord(value) {
|
|
@@ -272,22 +410,33 @@ function isRecord(value) {
|
|
|
272
410
|
async function deployNetlify(root, outputDir, site) {
|
|
273
411
|
_farm_js_core.logger.info("🚀 Deploying to Netlify...");
|
|
274
412
|
try {
|
|
275
|
-
(0, child_process.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
413
|
+
(0, child_process.execFileSync)("netlify", ["--version"], {
|
|
414
|
+
stdio: "ignore",
|
|
415
|
+
cwd: root
|
|
416
|
+
});
|
|
417
|
+
} catch (error) {
|
|
418
|
+
throw new FarmDeployError("CLI_NOT_INSTALLED", "netlify", "Netlify CLI is not installed. Install it with: npm i -g netlify-cli", { cause: error });
|
|
280
419
|
}
|
|
281
420
|
try {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
421
|
+
(0, child_process.execFileSync)("netlify", createNetlifyDeployArgs(site), {
|
|
422
|
+
stdio: "inherit",
|
|
423
|
+
cwd: outputDir
|
|
424
|
+
});
|
|
285
425
|
_farm_js_core.logger.success("✅ Deployed to Netlify successfully!");
|
|
286
426
|
} catch (error) {
|
|
287
|
-
|
|
288
|
-
process.exit(1);
|
|
427
|
+
throw new FarmDeployError("DEPLOY_FAILED", "netlify", `Failed to deploy to Netlify: ${getErrorMessage(error)}`, { cause: error });
|
|
289
428
|
}
|
|
290
429
|
}
|
|
430
|
+
function formatCommand$1(executable, args) {
|
|
431
|
+
return [executable, ...args].map(formatCommandArgument).join(" ");
|
|
432
|
+
}
|
|
433
|
+
function formatCommandArgument(argument) {
|
|
434
|
+
if (/^[A-Za-z0-9_./:=@+-]+$/.test(argument)) return argument;
|
|
435
|
+
return `'${argument.replace(/'/g, `'"'"'`)}'`;
|
|
436
|
+
}
|
|
437
|
+
function getErrorMessage(error) {
|
|
438
|
+
return error instanceof Error ? error.message : String(error);
|
|
439
|
+
}
|
|
291
440
|
//#endregion
|
|
292
441
|
//#region src/preview-gateway.ts
|
|
293
442
|
const DEFAULT_GATEWAY_URL = "https://preview.farming-labs.dev";
|
|
@@ -310,10 +459,12 @@ const HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
|
|
|
310
459
|
function createPreviewGatewayPlan(target, options = {}) {
|
|
311
460
|
const requestedName = sanitizePreviewName$1(options.name || process.env.FARM_PREVIEW_NAME) || randomPreviewName$1();
|
|
312
461
|
const gatewayUrl = normalizeGatewayUrl(options.gatewayUrl || process.env.FARM_PREVIEW_GATEWAY_URL || DEFAULT_GATEWAY_URL);
|
|
462
|
+
const relayUrl = normalizeRelayUrl(process.env.FARM_PREVIEW_RELAY_URL || gatewayUrl);
|
|
313
463
|
const requestedHostname = `${requestedName}.${normalizePreviewDomain$1(process.env.FARM_PREVIEW_DOMAIN || DEFAULT_PREVIEW_DOMAIN)}`;
|
|
314
464
|
return {
|
|
315
465
|
provider: "farm-gateway",
|
|
316
466
|
gatewayUrl,
|
|
467
|
+
relayUrl,
|
|
317
468
|
target,
|
|
318
469
|
requestedName,
|
|
319
470
|
requestedHostname,
|
|
@@ -478,6 +629,7 @@ async function closeGatewaySession(plan, session) {
|
|
|
478
629
|
function formatGatewayPlan(plan) {
|
|
479
630
|
return [
|
|
480
631
|
`Gateway: ${plan.gatewayUrl}`,
|
|
632
|
+
`Relay: ${plan.relayUrl}`,
|
|
481
633
|
`Local: ${plan.target.localUrl}`,
|
|
482
634
|
`Public: ${plan.requestedPublicUrl}`
|
|
483
635
|
].join("\n");
|
|
@@ -489,6 +641,17 @@ function formatRequestPath(path) {
|
|
|
489
641
|
function normalizeGatewayUrl(value) {
|
|
490
642
|
return value.replace(/\/+$/, "");
|
|
491
643
|
}
|
|
644
|
+
function normalizeRelayUrl(value) {
|
|
645
|
+
const url = new URL(value);
|
|
646
|
+
if (url.protocol === "http:") url.protocol = "ws:";
|
|
647
|
+
if (url.protocol === "https:") url.protocol = "wss:";
|
|
648
|
+
if (url.protocol !== "ws:" && url.protocol !== "wss:") throw new Error(`Preview relay must use ws or wss, received ${url.protocol}`);
|
|
649
|
+
const pathname = url.pathname.replace(/\/+$/, "");
|
|
650
|
+
url.pathname = pathname.endsWith("/agent") ? pathname : `${pathname}/agent`;
|
|
651
|
+
url.search = "";
|
|
652
|
+
url.hash = "";
|
|
653
|
+
return url.toString();
|
|
654
|
+
}
|
|
492
655
|
function normalizePreviewDomain$1(value) {
|
|
493
656
|
return value.replace(/^https?:\/\//, "").replace(/^\.*/, "").replace(/\/*$/, "");
|
|
494
657
|
}
|
|
@@ -500,6 +663,39 @@ function randomPreviewName$1() {
|
|
|
500
663
|
return `farm-${Math.random().toString(36).slice(2, 8)}`;
|
|
501
664
|
}
|
|
502
665
|
//#endregion
|
|
666
|
+
//#region src/preview-native.ts
|
|
667
|
+
async function runNativePreviewTunnel(plan, options = {}) {
|
|
668
|
+
const runtime = options.runtime || await loadNativeTunnel();
|
|
669
|
+
const session = await runtime.startPreviewAgent(plan.relayUrl, plan.requestedName, plan.target.localUrl);
|
|
670
|
+
let stopping = false;
|
|
671
|
+
const stop = () => {
|
|
672
|
+
if (stopping) return;
|
|
673
|
+
stopping = true;
|
|
674
|
+
runtime.stopPreviewAgent(session.sessionId);
|
|
675
|
+
};
|
|
676
|
+
process.once("SIGINT", stop);
|
|
677
|
+
process.once("SIGTERM", stop);
|
|
678
|
+
_farm_js_core.logger.success("Preview URL ready.");
|
|
679
|
+
_farm_js_core.logger.info(`Public: ${session.publicUrl}`);
|
|
680
|
+
_farm_js_core.logger.info("Forwarding requests through the native tunnel until Ctrl+C.");
|
|
681
|
+
try {
|
|
682
|
+
if (!await runtime.waitPreviewAgent(session.sessionId)) throw new Error("The native preview tunnel stopped before its lifecycle could be observed.");
|
|
683
|
+
return session;
|
|
684
|
+
} finally {
|
|
685
|
+
process.removeListener("SIGINT", stop);
|
|
686
|
+
process.removeListener("SIGTERM", stop);
|
|
687
|
+
await runtime.stopPreviewAgent(session.sessionId).catch(() => false);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
async function loadNativeTunnel() {
|
|
691
|
+
try {
|
|
692
|
+
return await import("@farm.js/tunnel");
|
|
693
|
+
} catch (error) {
|
|
694
|
+
const reason = error instanceof Error ? ` ${error.message}` : "";
|
|
695
|
+
throw new Error(`Could not load @farm.js/tunnel for this platform. Reinstall @farm.js/cli so its native platform package is restored.${reason}`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
//#endregion
|
|
503
699
|
//#region src/preview.ts
|
|
504
700
|
const DEFAULT_PREVIEW_PORTS = [
|
|
505
701
|
3e3,
|
|
@@ -523,15 +719,26 @@ async function previewFarm(options = {}) {
|
|
|
523
719
|
plan
|
|
524
720
|
};
|
|
525
721
|
}
|
|
526
|
-
_farm_js_core.logger.info(`
|
|
527
|
-
_farm_js_core.logger.info("Opening Farm preview
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
722
|
+
_farm_js_core.logger.info(`Relay: ${plan.relayUrl}`);
|
|
723
|
+
_farm_js_core.logger.info("Opening native Farm preview tunnel...");
|
|
724
|
+
try {
|
|
725
|
+
const session = await runNativePreviewTunnel(plan);
|
|
726
|
+
return {
|
|
727
|
+
target,
|
|
728
|
+
plan,
|
|
729
|
+
publicUrl: session.publicUrl,
|
|
730
|
+
session
|
|
731
|
+
};
|
|
732
|
+
} catch (error) {
|
|
733
|
+
_farm_js_core.logger.warn(`Native preview relay unavailable; using compatibility gateway polling.${formatPreviewError(error)}`);
|
|
734
|
+
const session = await runPreviewGateway(plan, { timeoutMs: options.timeoutMs });
|
|
735
|
+
return {
|
|
736
|
+
target,
|
|
737
|
+
plan,
|
|
738
|
+
publicUrl: session.publicUrl,
|
|
739
|
+
session
|
|
740
|
+
};
|
|
741
|
+
}
|
|
535
742
|
}
|
|
536
743
|
const plan = createPreviewTunnelPlan(target, options);
|
|
537
744
|
if (options.dryRun) {
|
|
@@ -549,6 +756,9 @@ async function previewFarm(options = {}) {
|
|
|
549
756
|
publicUrl: await runPreviewTunnel(plan, options.timeoutMs ?? 3e4)
|
|
550
757
|
};
|
|
551
758
|
}
|
|
759
|
+
function formatPreviewError(error) {
|
|
760
|
+
return error instanceof Error && error.message ? ` ${error.message}` : "";
|
|
761
|
+
}
|
|
552
762
|
function shouldUseManagedGateway(options) {
|
|
553
763
|
if (options.provider === "farm" || process.env.FARM_PREVIEW_PROVIDER === "farm") return true;
|
|
554
764
|
if (options.provider === "local" || process.env.FARM_PREVIEW_PROVIDER === "local") return false;
|
|
@@ -799,10 +1009,20 @@ function normalizePreviewDomain(value) {
|
|
|
799
1009
|
}
|
|
800
1010
|
//#endregion
|
|
801
1011
|
//#region src/generate.ts
|
|
1012
|
+
var FarmGeneratedArtifactsStaleError = class extends Error {
|
|
1013
|
+
constructor(root, stalePaths) {
|
|
1014
|
+
const normalizedPaths = [...new Set(stalePaths)].sort();
|
|
1015
|
+
const relativePaths = normalizedPaths.map((filePath) => node_path.default.relative(root, filePath));
|
|
1016
|
+
super(`Generated types are stale:\n${relativePaths.map((filePath) => ` - ${filePath}`).join("\n")}\nRun farm generate and commit the updated files.`);
|
|
1017
|
+
this.name = "FarmGeneratedArtifactsStaleError";
|
|
1018
|
+
this.stalePaths = normalizedPaths;
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
802
1021
|
const PRISMA_GENERATED_START = "// Farm.js integrations generated schema: start";
|
|
803
1022
|
const PRISMA_GENERATED_END = "// Farm.js integrations generated schema: end";
|
|
804
1023
|
async function generateFarmArtifacts(options = {}) {
|
|
805
1024
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
1025
|
+
if (options.check && hasSchemaOptions(options)) throw new Error("--check verifies generated framework types and cannot be combined with schema output options.");
|
|
806
1026
|
const userConfig = await (0, _farm_js_core.loadConfig)(root, options.configPath, "development");
|
|
807
1027
|
if (!userConfig && hasSchemaOptions(options)) throw new Error("No Farm config found. Please create farm.config.ts or config.ts.");
|
|
808
1028
|
const resolvedConfig = await (0, _farm_js_core.resolveConfig)({
|
|
@@ -810,34 +1030,28 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
810
1030
|
...userConfig
|
|
811
1031
|
}, "development");
|
|
812
1032
|
const extraRoutes = [...resolvedConfig.openapi?.enabled && resolvedConfig.openapi.route ? [resolvedConfig.openapi.route] : [], ...(0, _farm_js_core.getFarmDocsRouteTypeEntries)(resolvedConfig.docs)];
|
|
813
|
-
await (0, _farm_js_core.
|
|
1033
|
+
const typeArtifacts = await (0, _farm_js_core.generateFarmTypeArtifacts)({
|
|
814
1034
|
root: resolvedConfig.root,
|
|
815
1035
|
srcDir: resolvedConfig.srcDir,
|
|
1036
|
+
configPath: options.configPath,
|
|
1037
|
+
layers: resolvedConfig.layers,
|
|
816
1038
|
extraRoutes,
|
|
817
|
-
suppressLintOnLink: resolvedConfig.suppressLintOnLink
|
|
1039
|
+
suppressLintOnLink: resolvedConfig.suppressLintOnLink,
|
|
1040
|
+
componentExtensions: resolvedConfig.renderer.componentExtensions,
|
|
1041
|
+
i18nConfig: resolvedConfig.i18n,
|
|
1042
|
+
check: options.check
|
|
818
1043
|
});
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
const apiGenerator = new _farm_js_core.APITypeGenerator(appDir);
|
|
826
|
-
const apiRoutes = apiGenerator.scanAPIRoutes();
|
|
827
|
-
const apiTypesPath = node_path.default.join(resolvedConfig.root, resolvedConfig.srcDir, "lib", "api.generated.ts");
|
|
828
|
-
await (0, node_fs_promises.mkdir)(node_path.default.dirname(apiTypesPath), { recursive: true });
|
|
829
|
-
await (0, node_fs_promises.writeFile)(apiTypesPath, apiGenerator.generateAPIRouter(apiRoutes), "utf8");
|
|
830
|
-
if (resolvedConfig.i18n.enabled) await (0, _farm_js_core.generateFarmI18nTypes)({
|
|
831
|
-
root: resolvedConfig.root,
|
|
832
|
-
srcDir: resolvedConfig.srcDir,
|
|
833
|
-
config: resolvedConfig.i18n
|
|
834
|
-
});
|
|
835
|
-
_farm_js_core.logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types (${apiRoutes.length} API route${apiRoutes.length === 1 ? "" : "s"}).`);
|
|
1044
|
+
if (options.check) {
|
|
1045
|
+
if (typeArtifacts.stalePaths.length) throw new FarmGeneratedArtifactsStaleError(root, typeArtifacts.stalePaths);
|
|
1046
|
+
_farm_js_core.logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types are up to date.`);
|
|
1047
|
+
return typeArtifacts;
|
|
1048
|
+
}
|
|
1049
|
+
_farm_js_core.logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types (${typeArtifacts.apiRoutes.length} API route${typeArtifacts.apiRoutes.length === 1 ? "" : "s"}).`);
|
|
836
1050
|
const schemas = (0, _farm_js_core.getIntegrationSchemas)(resolvedConfig.integrations);
|
|
837
1051
|
const schemaEntries = Object.entries(schemas);
|
|
838
1052
|
if (!schemaEntries.length) {
|
|
839
1053
|
if (hasSchemaOptions(options)) _farm_js_core.logger.warn("No integration schemas were found in the current Farm config.");
|
|
840
|
-
return;
|
|
1054
|
+
return typeArtifacts;
|
|
841
1055
|
}
|
|
842
1056
|
const packageManifest = await readPackageManifest(root);
|
|
843
1057
|
const schemaOptionsExplicit = hasSchemaOptions(options);
|
|
@@ -847,12 +1061,12 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
847
1061
|
} catch (error) {
|
|
848
1062
|
if (schemaOptionsExplicit) throw error;
|
|
849
1063
|
_farm_js_core.logger.warn(`Integration schemas were found, but Farm could not choose a schema target automatically: ${error.message}`);
|
|
850
|
-
return;
|
|
1064
|
+
return typeArtifacts;
|
|
851
1065
|
}
|
|
852
1066
|
if (!orm) {
|
|
853
1067
|
if (!schemaOptionsExplicit) {
|
|
854
1068
|
_farm_js_core.logger.warn("Integration schemas were found, but no data layer was detected. Pass --orm prisma|drizzle|postgres|mysql|sqlite|mongodb to generate schema artifacts.");
|
|
855
|
-
return;
|
|
1069
|
+
return typeArtifacts;
|
|
856
1070
|
}
|
|
857
1071
|
throw new Error("Could not auto-detect a schema target. Pass one explicitly with --orm prisma|drizzle|postgres|mysql|sqlite|mongodb.");
|
|
858
1072
|
}
|
|
@@ -863,7 +1077,7 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
863
1077
|
if (!(0, node_fs.existsSync)(schemaPath)) throw new Error(`Prisma target was selected but no schema file was found at ${schemaPath}. Create prisma/schema.prisma or pass --output.`);
|
|
864
1078
|
await writePrismaSchema(schemaPath, collectedModels);
|
|
865
1079
|
_farm_js_core.logger.success(`Generated Prisma integration schema in ${node_path.default.relative(root, schemaPath)}.`);
|
|
866
|
-
return;
|
|
1080
|
+
return typeArtifacts;
|
|
867
1081
|
}
|
|
868
1082
|
case "drizzle": {
|
|
869
1083
|
const dialect = options.dialect ?? await detectDrizzleDialect(root, packageManifest) ?? void 0;
|
|
@@ -871,7 +1085,7 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
871
1085
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, "farm-integrations.generated.ts");
|
|
872
1086
|
await writeGeneratedFile(outputPath, generateDrizzleSchema(collectedModels, dialect));
|
|
873
1087
|
_farm_js_core.logger.success(`Generated Drizzle integration schema in ${node_path.default.relative(root, outputPath)}.`);
|
|
874
|
-
return;
|
|
1088
|
+
return typeArtifacts;
|
|
875
1089
|
}
|
|
876
1090
|
case "postgres":
|
|
877
1091
|
case "mysql":
|
|
@@ -879,13 +1093,13 @@ async function generateFarmArtifacts(options = {}) {
|
|
|
879
1093
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, `farm-integrations.generated.${orm}.sql`);
|
|
880
1094
|
await writeGeneratedFile(outputPath, generateSqlSchema(collectedModels, orm));
|
|
881
1095
|
_farm_js_core.logger.success(`Generated ${orm} integration schema in ${node_path.default.relative(root, outputPath)}.`);
|
|
882
|
-
return;
|
|
1096
|
+
return typeArtifacts;
|
|
883
1097
|
}
|
|
884
1098
|
case "mongodb": {
|
|
885
1099
|
const outputPath = options.output ? node_path.default.resolve(root, options.output) : node_path.default.join(root, "farm-integrations.generated.mongodb.ts");
|
|
886
1100
|
await writeGeneratedFile(outputPath, generateMongoBootstrap(collectedModels));
|
|
887
1101
|
_farm_js_core.logger.success(`Generated MongoDB integration bootstrap in ${node_path.default.relative(root, outputPath)}.`);
|
|
888
|
-
return;
|
|
1102
|
+
return typeArtifacts;
|
|
889
1103
|
}
|
|
890
1104
|
}
|
|
891
1105
|
}
|
|
@@ -1070,7 +1284,7 @@ function cloneSchemaModel(model) {
|
|
|
1070
1284
|
async function writePrismaSchema(schemaPath, models) {
|
|
1071
1285
|
const source = await (0, node_fs_promises.readFile)(schemaPath, "utf8");
|
|
1072
1286
|
const generated = createPrismaGeneratedBlock(generatePrismaSchema(models));
|
|
1073
|
-
const pattern = new RegExp(`${escapeRegExp(PRISMA_GENERATED_START)}[\\s\\S]*?${escapeRegExp(PRISMA_GENERATED_END)}`, "m");
|
|
1287
|
+
const pattern = new RegExp(`${escapeRegExp$1(PRISMA_GENERATED_START)}[\\s\\S]*?${escapeRegExp$1(PRISMA_GENERATED_END)}`, "m");
|
|
1074
1288
|
const nextSource = pattern.test(source) ? source.replace(pattern, generated) : `${source.trimEnd()}\n\n${generated}\n`;
|
|
1075
1289
|
await (0, node_fs_promises.writeFile)(schemaPath, nextSource, "utf8");
|
|
1076
1290
|
}
|
|
@@ -1398,16 +1612,17 @@ function toCamelCase(value) {
|
|
|
1398
1612
|
function escapeString(value) {
|
|
1399
1613
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/'/g, "''");
|
|
1400
1614
|
}
|
|
1401
|
-
function escapeRegExp(value) {
|
|
1615
|
+
function escapeRegExp$1(value) {
|
|
1402
1616
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1403
1617
|
}
|
|
1404
1618
|
//#endregion
|
|
1405
1619
|
//#region src/doctor.ts
|
|
1406
|
-
const ROUTE_EXTENSIONS = [
|
|
1620
|
+
const ROUTE_EXTENSIONS$1 = [
|
|
1407
1621
|
"ts",
|
|
1408
1622
|
"tsx",
|
|
1409
1623
|
"js",
|
|
1410
1624
|
"jsx",
|
|
1625
|
+
"vue",
|
|
1411
1626
|
"md",
|
|
1412
1627
|
"mdx"
|
|
1413
1628
|
];
|
|
@@ -1425,10 +1640,10 @@ async function runFarmDoctor(options = {}) {
|
|
|
1425
1640
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
1426
1641
|
const liveTarget = resolveLiveTarget(options);
|
|
1427
1642
|
let liveError;
|
|
1428
|
-
if (!options.offline) try {
|
|
1643
|
+
if (!options.offline && !options.fix) try {
|
|
1429
1644
|
return createLiveReport(await fetchLiveSnapshot(liveTarget, options), liveTarget, options.now);
|
|
1430
1645
|
} catch (error) {
|
|
1431
|
-
liveError = formatError$
|
|
1646
|
+
liveError = formatError$2(error);
|
|
1432
1647
|
}
|
|
1433
1648
|
const report = await createProjectReport(root, options);
|
|
1434
1649
|
if (!options.offline && hasExplicitLiveTarget(options) && liveError) {
|
|
@@ -1472,6 +1687,13 @@ function formatFarmDoctorReport(report, options = {}) {
|
|
|
1472
1687
|
`${report.summary.fail} failed`,
|
|
1473
1688
|
`${report.summary.info} info`
|
|
1474
1689
|
].join(" / ");
|
|
1690
|
+
if (report.fixes?.length) {
|
|
1691
|
+
lines.push("", color.bold("FIXED"));
|
|
1692
|
+
for (const fix of report.fixes) {
|
|
1693
|
+
lines.push(` ${color.green("✓")} ${fix.title}`);
|
|
1694
|
+
lines.push(` ${color.dim(fix.filePath)}`);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1475
1697
|
lines.push("", `${color.bold("SUMMARY")} ${summary}`);
|
|
1476
1698
|
if (report.target?.devtoolsUrl) lines.push(`${color.bold("DEVTOOLS")} ${report.target.devtoolsUrl}`);
|
|
1477
1699
|
return lines.join("\n");
|
|
@@ -1571,9 +1793,10 @@ async function createProjectReport(root, options) {
|
|
|
1571
1793
|
action: "Add farm.config.ts and export defineConfig({...})."
|
|
1572
1794
|
});
|
|
1573
1795
|
else {
|
|
1796
|
+
const configRoot = node_path.default.resolve(root, userConfig.root || ".");
|
|
1574
1797
|
config = await (0, _farm_js_core.resolveConfig)({
|
|
1575
|
-
|
|
1576
|
-
|
|
1798
|
+
...userConfig,
|
|
1799
|
+
root: configRoot
|
|
1577
1800
|
}, "development");
|
|
1578
1801
|
const configFile = findConfigFile(root, options.configPath);
|
|
1579
1802
|
checks.push({
|
|
@@ -1588,7 +1811,7 @@ async function createProjectReport(root, options) {
|
|
|
1588
1811
|
status: "fail",
|
|
1589
1812
|
code: "CONFIG_INVALID",
|
|
1590
1813
|
title: "Farm config could not be resolved",
|
|
1591
|
-
message: formatError$
|
|
1814
|
+
message: formatError$2(error),
|
|
1592
1815
|
action: "Fix the config or environment validation error, then run farm doctor again."
|
|
1593
1816
|
});
|
|
1594
1817
|
}
|
|
@@ -1597,9 +1820,41 @@ async function createProjectReport(root, options) {
|
|
|
1597
1820
|
report.target = collectDeploymentChecks(config, userConfig, checks);
|
|
1598
1821
|
collectCronChecks(config, options.env || process.env, checks);
|
|
1599
1822
|
}
|
|
1823
|
+
if (config && options.fix) {
|
|
1824
|
+
const fixes = applySafeProjectFixes(root, config, checks);
|
|
1825
|
+
if (fixes.length) {
|
|
1826
|
+
const refreshed = await createProjectReport(root, {
|
|
1827
|
+
...options,
|
|
1828
|
+
fix: false
|
|
1829
|
+
});
|
|
1830
|
+
refreshed.fixes = fixes;
|
|
1831
|
+
return refreshed;
|
|
1832
|
+
}
|
|
1833
|
+
report.fixes = [];
|
|
1834
|
+
}
|
|
1600
1835
|
finalizeReport(report);
|
|
1601
1836
|
return report;
|
|
1602
1837
|
}
|
|
1838
|
+
function applySafeProjectFixes(root, config, checks) {
|
|
1839
|
+
const fixes = [];
|
|
1840
|
+
if (checks.some((check) => check.code === "ROOT_LAYOUT_MISSING")) {
|
|
1841
|
+
const rendererExtension = config.renderer.componentExtensions?.[0] || ".tsx";
|
|
1842
|
+
const layoutPath = node_path.default.join(config.root, config.srcDir, "app", `layout${rendererExtension}`);
|
|
1843
|
+
if (!(0, node_fs.existsSync)(layoutPath)) {
|
|
1844
|
+
(0, node_fs.mkdirSync)(node_path.default.dirname(layoutPath), { recursive: true });
|
|
1845
|
+
(0, node_fs.writeFileSync)(layoutPath, config.renderer.name === "vue" ? `<script setup lang="ts">\ndefineOptions({ inheritAttrs: false });\n<\/script>\n\n<template>\n <slot />\n</template>\n` : config.renderer.name === "solid" ? `import type { ParentProps } from "solid-js";\n\nexport default function RootLayout(props: ParentProps) {\n return <>{props.children}</>;\n}\n` : `import type { ReactNode } from "react";\n\nexport default function RootLayout({ children }: { children: ReactNode }) {\n return (\n <html lang="en">\n <body>{children}</body>\n </html>\n );\n}\n`, {
|
|
1846
|
+
encoding: "utf8",
|
|
1847
|
+
flag: "wx"
|
|
1848
|
+
});
|
|
1849
|
+
fixes.push({
|
|
1850
|
+
code: "ROOT_LAYOUT_CREATED",
|
|
1851
|
+
title: "Created the missing root layout",
|
|
1852
|
+
filePath: node_path.default.relative(root, layoutPath)
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return fixes;
|
|
1857
|
+
}
|
|
1603
1858
|
function collectNodeCheck(checks) {
|
|
1604
1859
|
const major = Number(process.versions.node.split(".")[0]);
|
|
1605
1860
|
checks.push(major >= 18 ? {
|
|
@@ -1651,7 +1906,7 @@ function collectPackageCheck(root, checks) {
|
|
|
1651
1906
|
status: "fail",
|
|
1652
1907
|
code: "PACKAGE_INVALID",
|
|
1653
1908
|
title: "package.json is invalid",
|
|
1654
|
-
message: formatError$
|
|
1909
|
+
message: formatError$2(error),
|
|
1655
1910
|
action: "Fix the package manifest JSON."
|
|
1656
1911
|
});
|
|
1657
1912
|
}
|
|
@@ -1659,8 +1914,8 @@ function collectPackageCheck(root, checks) {
|
|
|
1659
1914
|
function collectRouterChecks(config, checks) {
|
|
1660
1915
|
const sources = (0, _farm_js_core.getFarmSourceRoots)(config);
|
|
1661
1916
|
const appDirectories = sources.map((source) => node_path.default.join(source.root, source.srcDir, "app"));
|
|
1662
|
-
const hasPages = appDirectories.some((directory) => containsFile(directory, /^page\.(?:ts|tsx|js|jsx|md|mdx)$/));
|
|
1663
|
-
const hasProgrammaticRoutes = sources.some((source) => ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(source.root, source.srcDir, `farm.routes.${extension}`))));
|
|
1917
|
+
const hasPages = appDirectories.some((directory) => containsFile(directory, /^page\.(?:ts|tsx|js|jsx|vue|md|mdx)$/));
|
|
1918
|
+
const hasProgrammaticRoutes = sources.some((source) => ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(source.root, source.srcDir, `farm.routes.${extension}`))));
|
|
1664
1919
|
checks.push(hasPages || hasProgrammaticRoutes ? {
|
|
1665
1920
|
status: "pass",
|
|
1666
1921
|
code: "APP_ROUTER_READY",
|
|
@@ -1673,7 +1928,8 @@ function collectRouterChecks(config, checks) {
|
|
|
1673
1928
|
message: `Farm found no page modules under ${config.srcDir}/app.`,
|
|
1674
1929
|
action: `Add ${config.srcDir}/app/page.tsx or ${config.srcDir}/farm.routes.tsx.`
|
|
1675
1930
|
});
|
|
1676
|
-
const hasRootLayout = appDirectories.some((directory) => ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `layout.${extension}`))));
|
|
1931
|
+
const hasRootLayout = appDirectories.some((directory) => ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `layout.${extension}`))));
|
|
1932
|
+
const suggestedLayoutExtension = config.renderer.componentExtensions?.[0] || ".tsx";
|
|
1677
1933
|
checks.push(hasRootLayout ? {
|
|
1678
1934
|
status: "pass",
|
|
1679
1935
|
code: "ROOT_LAYOUT_READY",
|
|
@@ -1684,7 +1940,7 @@ function collectRouterChecks(config, checks) {
|
|
|
1684
1940
|
code: "ROOT_LAYOUT_MISSING",
|
|
1685
1941
|
title: "Root layout is missing",
|
|
1686
1942
|
message: "The application has no shared root layout.",
|
|
1687
|
-
action: `Add ${config.srcDir}/app/layout
|
|
1943
|
+
action: `Add ${config.srcDir}/app/layout${suggestedLayoutExtension}.`
|
|
1688
1944
|
});
|
|
1689
1945
|
}
|
|
1690
1946
|
function collectDeploymentChecks(config, userConfig, checks) {
|
|
@@ -1743,7 +1999,7 @@ function hasCronRoute(config, job) {
|
|
|
1743
1999
|
const relative = job.path.replace(/^\/+/, "").replace(/^api\//, "");
|
|
1744
2000
|
return (0, _farm_js_core.getFarmSourceRoots)(config).some((source) => {
|
|
1745
2001
|
const directory = node_path.default.join(source.root, source.srcDir, "app", "api", relative);
|
|
1746
|
-
return ROUTE_EXTENSIONS.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `route.${extension}`)));
|
|
2002
|
+
return ROUTE_EXTENSIONS$1.some((extension) => (0, node_fs.existsSync)(node_path.default.join(directory, `route.${extension}`)));
|
|
1747
2003
|
});
|
|
1748
2004
|
}
|
|
1749
2005
|
function containsFile(directory, pattern) {
|
|
@@ -1804,13 +2060,422 @@ function finalizeReport(report) {
|
|
|
1804
2060
|
function asRecord(value) {
|
|
1805
2061
|
return value && typeof value === "object" ? value : {};
|
|
1806
2062
|
}
|
|
1807
|
-
function formatError$
|
|
2063
|
+
function formatError$2(error) {
|
|
1808
2064
|
return error instanceof Error ? error.message : String(error);
|
|
1809
2065
|
}
|
|
1810
2066
|
function formatCount$1(value, noun) {
|
|
1811
2067
|
return `${value} ${noun}${value === 1 ? "" : "s"}`;
|
|
1812
2068
|
}
|
|
1813
2069
|
//#endregion
|
|
2070
|
+
//#region src/explain.ts
|
|
2071
|
+
const ROUTE_EXTENSIONS = [
|
|
2072
|
+
"tsx",
|
|
2073
|
+
"ts",
|
|
2074
|
+
"jsx",
|
|
2075
|
+
"js",
|
|
2076
|
+
"vue",
|
|
2077
|
+
"mdx",
|
|
2078
|
+
"md"
|
|
2079
|
+
];
|
|
2080
|
+
const MIDDLEWARE_EXTENSIONS = [
|
|
2081
|
+
"ts",
|
|
2082
|
+
"tsx",
|
|
2083
|
+
"js",
|
|
2084
|
+
"jsx",
|
|
2085
|
+
"mjs",
|
|
2086
|
+
"cjs"
|
|
2087
|
+
];
|
|
2088
|
+
const SOCIAL_IMAGE_EXTENSIONS = [
|
|
2089
|
+
"tsx",
|
|
2090
|
+
"ts",
|
|
2091
|
+
"jsx",
|
|
2092
|
+
"js",
|
|
2093
|
+
"png",
|
|
2094
|
+
"jpg",
|
|
2095
|
+
"jpeg",
|
|
2096
|
+
"gif",
|
|
2097
|
+
"webp"
|
|
2098
|
+
];
|
|
2099
|
+
async function explainFarmRoute(pathname, options = {}) {
|
|
2100
|
+
const root = node_path.default.resolve(options.root || process.cwd());
|
|
2101
|
+
const userConfig = await (0, _farm_js_core.loadConfig)(root, options.configPath, "production");
|
|
2102
|
+
const config = await (0, _farm_js_core.resolveConfig)({
|
|
2103
|
+
root,
|
|
2104
|
+
...userConfig
|
|
2105
|
+
}, "production");
|
|
2106
|
+
const normalizedPathname = normalizePathname(pathname, config.basePath || "/");
|
|
2107
|
+
const page = discoverMatchingPages(config, normalizedPathname).sort((left, right) => right.score - left.score || right.priority - left.priority)[0];
|
|
2108
|
+
if (!page) throw new Error(`No Farm page route matches ${normalizedPathname}.`);
|
|
2109
|
+
const layouts = collectInheritedRouteFiles(config, normalizedPathname, "layout", ROUTE_EXTENSIONS);
|
|
2110
|
+
const middleware = collectMiddleware(root, Boolean(userConfig?.middleware && Object.keys(userConfig.middleware).length), config, normalizedPathname);
|
|
2111
|
+
const pageSource = (0, node_fs.readFileSync)(page.filePath, "utf8");
|
|
2112
|
+
const layoutSources = layouts.map((filePath) => ({
|
|
2113
|
+
filePath,
|
|
2114
|
+
source: (0, node_fs.readFileSync)(filePath, "utf8")
|
|
2115
|
+
}));
|
|
2116
|
+
const runtime = (0, _farm_js_core.resolveFarmRouteRuntimeConfig)((0, _farm_js_core.mergeFarmRouteRuntimeConfigs)((0, _farm_js_core.resolveFarmRouteRuleRuntimeConfig)(normalizedPathname, config.routeRules), ...layoutSources.map(({ source }) => readRuntimeExports(source)), readRuntimeExports(pageSource)), `Route ${page.pattern}`);
|
|
2117
|
+
const matchingRules = Object.entries(config.routeRules).filter(([pattern]) => (0, _farm_js_core.farmRouteRuleMatches)(pattern, normalizedPathname)).sort(([left], [right]) => routeSpecificity(left) - routeSpecificity(right));
|
|
2118
|
+
const rendering = resolveRendering(pageSource, matchingRules);
|
|
2119
|
+
const cache = resolveCaching(pageSource, matchingRules);
|
|
2120
|
+
const metadataSources = [...layoutSources, {
|
|
2121
|
+
filePath: page.filePath,
|
|
2122
|
+
source: pageSource
|
|
2123
|
+
}];
|
|
2124
|
+
const openGraphImage = findNearestSocialImage(config, normalizedPathname, "opengraph-image");
|
|
2125
|
+
const twitterImage = findNearestSocialImage(config, normalizedPathname, "twitter-image");
|
|
2126
|
+
const preset = String(config.deploy.preset || config.preset || "node-server");
|
|
2127
|
+
const presetRuntime = (0, _farm_js_core.getFarmPresetRuntime)(preset);
|
|
2128
|
+
const compatible = rendering.mode === "static" || rendering.mode === "client" || runtime.runtime === "auto" || presetRuntime !== "unknown" && runtime.runtime === presetRuntime;
|
|
2129
|
+
const warnings = [];
|
|
2130
|
+
if (presetRuntime === "unknown" && runtime.runtime !== "auto") warnings.push(`Farm cannot verify the ${runtime.runtime} route requirement because the ${preset} preset runtime is unknown.`);
|
|
2131
|
+
else if (!compatible) warnings.push(`The route requires ${runtime.runtime}, but the ${preset} preset emits ${presetRuntime} functions.`);
|
|
2132
|
+
if (runtime.regions?.length && preset !== "vercel" && preset !== "vercel-edge") warnings.push(`${preset} does not map Farm per-route region hints.`);
|
|
2133
|
+
if (runtime.maxDuration && preset !== "vercel") warnings.push(`${preset} does not map Farm per-route maxDuration.`);
|
|
2134
|
+
return {
|
|
2135
|
+
pathname: normalizedPathname,
|
|
2136
|
+
pattern: page.pattern,
|
|
2137
|
+
params: page.params,
|
|
2138
|
+
filePath: toProjectPath(root, page.filePath),
|
|
2139
|
+
source: page.source,
|
|
2140
|
+
layouts: layouts.map((filePath) => toProjectPath(root, filePath)),
|
|
2141
|
+
middleware,
|
|
2142
|
+
runtime,
|
|
2143
|
+
rendering,
|
|
2144
|
+
cache,
|
|
2145
|
+
metadata: {
|
|
2146
|
+
static: metadataSources.filter(({ source }) => /export\s+const\s+metadata\b/.test(source)).map(({ filePath }) => toProjectPath(root, filePath)),
|
|
2147
|
+
dynamic: metadataSources.filter(({ source }) => /export\s+(?:async\s+)?function\s+generateMetadata\b|export\s+const\s+generateMetadata\b/.test(source)).map(({ filePath }) => toProjectPath(root, filePath)),
|
|
2148
|
+
...openGraphImage ? { openGraphImage: toProjectPath(root, openGraphImage) } : {},
|
|
2149
|
+
...twitterImage ? { twitterImage: toProjectPath(root, twitterImage) } : {}
|
|
2150
|
+
},
|
|
2151
|
+
deployment: {
|
|
2152
|
+
target: String(config.deploy.target || "node"),
|
|
2153
|
+
preset,
|
|
2154
|
+
runtime: presetRuntime,
|
|
2155
|
+
compatible,
|
|
2156
|
+
warnings
|
|
2157
|
+
}
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
function formatFarmRouteExplanation(explanation, options = {}) {
|
|
2161
|
+
const color = options.color === void 0 ? picocolors.default : picocolors.default.createColors(options.color);
|
|
2162
|
+
const lines = [
|
|
2163
|
+
color.bold("FARM / EXPLAIN"),
|
|
2164
|
+
"",
|
|
2165
|
+
`${color.bold("Path")} ${explanation.pathname}`,
|
|
2166
|
+
`${color.bold("Pattern")} ${explanation.pattern}`,
|
|
2167
|
+
`${color.bold("File")} ${explanation.filePath}`,
|
|
2168
|
+
`${color.bold("Source")} ${explanation.source}`,
|
|
2169
|
+
`${color.bold("Params")} ${formatParams(explanation.params)}`,
|
|
2170
|
+
`${color.bold("Layouts")} ${explanation.layouts.length ? explanation.layouts.join(" -> ") : "none"}`,
|
|
2171
|
+
`${color.bold("Middleware")} ${explanation.middleware.length ? explanation.middleware.map((entry) => entry.filePath).join(", ") : "none"}`,
|
|
2172
|
+
`${color.bold("Runtime")} ${formatRuntime(explanation.runtime)}`,
|
|
2173
|
+
`${color.bold("Rendering")} ${explanation.rendering.mode} (${explanation.rendering.reason})${explanation.rendering.ppr ? ", PPR" : ""}`,
|
|
2174
|
+
`${color.bold("Caching")} ${formatCaching(explanation.cache)}`,
|
|
2175
|
+
`${color.bold("Metadata")} ${formatMetadata(explanation.metadata)}`,
|
|
2176
|
+
`${color.bold("Deployment")} ${explanation.deployment.target} / ${explanation.deployment.preset} — ${explanation.deployment.compatible ? color.green("compatible") : color.red("incompatible")}`
|
|
2177
|
+
];
|
|
2178
|
+
for (const warning of explanation.deployment.warnings) lines.push(` ${color.yellow("!")} ${warning}`);
|
|
2179
|
+
return lines.join("\n");
|
|
2180
|
+
}
|
|
2181
|
+
function discoverMatchingPages(config, pathname) {
|
|
2182
|
+
const candidates = [];
|
|
2183
|
+
for (const [priority, source] of (0, _farm_js_core.getFarmSourceRoots)(config).entries()) {
|
|
2184
|
+
const appDirectory = node_path.default.join(source.root, source.srcDir, "app");
|
|
2185
|
+
if ((0, node_fs.existsSync)(appDirectory)) for (const filePath of walkFiles(appDirectory)) {
|
|
2186
|
+
if (!/^page\.(?:tsx?|jsx?|vue|svelte|mdx?)$/.test(node_path.default.basename(filePath))) continue;
|
|
2187
|
+
const relativeDirectory = node_path.default.relative(appDirectory, node_path.default.dirname(filePath));
|
|
2188
|
+
if (relativeDirectory.split(node_path.default.sep).includes("api") || isRouteSlotDirectory(relativeDirectory)) continue;
|
|
2189
|
+
const pattern = directoryToRoutePattern(relativeDirectory);
|
|
2190
|
+
const match = matchRoutePattern(pattern, pathname);
|
|
2191
|
+
if (!match) continue;
|
|
2192
|
+
candidates.push({
|
|
2193
|
+
filePath,
|
|
2194
|
+
pattern,
|
|
2195
|
+
params: match.params,
|
|
2196
|
+
score: match.score,
|
|
2197
|
+
source: source.name,
|
|
2198
|
+
priority
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
const sourceDirectory = node_path.default.join(source.root, source.srcDir);
|
|
2202
|
+
if (!(0, node_fs.existsSync)(sourceDirectory)) continue;
|
|
2203
|
+
for (const filePath of walkFiles(sourceDirectory)) {
|
|
2204
|
+
if (!/\.(?:tsx?|jsx?)$/.test(filePath) || filePath.endsWith(".d.ts")) continue;
|
|
2205
|
+
const moduleSource = (0, node_fs.readFileSync)(filePath, "utf8");
|
|
2206
|
+
for (const pattern of (0, _farm_js_core.scanProgrammaticPagePaths)(moduleSource)) {
|
|
2207
|
+
const match = matchRoutePattern(pattern, pathname);
|
|
2208
|
+
if (!match) continue;
|
|
2209
|
+
candidates.push({
|
|
2210
|
+
filePath,
|
|
2211
|
+
pattern,
|
|
2212
|
+
params: match.params,
|
|
2213
|
+
score: match.score,
|
|
2214
|
+
source: source.name,
|
|
2215
|
+
priority
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
return candidates;
|
|
2221
|
+
}
|
|
2222
|
+
function isRouteSlotDirectory(relativeDirectory) {
|
|
2223
|
+
return relativeDirectory.split(node_path.default.sep).some((segment) => /^@[A-Za-z][\w-]*$/.test(segment));
|
|
2224
|
+
}
|
|
2225
|
+
function walkFiles(directory) {
|
|
2226
|
+
const files = [];
|
|
2227
|
+
const pending = [directory];
|
|
2228
|
+
while (pending.length) {
|
|
2229
|
+
const current = pending.pop();
|
|
2230
|
+
for (const entry of (0, node_fs.readdirSync)(current, { withFileTypes: true })) {
|
|
2231
|
+
const entryPath = node_path.default.join(current, entry.name);
|
|
2232
|
+
if (entry.isDirectory()) {
|
|
2233
|
+
if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
|
|
2234
|
+
pending.push(entryPath);
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
files.push(entryPath);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return files;
|
|
2241
|
+
}
|
|
2242
|
+
function directoryToRoutePattern(relativeDirectory) {
|
|
2243
|
+
const segments = relativeDirectory.split(node_path.default.sep).filter(Boolean).filter((segment) => !/^\(.+\)$/.test(segment) && !segment.startsWith("@")).map((segment) => segment.replace(/^\(\.{1,3}\)/, ""));
|
|
2244
|
+
return segments.length ? `/${segments.join("/")}` : "/";
|
|
2245
|
+
}
|
|
2246
|
+
function matchRoutePattern(pattern, pathname) {
|
|
2247
|
+
const patternSegments = splitPath(pattern);
|
|
2248
|
+
const pathSegments = splitPath(pathname);
|
|
2249
|
+
const params = {};
|
|
2250
|
+
let score = 0;
|
|
2251
|
+
let pathIndex = 0;
|
|
2252
|
+
for (const segment of patternSegments) {
|
|
2253
|
+
const optionalCatchAll = segment.match(/^\[\[\.\.\.(.+)\]\]$/);
|
|
2254
|
+
if (optionalCatchAll) {
|
|
2255
|
+
params[optionalCatchAll[1]] = pathSegments.slice(pathIndex);
|
|
2256
|
+
pathIndex = pathSegments.length;
|
|
2257
|
+
score += 1;
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
const catchAll = segment.match(/^\[\.\.\.(.+)\]$/);
|
|
2261
|
+
if (catchAll) {
|
|
2262
|
+
if (pathIndex >= pathSegments.length) return null;
|
|
2263
|
+
params[catchAll[1]] = pathSegments.slice(pathIndex);
|
|
2264
|
+
pathIndex = pathSegments.length;
|
|
2265
|
+
score += 10;
|
|
2266
|
+
continue;
|
|
2267
|
+
}
|
|
2268
|
+
const dynamic = segment.match(/^\[(.+)\]$/);
|
|
2269
|
+
if (dynamic) {
|
|
2270
|
+
if (pathIndex >= pathSegments.length) return null;
|
|
2271
|
+
params[dynamic[1]] = pathSegments[pathIndex++];
|
|
2272
|
+
score += 50;
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
if (segment !== pathSegments[pathIndex++]) return null;
|
|
2276
|
+
score += 100;
|
|
2277
|
+
}
|
|
2278
|
+
return pathIndex === pathSegments.length ? {
|
|
2279
|
+
params,
|
|
2280
|
+
score
|
|
2281
|
+
} : null;
|
|
2282
|
+
}
|
|
2283
|
+
function collectInheritedRouteFiles(config, pathname, baseName, extensions) {
|
|
2284
|
+
return collectLayeredRouteFiles(config, baseName, extensions).filter((entry) => matchesRoutePrefix(entry.pattern, pathname)).sort(compareInheritedRouteFiles).map((entry) => entry.filePath);
|
|
2285
|
+
}
|
|
2286
|
+
function collectMiddleware(root, hasConfigMiddleware, config, pathname) {
|
|
2287
|
+
const rootMiddleware = /* @__PURE__ */ new Map();
|
|
2288
|
+
for (const source of (0, _farm_js_core.getFarmSourceRoots)(config)) {
|
|
2289
|
+
const filePath = findFile(node_path.default.join(source.root, source.srcDir), "middleware", MIDDLEWARE_EXTENSIONS);
|
|
2290
|
+
if (filePath) rootMiddleware.set("root", {
|
|
2291
|
+
filePath,
|
|
2292
|
+
pattern: "/"
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
const files = [...rootMiddleware.values(), ...collectLayeredRouteFiles(config, "middleware", MIDDLEWARE_EXTENSIONS).filter((entry) => matchesRoutePrefix(entry.pattern, pathname))].sort(compareInheritedRouteFiles).map((entry) => entry.filePath);
|
|
2296
|
+
return [...hasConfigMiddleware ? [{
|
|
2297
|
+
source: "config",
|
|
2298
|
+
filePath: "farm.config (middleware)"
|
|
2299
|
+
}] : [], ...files.map((filePath) => ({
|
|
2300
|
+
source: "file",
|
|
2301
|
+
filePath: toProjectPath(root, filePath)
|
|
2302
|
+
}))];
|
|
2303
|
+
}
|
|
2304
|
+
function collectLayeredRouteFiles(config, baseName, extensions) {
|
|
2305
|
+
const files = /* @__PURE__ */ new Map();
|
|
2306
|
+
const filePattern = new RegExp(`^${escapeRegExp(baseName)}\\.(?:${extensions.map(escapeRegExp).join("|")})$`);
|
|
2307
|
+
for (const source of (0, _farm_js_core.getFarmSourceRoots)(config)) {
|
|
2308
|
+
const appDirectory = node_path.default.join(source.root, source.srcDir, "app");
|
|
2309
|
+
if (!(0, node_fs.existsSync)(appDirectory)) continue;
|
|
2310
|
+
for (const filePath of walkFiles(appDirectory)) {
|
|
2311
|
+
if (!filePattern.test(node_path.default.basename(filePath))) continue;
|
|
2312
|
+
const pattern = directoryToRoutePattern(node_path.default.relative(appDirectory, node_path.default.dirname(filePath)));
|
|
2313
|
+
files.set(pattern, {
|
|
2314
|
+
filePath,
|
|
2315
|
+
pattern
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return [...files.values()];
|
|
2320
|
+
}
|
|
2321
|
+
function findNearestSocialImage(config, pathname, baseName) {
|
|
2322
|
+
return collectLayeredRouteFiles(config, baseName, SOCIAL_IMAGE_EXTENSIONS).filter((entry) => matchesRoutePrefix(entry.pattern, pathname)).sort((left, right) => compareInheritedRouteFiles(right, left))[0]?.filePath;
|
|
2323
|
+
}
|
|
2324
|
+
function compareInheritedRouteFiles(left, right) {
|
|
2325
|
+
return splitPath(left.pattern).length - splitPath(right.pattern).length;
|
|
2326
|
+
}
|
|
2327
|
+
function matchesRoutePrefix(pattern, pathname) {
|
|
2328
|
+
const patternSegments = splitPath(pattern);
|
|
2329
|
+
const pathSegments = splitPath(pathname);
|
|
2330
|
+
if (patternSegments.length > pathSegments.length) return false;
|
|
2331
|
+
return patternSegments.every((segment, index) => {
|
|
2332
|
+
if (/^\[{1,2}(?:\.\.\.)?.+\]{1,2}$/.test(segment)) return true;
|
|
2333
|
+
return segment === pathSegments[index];
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
function findFile(directory, baseName, extensions) {
|
|
2337
|
+
return extensions.map((extension) => node_path.default.join(directory, `${baseName}.${extension}`)).find(node_fs.existsSync);
|
|
2338
|
+
}
|
|
2339
|
+
function escapeRegExp(value) {
|
|
2340
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2341
|
+
}
|
|
2342
|
+
function readRuntimeExports(source) {
|
|
2343
|
+
const runtime = readStringExport(source, "runtime");
|
|
2344
|
+
const maxDuration = readNumberOrAutoExport(source, "maxDuration");
|
|
2345
|
+
const regions = readStringArrayOrAutoExport(source, "regions");
|
|
2346
|
+
return {
|
|
2347
|
+
...runtime === "auto" || runtime === "node" || runtime === "edge" ? { runtime } : {},
|
|
2348
|
+
...regions ? { regions } : {},
|
|
2349
|
+
...maxDuration !== void 0 ? { maxDuration } : {}
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
function resolveRendering(pageSource, matchingRules) {
|
|
2353
|
+
const pageRendering = (0, _farm_js_core.resolveRouteRenderingConfig)({
|
|
2354
|
+
...readBooleanExport(pageSource, "ssg") !== void 0 ? { ssg: readBooleanExport(pageSource, "ssg") } : {},
|
|
2355
|
+
...readBooleanExport(pageSource, "ppr") !== void 0 ? { ppr: readBooleanExport(pageSource, "ppr") } : {},
|
|
2356
|
+
...readBooleanExport(pageSource, "experimental_ppr") !== void 0 ? { experimental_ppr: readBooleanExport(pageSource, "experimental_ppr") } : {},
|
|
2357
|
+
...readNumberOrFalseExport(pageSource, "revalidate") !== void 0 ? { revalidate: readNumberOrFalseExport(pageSource, "revalidate") } : {},
|
|
2358
|
+
...readDynamicExport(pageSource) ? { dynamic: readDynamicExport(pageSource) } : {}
|
|
2359
|
+
}, pageSource);
|
|
2360
|
+
let mode = pageRendering.ssg ? "static" : pageRendering.ppr ? "partial" : "dynamic";
|
|
2361
|
+
let reason = pageRendering.directive ? `page directive ${JSON.stringify(pageRendering.directive)}` : pageRendering.ssg ? "page static rendering declaration" : pageRendering.ppr ? "page PPR declaration" : "default server rendering";
|
|
2362
|
+
let ppr = pageRendering.ppr;
|
|
2363
|
+
for (const [pattern, rule] of matchingRules) if (rule.prerender === true || rule.render === "static") {
|
|
2364
|
+
mode = "static";
|
|
2365
|
+
reason = `routeRules ${pattern}`;
|
|
2366
|
+
ppr = false;
|
|
2367
|
+
} else if (rule.prerender === false || rule.render === "dynamic" || rule.ssr === true) {
|
|
2368
|
+
mode = "dynamic";
|
|
2369
|
+
reason = `routeRules ${pattern}`;
|
|
2370
|
+
ppr = false;
|
|
2371
|
+
} else if (rule.ssr === false) {
|
|
2372
|
+
mode = "client";
|
|
2373
|
+
reason = `routeRules ${pattern}`;
|
|
2374
|
+
ppr = false;
|
|
2375
|
+
}
|
|
2376
|
+
return {
|
|
2377
|
+
mode,
|
|
2378
|
+
reason,
|
|
2379
|
+
ppr
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
function resolveCaching(pageSource, matchingRules) {
|
|
2383
|
+
let swr;
|
|
2384
|
+
let isr;
|
|
2385
|
+
for (const [, rule] of matchingRules) {
|
|
2386
|
+
if (typeof rule.swr === "number" || typeof rule.swr === "boolean") swr = rule.swr;
|
|
2387
|
+
if (typeof rule.isr === "number" || typeof rule.isr === "boolean") isr = rule.isr;
|
|
2388
|
+
}
|
|
2389
|
+
return {
|
|
2390
|
+
...readNumberOrFalseExport(pageSource, "revalidate") !== void 0 ? { revalidate: readNumberOrFalseExport(pageSource, "revalidate") } : {},
|
|
2391
|
+
...swr !== void 0 ? { swr } : {},
|
|
2392
|
+
...isr !== void 0 ? { isr } : {},
|
|
2393
|
+
rules: matchingRules.map(([pattern]) => pattern)
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
function readStringExport(source, name) {
|
|
2397
|
+
return source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*["']([^"']+)["']`))?.[1];
|
|
2398
|
+
}
|
|
2399
|
+
function readDynamicExport(source) {
|
|
2400
|
+
const dynamic = readStringExport(source, "dynamic");
|
|
2401
|
+
return dynamic === "auto" || dynamic === "force-dynamic" || dynamic === "error" || dynamic === "force-static" ? dynamic : void 0;
|
|
2402
|
+
}
|
|
2403
|
+
function readBooleanExport(source, name) {
|
|
2404
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(true|false)`))?.[1];
|
|
2405
|
+
return value === void 0 ? void 0 : value === "true";
|
|
2406
|
+
}
|
|
2407
|
+
function readNumberOrAutoExport(source, name) {
|
|
2408
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(?:["'](auto)["']|(\\d+))`));
|
|
2409
|
+
return value?.[1] === "auto" ? "auto" : value?.[2] ? Number(value[2]) : void 0;
|
|
2410
|
+
}
|
|
2411
|
+
function readNumberOrFalseExport(source, name) {
|
|
2412
|
+
const value = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*(false|\\d+)`))?.[1];
|
|
2413
|
+
return value === "false" ? false : value ? Number(value) : void 0;
|
|
2414
|
+
}
|
|
2415
|
+
function readStringArrayOrAutoExport(source, name) {
|
|
2416
|
+
if (source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*["']auto["']`))) return "auto";
|
|
2417
|
+
const array = source.match(new RegExp(`export\\s+const\\s+${name}\\s*=\\s*\\[([^\\]]*)\\]`))?.[1];
|
|
2418
|
+
if (array === void 0) return void 0;
|
|
2419
|
+
return [...array.matchAll(/["']([^"']+)["']/g)].map((match) => match[1]);
|
|
2420
|
+
}
|
|
2421
|
+
function routeSpecificity(pattern) {
|
|
2422
|
+
return splitPath(pattern).reduce((score, segment) => {
|
|
2423
|
+
if (segment === "**" || segment.startsWith("[[...")) return score + 1;
|
|
2424
|
+
if (segment === "*" || segment.startsWith("[...")) return score + 10;
|
|
2425
|
+
if (segment.startsWith("[") || segment.startsWith(":")) return score + 50;
|
|
2426
|
+
return score + 100;
|
|
2427
|
+
}, 0);
|
|
2428
|
+
}
|
|
2429
|
+
function normalizePathname(value, basePath) {
|
|
2430
|
+
let pathname;
|
|
2431
|
+
try {
|
|
2432
|
+
pathname = new URL(value, "http://farm.local").pathname;
|
|
2433
|
+
} catch {
|
|
2434
|
+
pathname = value;
|
|
2435
|
+
}
|
|
2436
|
+
pathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
2437
|
+
const normalizedBase = basePath && basePath !== "/" ? `/${basePath.replace(/^\/+|\/+$/g, "")}` : "";
|
|
2438
|
+
if (normalizedBase && (pathname === normalizedBase || pathname.startsWith(`${normalizedBase}/`))) pathname = pathname.slice(normalizedBase.length) || "/";
|
|
2439
|
+
return pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
|
|
2440
|
+
}
|
|
2441
|
+
function splitPath(value) {
|
|
2442
|
+
return value.split("/").filter(Boolean).map(decodeURIComponent);
|
|
2443
|
+
}
|
|
2444
|
+
function toProjectPath(root, filePath) {
|
|
2445
|
+
let relativePath = node_path.default.relative(root, filePath);
|
|
2446
|
+
if ((relativePath === ".." || relativePath.startsWith(`..${node_path.default.sep}`)) && (0, node_fs.existsSync)(root) && (0, node_fs.existsSync)(filePath)) relativePath = node_path.default.relative((0, node_fs.realpathSync)(root), (0, node_fs.realpathSync)(filePath));
|
|
2447
|
+
return relativePath.split(node_path.default.sep).join("/");
|
|
2448
|
+
}
|
|
2449
|
+
function formatParams(params) {
|
|
2450
|
+
const entries = Object.entries(params);
|
|
2451
|
+
return entries.length ? entries.map(([key, value]) => `${key}=${Array.isArray(value) ? value.join("/") : value}`).join(", ") : "none";
|
|
2452
|
+
}
|
|
2453
|
+
function formatRuntime(runtime) {
|
|
2454
|
+
return [
|
|
2455
|
+
runtime.runtime,
|
|
2456
|
+
runtime.regions?.length ? `regions=${runtime.regions.join(",")}` : "",
|
|
2457
|
+
runtime.maxDuration ? `maxDuration=${runtime.maxDuration}s` : ""
|
|
2458
|
+
].filter(Boolean).join(", ");
|
|
2459
|
+
}
|
|
2460
|
+
function formatCaching(cache) {
|
|
2461
|
+
const values = [
|
|
2462
|
+
cache.revalidate !== void 0 ? `revalidate=${cache.revalidate}` : "",
|
|
2463
|
+
cache.swr !== void 0 ? `swr=${cache.swr}` : "",
|
|
2464
|
+
cache.isr !== void 0 ? `isr=${cache.isr}` : "",
|
|
2465
|
+
cache.rules.length ? `rules=${cache.rules.join(",")}` : ""
|
|
2466
|
+
].filter(Boolean);
|
|
2467
|
+
return values.length ? values.join("; ") : "request-time / no declared cache";
|
|
2468
|
+
}
|
|
2469
|
+
function formatMetadata(metadata) {
|
|
2470
|
+
const values = [
|
|
2471
|
+
metadata.static.length ? `static=${metadata.static.join(",")}` : "",
|
|
2472
|
+
metadata.dynamic.length ? `dynamic=${metadata.dynamic.join(",")}` : "",
|
|
2473
|
+
metadata.openGraphImage ? `og=${metadata.openGraphImage}` : "",
|
|
2474
|
+
metadata.twitterImage ? `twitter=${metadata.twitterImage}` : ""
|
|
2475
|
+
].filter(Boolean);
|
|
2476
|
+
return values.length ? values.join("; ") : "none";
|
|
2477
|
+
}
|
|
2478
|
+
//#endregion
|
|
1814
2479
|
//#region src/cron.ts
|
|
1815
2480
|
async function loadFarmCronConfig(options = {}) {
|
|
1816
2481
|
const root = node_path.default.resolve(options.root || process.cwd());
|
|
@@ -1861,7 +2526,7 @@ async function startFarmCronScheduler(options = {}) {
|
|
|
1861
2526
|
_farm_js_core.logger.warn(`Cron ${job.name} skipped an overlapping run.`);
|
|
1862
2527
|
},
|
|
1863
2528
|
catch: (error) => {
|
|
1864
|
-
_farm_js_core.logger.error(`Cron ${job.name} failed: ${formatError(error)}`);
|
|
2529
|
+
_farm_js_core.logger.error(`Cron ${job.name} failed: ${formatError$1(error)}`);
|
|
1865
2530
|
}
|
|
1866
2531
|
}, async () => {
|
|
1867
2532
|
_farm_js_core.logger.info(`Cron ${job.name} -> ${job.path}`);
|
|
@@ -1950,7 +2615,7 @@ function formatResponseDetail(body) {
|
|
|
1950
2615
|
const detail = typeof body === "string" ? body : JSON.stringify(body);
|
|
1951
2616
|
return detail ? `: ${detail}` : "";
|
|
1952
2617
|
}
|
|
1953
|
-
function formatError(error) {
|
|
2618
|
+
function formatError$1(error) {
|
|
1954
2619
|
return error instanceof Error ? error.message : String(error);
|
|
1955
2620
|
}
|
|
1956
2621
|
//#endregion
|
|
@@ -2420,8 +3085,207 @@ function toPosix(value) {
|
|
|
2420
3085
|
return value.split(node_path.default.sep).join("/");
|
|
2421
3086
|
}
|
|
2422
3087
|
//#endregion
|
|
3088
|
+
//#region src/auth.ts
|
|
3089
|
+
async function migrateFarmAuth(options = {}) {
|
|
3090
|
+
const root = node_path.default.resolve(options.root || process.cwd());
|
|
3091
|
+
const userConfig = await (0, _farm_js_core.loadConfig)(root, options.configPath, "production");
|
|
3092
|
+
if (!userConfig) throw new Error(`No farm.config file was found in ${root}.`);
|
|
3093
|
+
if (!(await (0, _farm_js_core.resolveConfig)({
|
|
3094
|
+
...userConfig,
|
|
3095
|
+
root
|
|
3096
|
+
}, "production")).auth.enabled) throw new Error("Farm Auth is disabled. Add `auth: true` to farm.config.ts first.");
|
|
3097
|
+
const resolveFromApp = (0, node_module.createRequire)(node_path.default.join(root, "package.json"));
|
|
3098
|
+
let modulePath;
|
|
3099
|
+
try {
|
|
3100
|
+
modulePath = resolveFromApp.resolve("@farm.js/auth/internal");
|
|
3101
|
+
} catch {
|
|
3102
|
+
throw new Error("Install @farm.js/auth before running `farm auth migrate`.");
|
|
3103
|
+
}
|
|
3104
|
+
const runtime = await import(
|
|
3105
|
+
/* @vite-ignore */
|
|
3106
|
+
(0, node_url.pathToFileURL)(modulePath).href
|
|
3107
|
+
);
|
|
3108
|
+
_farm_js_core.logger.info("Applying the Farm Auth database schema...");
|
|
3109
|
+
await runtime.migrateFarmAuth();
|
|
3110
|
+
_farm_js_core.logger.success("Farm Auth database is ready.");
|
|
3111
|
+
}
|
|
3112
|
+
//#endregion
|
|
3113
|
+
//#region src/upgrade.ts
|
|
3114
|
+
const DEPENDENCY_SECTIONS = [
|
|
3115
|
+
"dependencies",
|
|
3116
|
+
"devDependencies",
|
|
3117
|
+
"optionalDependencies",
|
|
3118
|
+
"peerDependencies"
|
|
3119
|
+
];
|
|
3120
|
+
const LOCAL_SPECIFIER_PREFIXES = [
|
|
3121
|
+
"workspace:",
|
|
3122
|
+
"file:",
|
|
3123
|
+
"link:",
|
|
3124
|
+
"portal:",
|
|
3125
|
+
"catalog:"
|
|
3126
|
+
];
|
|
3127
|
+
async function createFarmUpgradePlan(options) {
|
|
3128
|
+
assertUpgradeChannel(options.channel);
|
|
3129
|
+
const root = node_path.default.resolve(options.root || process.cwd());
|
|
3130
|
+
const packageJsonPath = node_path.default.join(root, "package.json");
|
|
3131
|
+
let packageJson;
|
|
3132
|
+
try {
|
|
3133
|
+
packageJson = JSON.parse(await (0, node_fs_promises.readFile)(packageJsonPath, "utf8"));
|
|
3134
|
+
} catch (error) {
|
|
3135
|
+
if (!(0, node_fs.existsSync)(packageJsonPath)) throw new Error(`No package.json found at ${packageJsonPath}.`);
|
|
3136
|
+
throw new Error(`Could not read ${packageJsonPath}: ${formatError(error)}`);
|
|
3137
|
+
}
|
|
3138
|
+
const packageManager = options.packageManager || detectFarmPackageManager(root, packageJson.packageManager);
|
|
3139
|
+
const packages = [];
|
|
3140
|
+
const skipped = [];
|
|
3141
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3142
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
3143
|
+
const dependencies = packageJson[section];
|
|
3144
|
+
if (!dependencies || typeof dependencies !== "object") continue;
|
|
3145
|
+
for (const [name, rawSpecifier] of Object.entries(dependencies)) {
|
|
3146
|
+
if (!name.startsWith("@farm.js/") || seen.has(name)) continue;
|
|
3147
|
+
seen.add(name);
|
|
3148
|
+
const current = typeof rawSpecifier === "string" ? rawSpecifier : String(rawSpecifier);
|
|
3149
|
+
if (isLocalSpecifier(current)) {
|
|
3150
|
+
skipped.push({
|
|
3151
|
+
name,
|
|
3152
|
+
current,
|
|
3153
|
+
section,
|
|
3154
|
+
reason: "local workspace and file dependencies are not published-package upgrades"
|
|
3155
|
+
});
|
|
3156
|
+
continue;
|
|
3157
|
+
}
|
|
3158
|
+
packages.push({
|
|
3159
|
+
name,
|
|
3160
|
+
current,
|
|
3161
|
+
section,
|
|
3162
|
+
target: `${name}@${options.channel}`
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
if (packages.length === 0) {
|
|
3167
|
+
const suffix = skipped.length > 0 ? " The Farm packages in this project use local workspace or file references." : "";
|
|
3168
|
+
throw new Error(`No published @farm.js/* dependencies were found in ${packageJsonPath}.${suffix}`);
|
|
3169
|
+
}
|
|
3170
|
+
packages.sort((left, right) => left.name.localeCompare(right.name));
|
|
3171
|
+
skipped.sort((left, right) => left.name.localeCompare(right.name));
|
|
3172
|
+
return {
|
|
3173
|
+
root,
|
|
3174
|
+
packageJsonPath,
|
|
3175
|
+
packageManager,
|
|
3176
|
+
channel: options.channel,
|
|
3177
|
+
packages,
|
|
3178
|
+
skipped,
|
|
3179
|
+
commands: createUpgradeCommands(root, packageManager, packages)
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
async function upgradeFarm(options) {
|
|
3183
|
+
const plan = await createFarmUpgradePlan(options);
|
|
3184
|
+
if (options.dryRun) return {
|
|
3185
|
+
plan,
|
|
3186
|
+
executed: false
|
|
3187
|
+
};
|
|
3188
|
+
const runCommand = options.runCommand || runFarmUpgradeCommand;
|
|
3189
|
+
for (const command of plan.commands) await runCommand(command);
|
|
3190
|
+
return {
|
|
3191
|
+
plan,
|
|
3192
|
+
executed: true
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
3195
|
+
function formatFarmUpgradePlan(plan) {
|
|
3196
|
+
const lines = [
|
|
3197
|
+
`Farm upgrade: ${plan.channel === "latest" ? "latest stable" : "latest beta"}`,
|
|
3198
|
+
`Project: ${plan.root}`,
|
|
3199
|
+
`Package manager: ${plan.packageManager}`,
|
|
3200
|
+
"Packages:"
|
|
3201
|
+
];
|
|
3202
|
+
for (const entry of plan.packages) lines.push(` ${entry.name} (${entry.section}): ${entry.current} -> ${plan.channel}`);
|
|
3203
|
+
if (plan.skipped.length > 0) {
|
|
3204
|
+
lines.push("Skipped local packages:");
|
|
3205
|
+
for (const entry of plan.skipped) lines.push(` ${entry.name} (${entry.current})`);
|
|
3206
|
+
}
|
|
3207
|
+
lines.push("Commands:");
|
|
3208
|
+
for (const command of plan.commands) lines.push(` ${command.command} ${command.args.join(" ")}`);
|
|
3209
|
+
return lines.join("\n");
|
|
3210
|
+
}
|
|
3211
|
+
function detectFarmPackageManager(root, packageManagerField) {
|
|
3212
|
+
if (typeof packageManagerField === "string") {
|
|
3213
|
+
const name = packageManagerField.split("@", 1)[0];
|
|
3214
|
+
if (isFarmPackageManager(name)) return name;
|
|
3215
|
+
}
|
|
3216
|
+
for (const [packageManager, lockfiles] of [
|
|
3217
|
+
["pnpm", ["pnpm-lock.yaml"]],
|
|
3218
|
+
["yarn", ["yarn.lock"]],
|
|
3219
|
+
["bun", ["bun.lock", "bun.lockb"]],
|
|
3220
|
+
["npm", ["package-lock.json", "npm-shrinkwrap.json"]]
|
|
3221
|
+
]) if (lockfiles.some((lockfile) => (0, node_fs.existsSync)(node_path.default.join(root, lockfile)))) return packageManager;
|
|
3222
|
+
return "npm";
|
|
3223
|
+
}
|
|
3224
|
+
function createUpgradeCommands(root, packageManager, packages) {
|
|
3225
|
+
const command = packageManager === "npm" ? "install" : "add";
|
|
3226
|
+
return DEPENDENCY_SECTIONS.flatMap((section) => {
|
|
3227
|
+
const targets = packages.filter((entry) => entry.section === section).map((entry) => entry.target);
|
|
3228
|
+
if (targets.length === 0) return [];
|
|
3229
|
+
return [{
|
|
3230
|
+
command: packageManager,
|
|
3231
|
+
args: [
|
|
3232
|
+
command,
|
|
3233
|
+
...getDependencySectionFlags(packageManager, section),
|
|
3234
|
+
...targets
|
|
3235
|
+
],
|
|
3236
|
+
cwd: root
|
|
3237
|
+
}];
|
|
3238
|
+
});
|
|
3239
|
+
}
|
|
3240
|
+
function getDependencySectionFlags(packageManager, section) {
|
|
3241
|
+
if (section === "dependencies") return [];
|
|
3242
|
+
if (packageManager === "yarn" || packageManager === "bun") {
|
|
3243
|
+
if (section === "devDependencies") return ["--dev"];
|
|
3244
|
+
if (section === "optionalDependencies") return ["--optional"];
|
|
3245
|
+
return ["--peer"];
|
|
3246
|
+
}
|
|
3247
|
+
if (section === "devDependencies") return ["--save-dev"];
|
|
3248
|
+
if (section === "optionalDependencies") return ["--save-optional"];
|
|
3249
|
+
return ["--save-peer"];
|
|
3250
|
+
}
|
|
3251
|
+
function runFarmUpgradeCommand(command) {
|
|
3252
|
+
return new Promise((resolve, reject) => {
|
|
3253
|
+
const executable = process.platform === "win32" ? `${command.command}.cmd` : command.command;
|
|
3254
|
+
const child = (0, node_child_process.spawn)(executable, command.args, {
|
|
3255
|
+
cwd: command.cwd,
|
|
3256
|
+
env: process.env,
|
|
3257
|
+
stdio: "inherit"
|
|
3258
|
+
});
|
|
3259
|
+
child.on("error", reject);
|
|
3260
|
+
child.on("close", (code, signal) => {
|
|
3261
|
+
if (code === 0) {
|
|
3262
|
+
resolve();
|
|
3263
|
+
return;
|
|
3264
|
+
}
|
|
3265
|
+
const termination = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
|
|
3266
|
+
reject(/* @__PURE__ */ new Error(`${command.command} ${command.args.join(" ")} failed with ${termination}.`));
|
|
3267
|
+
});
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
3270
|
+
function isLocalSpecifier(specifier) {
|
|
3271
|
+
return LOCAL_SPECIFIER_PREFIXES.some((prefix) => specifier.startsWith(prefix));
|
|
3272
|
+
}
|
|
3273
|
+
function isFarmPackageManager(value) {
|
|
3274
|
+
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun";
|
|
3275
|
+
}
|
|
3276
|
+
function assertUpgradeChannel(channel) {
|
|
3277
|
+
if (channel !== "latest" && channel !== "beta") throw new Error("Farm upgrade channel must be \"latest\" or \"beta\".");
|
|
3278
|
+
}
|
|
3279
|
+
function formatError(error) {
|
|
3280
|
+
return error instanceof Error ? error.message : String(error);
|
|
3281
|
+
}
|
|
3282
|
+
//#endregion
|
|
3283
|
+
exports.FarmDeployError = FarmDeployError;
|
|
3284
|
+
exports.FarmGeneratedArtifactsStaleError = FarmGeneratedArtifactsStaleError;
|
|
2423
3285
|
exports.addFarmIntegration = require_add_integration.addFarmIntegration;
|
|
2424
3286
|
exports.buildFarm = require_build.buildFarm;
|
|
3287
|
+
exports.createFarmDeployPlan = createFarmDeployPlan;
|
|
3288
|
+
exports.createFarmUpgradePlan = createFarmUpgradePlan;
|
|
2425
3289
|
exports.createFrameworkMigrationPlan = createFrameworkMigrationPlan;
|
|
2426
3290
|
exports.createGatewaySession = createGatewaySession;
|
|
2427
3291
|
exports.createPreviewGatewayPlan = createPreviewGatewayPlan;
|
|
@@ -2433,22 +3297,34 @@ Object.defineProperty(exports, "createServer", {
|
|
|
2433
3297
|
}
|
|
2434
3298
|
});
|
|
2435
3299
|
exports.deployFarm = deployFarm;
|
|
3300
|
+
exports.detectFarmPackageManager = detectFarmPackageManager;
|
|
3301
|
+
exports.explainFarmRoute = explainFarmRoute;
|
|
2436
3302
|
exports.formatFarmCronJobs = formatFarmCronJobs;
|
|
3303
|
+
exports.formatFarmDeployPlan = formatFarmDeployPlan;
|
|
2437
3304
|
exports.formatFarmDoctorReport = formatFarmDoctorReport;
|
|
3305
|
+
exports.formatFarmRouteExplanation = formatFarmRouteExplanation;
|
|
3306
|
+
exports.formatFarmUpgradePlan = formatFarmUpgradePlan;
|
|
2438
3307
|
exports.forwardGatewayRequest = forwardGatewayRequest;
|
|
2439
3308
|
exports.generateFarmArtifacts = generateFarmArtifacts;
|
|
3309
|
+
exports.getFarmTelemetryConfigFile = require_telemetry.getFarmTelemetryConfigFile;
|
|
3310
|
+
exports.getFarmTelemetryStatus = require_telemetry.getFarmTelemetryStatus;
|
|
2440
3311
|
exports.inspectFrameworkMigrations = inspectFrameworkMigrations;
|
|
2441
3312
|
exports.listFarmCronJobs = listFarmCronJobs;
|
|
2442
3313
|
exports.listFarmIntegrationProviders = require_add_integration.listFarmIntegrationProviders;
|
|
2443
3314
|
exports.loadFarmCronConfig = loadFarmCronConfig;
|
|
2444
3315
|
exports.migrateFarm = migrateFarm;
|
|
3316
|
+
exports.migrateFarmAuth = migrateFarmAuth;
|
|
2445
3317
|
exports.parsePreviewPublicUrl = parsePreviewPublicUrl;
|
|
2446
3318
|
exports.previewFarm = previewFarm;
|
|
2447
3319
|
exports.resolveCloudflareAgentDeployPlan = resolveCloudflareAgentDeployPlan;
|
|
3320
|
+
exports.resolveFarmTelemetryCommand = require_telemetry.resolveFarmTelemetryCommand;
|
|
2448
3321
|
exports.resolvePreviewTarget = resolvePreviewTarget;
|
|
2449
3322
|
exports.runFarmCronJob = runFarmCronJob;
|
|
2450
3323
|
exports.runFarmDoctor = runFarmDoctor;
|
|
3324
|
+
exports.runNativePreviewTunnel = runNativePreviewTunnel;
|
|
2451
3325
|
exports.runPreviewGateway = runPreviewGateway;
|
|
3326
|
+
exports.setFarmTelemetryEnabled = require_telemetry.setFarmTelemetryEnabled;
|
|
3327
|
+
exports.showFarmTelemetryNotice = require_telemetry.showFarmTelemetryNotice;
|
|
2452
3328
|
Object.defineProperty(exports, "startDevServer", {
|
|
2453
3329
|
enumerable: true,
|
|
2454
3330
|
get: function() {
|
|
@@ -2456,5 +3332,8 @@ Object.defineProperty(exports, "startDevServer", {
|
|
|
2456
3332
|
}
|
|
2457
3333
|
});
|
|
2458
3334
|
exports.startFarmCronScheduler = startFarmCronScheduler;
|
|
3335
|
+
exports.trackFarmCommand = require_telemetry.trackFarmCommand;
|
|
3336
|
+
exports.trackFarmProjectCreated = require_telemetry.trackFarmProjectCreated;
|
|
3337
|
+
exports.upgradeFarm = upgradeFarm;
|
|
2459
3338
|
|
|
2460
3339
|
//# sourceMappingURL=index.js.map
|