@farm.js/cli 0.1.0-beta.5 → 0.1.0-beta.51

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/dist/index.js CHANGED
@@ -1,52 +1,174 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_add_integration = require("./add-integration-Xc0p-k-m.js");
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 = require_add_integration.__toESM(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 = require_add_integration.__toESM(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 = require_add_integration.__toESM(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 root = options.root || process.cwd();
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 = userConfig ? await (0, _farm_js_core.resolveConfig)(userConfig, mode) : void 0;
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?.deploy.target);
30
- if (platform !== "vercel" && platform !== "cloudflare" && platform !== "netlify") {
31
- _farm_js_core.logger.error("Please specify a deployment target with --vercel, --cloudflare, --netlify, or farm.config deploy.target.");
32
- process.exit(1);
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 ? userConfig?.deploy?.preset || userConfig?.preset || (0, _farm_js_core.getPresetForDeployTarget)(platform) : void 0
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
- if (!(0, fs.existsSync)(nitroOutput)) {
46
- _farm_js_core.logger.error(`Build output not found at ${nitroOutput}. Please run 'farm build' first.`);
47
- process.exit(1);
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
- await deployPlatform(platform, root, nitroOutput, deployConfig, options.prod);
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.execSync)("vercel --version", { stdio: "ignore" });
72
- } catch {
73
- _farm_js_core.logger.error("❌ Vercel CLI is not installed.");
74
- _farm_js_core.logger.info("💡 Install it with: npm i -g vercel");
75
- process.exit(1);
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.execSync)("vercel whoami", { stdio: "ignore" });
79
- } catch {
80
- _farm_js_core.logger.warn("⚠️ Not logged in to Vercel.");
81
- _farm_js_core.logger.info("💡 Please run: vercel login");
82
- _farm_js_core.logger.info(" Then run: farm deploy --vercel");
83
- process.exit(1);
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
- _farm_js_core.logger.error(`❌ Functions directory not found at ${functionsDir}`);
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.execSync)(`vercel deploy --prebuilt --yes${prod ? " --prod" : ""}`, {
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
- _farm_js_core.logger.error(`❌ Failed to deploy to Vercel: ${error.message}`);
187
- process.exit(1);
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
- _farm_js_core.logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
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
- _farm_js_core.logger.error(`❌ Failed to deploy to Cloudflare: ${error.message}`);
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
- _farm_js_core.logger.error(" Wrangler CLI is not installed.");
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.execSync)("netlify --version", { stdio: "ignore" });
276
- } catch {
277
- _farm_js_core.logger.error("❌ Netlify CLI is not installed.");
278
- _farm_js_core.logger.info("💡 Install it with: npm i -g netlify-cli");
279
- process.exit(1);
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
- process.chdir(outputDir);
283
- const siteFlag = site ? ` --site=${site}` : "";
284
- (0, child_process.execSync)(`netlify deploy --prod --dir=.${siteFlag}`, { stdio: "inherit" });
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
- _farm_js_core.logger.error(`❌ Failed to deploy to Netlify: ${error.message}`);
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(`Gateway: ${plan.gatewayUrl}`);
527
- _farm_js_core.logger.info("Opening Farm preview gateway session...");
528
- const session = await runPreviewGateway(plan, { timeoutMs: options.timeoutMs });
529
- return {
530
- target,
531
- plan,
532
- publicUrl: session.publicUrl,
533
- session
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.generateRouteTypes)({
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
818
- });
819
- await (0, _farm_js_core.generateEnvTypes)({
820
- root: resolvedConfig.root,
821
- srcDir: resolvedConfig.srcDir,
822
- configPath: options.configPath
823
- });
824
- const appDir = node_path.default.join(resolvedConfig.root, resolvedConfig.srcDir, "app");
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
1039
+ suppressLintOnLink: resolvedConfig.suppressLintOnLink,
1040
+ componentExtensions: resolvedConfig.renderer.componentExtensions,
1041
+ i18nConfig: resolvedConfig.i18n,
1042
+ check: options.check
834
1043
  });
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,7 +1640,7 @@ 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
1646
  liveError = formatError$2(error);
@@ -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
- root,
1576
- ...userConfig
1798
+ ...userConfig,
1799
+ root: configRoot
1577
1800
  }, "development");
1578
1801
  const configFile = findConfigFile(root, options.configPath);
1579
1802
  checks.push({
@@ -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 ? {
@@ -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.tsx.`
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) {
@@ -1811,6 +2067,415 @@ 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());
@@ -2420,6 +3085,31 @@ 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
2423
3113
  //#region src/upgrade.ts
2424
3114
  const DEPENDENCY_SECTIONS = [
2425
3115
  "dependencies",
@@ -2590,8 +3280,11 @@ function formatError(error) {
2590
3280
  return error instanceof Error ? error.message : String(error);
2591
3281
  }
2592
3282
  //#endregion
3283
+ exports.FarmDeployError = FarmDeployError;
3284
+ exports.FarmGeneratedArtifactsStaleError = FarmGeneratedArtifactsStaleError;
2593
3285
  exports.addFarmIntegration = require_add_integration.addFarmIntegration;
2594
3286
  exports.buildFarm = require_build.buildFarm;
3287
+ exports.createFarmDeployPlan = createFarmDeployPlan;
2595
3288
  exports.createFarmUpgradePlan = createFarmUpgradePlan;
2596
3289
  exports.createFrameworkMigrationPlan = createFrameworkMigrationPlan;
2597
3290
  exports.createGatewaySession = createGatewaySession;
@@ -2605,23 +3298,33 @@ Object.defineProperty(exports, "createServer", {
2605
3298
  });
2606
3299
  exports.deployFarm = deployFarm;
2607
3300
  exports.detectFarmPackageManager = detectFarmPackageManager;
3301
+ exports.explainFarmRoute = explainFarmRoute;
2608
3302
  exports.formatFarmCronJobs = formatFarmCronJobs;
3303
+ exports.formatFarmDeployPlan = formatFarmDeployPlan;
2609
3304
  exports.formatFarmDoctorReport = formatFarmDoctorReport;
3305
+ exports.formatFarmRouteExplanation = formatFarmRouteExplanation;
2610
3306
  exports.formatFarmUpgradePlan = formatFarmUpgradePlan;
2611
3307
  exports.forwardGatewayRequest = forwardGatewayRequest;
2612
3308
  exports.generateFarmArtifacts = generateFarmArtifacts;
3309
+ exports.getFarmTelemetryConfigFile = require_telemetry.getFarmTelemetryConfigFile;
3310
+ exports.getFarmTelemetryStatus = require_telemetry.getFarmTelemetryStatus;
2613
3311
  exports.inspectFrameworkMigrations = inspectFrameworkMigrations;
2614
3312
  exports.listFarmCronJobs = listFarmCronJobs;
2615
3313
  exports.listFarmIntegrationProviders = require_add_integration.listFarmIntegrationProviders;
2616
3314
  exports.loadFarmCronConfig = loadFarmCronConfig;
2617
3315
  exports.migrateFarm = migrateFarm;
3316
+ exports.migrateFarmAuth = migrateFarmAuth;
2618
3317
  exports.parsePreviewPublicUrl = parsePreviewPublicUrl;
2619
3318
  exports.previewFarm = previewFarm;
2620
3319
  exports.resolveCloudflareAgentDeployPlan = resolveCloudflareAgentDeployPlan;
3320
+ exports.resolveFarmTelemetryCommand = require_telemetry.resolveFarmTelemetryCommand;
2621
3321
  exports.resolvePreviewTarget = resolvePreviewTarget;
2622
3322
  exports.runFarmCronJob = runFarmCronJob;
2623
3323
  exports.runFarmDoctor = runFarmDoctor;
3324
+ exports.runNativePreviewTunnel = runNativePreviewTunnel;
2624
3325
  exports.runPreviewGateway = runPreviewGateway;
3326
+ exports.setFarmTelemetryEnabled = require_telemetry.setFarmTelemetryEnabled;
3327
+ exports.showFarmTelemetryNotice = require_telemetry.showFarmTelemetryNotice;
2625
3328
  Object.defineProperty(exports, "startDevServer", {
2626
3329
  enumerable: true,
2627
3330
  get: function() {
@@ -2629,6 +3332,8 @@ Object.defineProperty(exports, "startDevServer", {
2629
3332
  }
2630
3333
  });
2631
3334
  exports.startFarmCronScheduler = startFarmCronScheduler;
3335
+ exports.trackFarmCommand = require_telemetry.trackFarmCommand;
3336
+ exports.trackFarmProjectCreated = require_telemetry.trackFarmProjectCreated;
2632
3337
  exports.upgradeFarm = upgradeFarm;
2633
3338
 
2634
3339
  //# sourceMappingURL=index.js.map