@farm.js/cli 0.1.0-beta.1 → 0.1.0-beta.11

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.mjs CHANGED
@@ -8,11 +8,12 @@ import path from "node:path";
8
8
  import { execFileSync, execSync } from "child_process";
9
9
  import { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "fs";
10
10
  import path$1 from "path";
11
- import { APITypeGenerator, generateEnvTypes, generateFarmI18nTypes, generateRouteTypes, getFarmDocsRouteTypeEntries, getFarmSourceRoots, getIntegrationSchemas, getPresetForDeployTarget, loadConfig, logger, normalizeDeployTarget, resolveConfig, resolveDeployConfig, resolveDeployOutputPath } from "@farm.js/core";
11
+ import { generateFarmTypeArtifacts, getFarmDocsRouteTypeEntries, getFarmSourceRoots, getIntegrationSchemas, getPresetForDeployTarget, loadConfig, logger, normalizeDeployTarget, resolveConfig, resolveDeployConfig, resolveDeployOutputPath } from "@farm.js/core";
12
12
  import { spawn, spawnSync } from "node:child_process";
13
13
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
14
14
  import pc from "picocolors";
15
15
  import { Cron } from "croner";
16
+ import { pathToFileURL } from "node:url";
16
17
  //#region \0rolldown/runtime.js
17
18
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
18
19
  //#endregion
@@ -59,9 +60,7 @@ async function deployPlatform(platform, root, outputDir, deployConfig, prod) {
59
60
  case "cloudflare":
60
61
  await deployCloudflare(root, outputDir, deployConfig.cloudflare?.projectName || deployConfig.projectName);
61
62
  break;
62
- case "netlify":
63
- await deployNetlify(root, outputDir, deployConfig.netlify?.site);
64
- break;
63
+ case "netlify": await deployNetlify(root, outputDir, deployConfig.netlify?.site);
65
64
  }
66
65
  }
67
66
  /**
@@ -282,7 +281,8 @@ async function deployNetlify(root, outputDir, site) {
282
281
  }
283
282
  try {
284
283
  process.chdir(outputDir);
285
- execSync(`netlify deploy --prod --dir=.${site ? ` --site=${site}` : ""}`, { stdio: "inherit" });
284
+ const siteFlag = site ? ` --site=${site}` : "";
285
+ execSync(`netlify deploy --prod --dir=.${siteFlag}`, { stdio: "inherit" });
286
286
  logger.success("✅ Deployed to Netlify successfully!");
287
287
  } catch (error) {
288
288
  logger.error(`❌ Failed to deploy to Netlify: ${error.message}`);
@@ -765,7 +765,7 @@ function commandExists(command) {
765
765
  return !result.error && result.status === 0;
766
766
  }
767
767
  function expandTunnelTemplate(template, target, requestedName, requestedHostname) {
768
- return template.replaceAll("{url}", shellQuote(target.localUrl)).replaceAll("{port}", shellQuote(String(target.port))).replaceAll("{host}", shellQuote(target.host)).replaceAll("{name}", shellQuote(requestedName)).replaceAll("{hostname}", shellQuote(requestedHostname));
768
+ return template.split("{url}").join(shellQuote(target.localUrl)).split("{port}").join(shellQuote(String(target.port))).split("{host}").join(shellQuote(target.host)).split("{name}").join(shellQuote(requestedName)).split("{hostname}").join(shellQuote(requestedHostname));
769
769
  }
770
770
  function shellQuote(value) {
771
771
  return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
@@ -808,31 +808,19 @@ async function generateFarmArtifacts(options = {}) {
808
808
  if (!userConfig && hasSchemaOptions(options)) throw new Error("No Farm config found. Please create farm.config.ts or config.ts.");
809
809
  const resolvedConfig = await resolveConfig({
810
810
  root,
811
- ...userConfig || {}
811
+ ...userConfig
812
812
  }, "development");
813
813
  const extraRoutes = [...resolvedConfig.openapi?.enabled && resolvedConfig.openapi.route ? [resolvedConfig.openapi.route] : [], ...getFarmDocsRouteTypeEntries(resolvedConfig.docs)];
814
- await generateRouteTypes({
814
+ const typeArtifacts = await generateFarmTypeArtifacts({
815
815
  root: resolvedConfig.root,
816
816
  srcDir: resolvedConfig.srcDir,
817
+ configPath: options.configPath,
818
+ layers: resolvedConfig.layers,
817
819
  extraRoutes,
818
- suppressLintOnLink: resolvedConfig.suppressLintOnLink
819
- });
820
- await generateEnvTypes({
821
- root: resolvedConfig.root,
822
- srcDir: resolvedConfig.srcDir,
823
- configPath: options.configPath
820
+ suppressLintOnLink: resolvedConfig.suppressLintOnLink,
821
+ i18nConfig: resolvedConfig.i18n
824
822
  });
825
- const apiGenerator = new APITypeGenerator(path.join(resolvedConfig.root, resolvedConfig.srcDir, "app"));
826
- const apiRoutes = apiGenerator.scanAPIRoutes();
827
- const apiTypesPath = path.join(resolvedConfig.root, resolvedConfig.srcDir, "lib", "api.generated.ts");
828
- await mkdir(path.dirname(apiTypesPath), { recursive: true });
829
- await writeFile(apiTypesPath, apiGenerator.generateAPIRouter(apiRoutes), "utf8");
830
- if (resolvedConfig.i18n.enabled) await generateFarmI18nTypes({
831
- root: resolvedConfig.root,
832
- srcDir: resolvedConfig.srcDir,
833
- config: resolvedConfig.i18n
834
- });
835
- logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types (${apiRoutes.length} API route${apiRoutes.length === 1 ? "" : "s"}).`);
823
+ logger.success(`Generated route, API, env${resolvedConfig.i18n.enabled ? ", and i18n" : ""} types (${typeArtifacts.apiRoutes.length} API route${typeArtifacts.apiRoutes.length === 1 ? "" : "s"}).`);
836
824
  const schemas = getIntegrationSchemas(resolvedConfig.integrations);
837
825
  const schemaEntries = Object.entries(schemas);
838
826
  if (!schemaEntries.length) {
@@ -1071,7 +1059,8 @@ async function writePrismaSchema(schemaPath, models) {
1071
1059
  const source = await readFile(schemaPath, "utf8");
1072
1060
  const generated = createPrismaGeneratedBlock(generatePrismaSchema(models));
1073
1061
  const pattern = new RegExp(`${escapeRegExp(PRISMA_GENERATED_START)}[\\s\\S]*?${escapeRegExp(PRISMA_GENERATED_END)}`, "m");
1074
- await writeFile(schemaPath, pattern.test(source) ? source.replace(pattern, generated) : `${source.trimEnd()}\n\n${generated}\n`, "utf8");
1062
+ const nextSource = pattern.test(source) ? source.replace(pattern, generated) : `${source.trimEnd()}\n\n${generated}\n`;
1063
+ await writeFile(schemaPath, nextSource, "utf8");
1075
1064
  }
1076
1065
  function generatePrismaSchema(models) {
1077
1066
  return models.map((model) => renderPrismaModel(model)).join("\n\n");
@@ -1427,7 +1416,7 @@ async function runFarmDoctor(options = {}) {
1427
1416
  if (!options.offline) try {
1428
1417
  return createLiveReport(await fetchLiveSnapshot(liveTarget, options), liveTarget, options.now);
1429
1418
  } catch (error) {
1430
- liveError = formatError$1(error);
1419
+ liveError = formatError$2(error);
1431
1420
  }
1432
1421
  const report = await createProjectReport(root, options);
1433
1422
  if (!options.offline && hasExplicitLiveTarget(options) && liveError) {
@@ -1587,7 +1576,7 @@ async function createProjectReport(root, options) {
1587
1576
  status: "fail",
1588
1577
  code: "CONFIG_INVALID",
1589
1578
  title: "Farm config could not be resolved",
1590
- message: formatError$1(error),
1579
+ message: formatError$2(error),
1591
1580
  action: "Fix the config or environment validation error, then run farm doctor again."
1592
1581
  });
1593
1582
  }
@@ -1650,7 +1639,7 @@ function collectPackageCheck(root, checks) {
1650
1639
  status: "fail",
1651
1640
  code: "PACKAGE_INVALID",
1652
1641
  title: "package.json is invalid",
1653
- message: formatError$1(error),
1642
+ message: formatError$2(error),
1654
1643
  action: "Fix the package manifest JSON."
1655
1644
  });
1656
1645
  }
@@ -1803,7 +1792,7 @@ function finalizeReport(report) {
1803
1792
  function asRecord(value) {
1804
1793
  return value && typeof value === "object" ? value : {};
1805
1794
  }
1806
- function formatError$1(error) {
1795
+ function formatError$2(error) {
1807
1796
  return error instanceof Error ? error.message : String(error);
1808
1797
  }
1809
1798
  function formatCount$1(value, noun) {
@@ -1860,7 +1849,7 @@ async function startFarmCronScheduler(options = {}) {
1860
1849
  logger.warn(`Cron ${job.name} skipped an overlapping run.`);
1861
1850
  },
1862
1851
  catch: (error) => {
1863
- logger.error(`Cron ${job.name} failed: ${formatError(error)}`);
1852
+ logger.error(`Cron ${job.name} failed: ${formatError$1(error)}`);
1864
1853
  }
1865
1854
  }, async () => {
1866
1855
  logger.info(`Cron ${job.name} -> ${job.path}`);
@@ -1949,7 +1938,7 @@ function formatResponseDetail(body) {
1949
1938
  const detail = typeof body === "string" ? body : JSON.stringify(body);
1950
1939
  return detail ? `: ${detail}` : "";
1951
1940
  }
1952
- function formatError(error) {
1941
+ function formatError$1(error) {
1953
1942
  return error instanceof Error ? error.message : String(error);
1954
1943
  }
1955
1944
  //#endregion
@@ -2066,7 +2055,7 @@ async function createFrameworkMigrationPlan(root, source, options = {}) {
2066
2055
  };
2067
2056
  if (source === "next") await planNextMigration(root, packageJson, plan, options);
2068
2057
  else await planTanStackMigration(root, packageJson, plan, options);
2069
- addSharedFarmFiles(root, packageJson, plan, source, options);
2058
+ addSharedFarmFiles(root, packageJson, plan, options);
2070
2059
  return plan;
2071
2060
  }
2072
2061
  async function planNextMigration(root, packageJson, plan, options) {
@@ -2146,32 +2135,22 @@ async function planTanStackMigration(root, packageJson, plan, options) {
2146
2135
  plan.manual.push("Review loaders, beforeLoad hooks, search params, and Route.use* calls; Farm page modules should move that logic into props, API routes, or server helpers.");
2147
2136
  if (packageJson) addPackageOperation(root, plan, createMigratedPackageJson(packageJson, "tanstack"));
2148
2137
  }
2149
- function addSharedFarmFiles(root, packageJson, plan, source, options) {
2138
+ function addSharedFarmFiles(root, packageJson, plan, options) {
2150
2139
  if (![
2151
2140
  "farm.config.ts",
2152
2141
  "farm.config.mts",
2153
2142
  "farm.config.js",
2154
2143
  "farm.config.mjs"
2155
- ].map((file) => path.join(root, file)).find((file) => existsSync(file))) {
2156
- const output = source === "next" ? `import { defineConfig } from "@farm.js/core";
2157
-
2158
- export default defineConfig({
2159
- srcDir: "src",
2160
- });
2161
- ` : `import { defineConfig } from "@farm.js/core";
2144
+ ].map((file) => path.join(root, file)).find((file) => existsSync(file))) plan.operations.push({
2145
+ kind: "write-file",
2146
+ path: path.join(root, "farm.config.ts"),
2147
+ description: "Create farm.config.ts",
2148
+ content: `import { defineConfig } from "@farm.js/core";
2162
2149
 
2163
- export default defineConfig({
2164
- srcDir: "src",
2165
- });
2166
- `;
2167
- plan.operations.push({
2168
- kind: "write-file",
2169
- path: path.join(root, "farm.config.ts"),
2170
- description: "Create farm.config.ts",
2171
- content: output,
2172
- skipped: false
2173
- });
2174
- }
2150
+ export default defineConfig({});
2151
+ `,
2152
+ skipped: false
2153
+ });
2175
2154
  const layoutPath = path.join(root, "src", "app", "layout.tsx");
2176
2155
  if (!existsSync(layoutPath)) plan.operations.push({
2177
2156
  kind: "write-file",
@@ -2429,6 +2408,201 @@ function toPosix(value) {
2429
2408
  return value.split(path.sep).join("/");
2430
2409
  }
2431
2410
  //#endregion
2432
- export { addFarmIntegration, buildFarm, createFrameworkMigrationPlan, createGatewaySession, createPreviewGatewayPlan, createPreviewTunnelPlan, createServer, deployFarm, formatFarmCronJobs, formatFarmDoctorReport, forwardGatewayRequest, generateFarmArtifacts, inspectFrameworkMigrations, listFarmCronJobs, listFarmIntegrationProviders, loadFarmCronConfig, migrateFarm, parsePreviewPublicUrl, previewFarm, resolveCloudflareAgentDeployPlan, resolvePreviewTarget, runFarmCronJob, runFarmDoctor, runPreviewGateway, startDevServer, startFarmCronScheduler };
2411
+ //#region src/auth.ts
2412
+ async function migrateFarmAuth(options = {}) {
2413
+ const root = path.resolve(options.root || process.cwd());
2414
+ const userConfig = await loadConfig(root, options.configPath, "production");
2415
+ if (!userConfig) throw new Error(`No farm.config file was found in ${root}.`);
2416
+ if (!(await resolveConfig({
2417
+ ...userConfig,
2418
+ root
2419
+ }, "production")).auth.enabled) throw new Error("Farm Auth is disabled. Add `auth: true` to farm.config.ts first.");
2420
+ const resolveFromApp = createRequire(path.join(root, "package.json"));
2421
+ let modulePath;
2422
+ try {
2423
+ modulePath = resolveFromApp.resolve("@farm.js/auth/internal");
2424
+ } catch {
2425
+ throw new Error("Install @farm.js/auth before running `farm auth migrate`.");
2426
+ }
2427
+ const runtime = await import(
2428
+ /* @vite-ignore */
2429
+ pathToFileURL(modulePath).href
2430
+ );
2431
+ logger.info("Applying the Farm Auth database schema...");
2432
+ await runtime.migrateFarmAuth();
2433
+ logger.success("Farm Auth database is ready.");
2434
+ }
2435
+ //#endregion
2436
+ //#region src/upgrade.ts
2437
+ const DEPENDENCY_SECTIONS = [
2438
+ "dependencies",
2439
+ "devDependencies",
2440
+ "optionalDependencies",
2441
+ "peerDependencies"
2442
+ ];
2443
+ const LOCAL_SPECIFIER_PREFIXES = [
2444
+ "workspace:",
2445
+ "file:",
2446
+ "link:",
2447
+ "portal:",
2448
+ "catalog:"
2449
+ ];
2450
+ async function createFarmUpgradePlan(options) {
2451
+ assertUpgradeChannel(options.channel);
2452
+ const root = path.resolve(options.root || process.cwd());
2453
+ const packageJsonPath = path.join(root, "package.json");
2454
+ let packageJson;
2455
+ try {
2456
+ packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
2457
+ } catch (error) {
2458
+ if (!existsSync(packageJsonPath)) throw new Error(`No package.json found at ${packageJsonPath}.`);
2459
+ throw new Error(`Could not read ${packageJsonPath}: ${formatError(error)}`);
2460
+ }
2461
+ const packageManager = options.packageManager || detectFarmPackageManager(root, packageJson.packageManager);
2462
+ const packages = [];
2463
+ const skipped = [];
2464
+ const seen = /* @__PURE__ */ new Set();
2465
+ for (const section of DEPENDENCY_SECTIONS) {
2466
+ const dependencies = packageJson[section];
2467
+ if (!dependencies || typeof dependencies !== "object") continue;
2468
+ for (const [name, rawSpecifier] of Object.entries(dependencies)) {
2469
+ if (!name.startsWith("@farm.js/") || seen.has(name)) continue;
2470
+ seen.add(name);
2471
+ const current = typeof rawSpecifier === "string" ? rawSpecifier : String(rawSpecifier);
2472
+ if (isLocalSpecifier(current)) {
2473
+ skipped.push({
2474
+ name,
2475
+ current,
2476
+ section,
2477
+ reason: "local workspace and file dependencies are not published-package upgrades"
2478
+ });
2479
+ continue;
2480
+ }
2481
+ packages.push({
2482
+ name,
2483
+ current,
2484
+ section,
2485
+ target: `${name}@${options.channel}`
2486
+ });
2487
+ }
2488
+ }
2489
+ if (packages.length === 0) {
2490
+ const suffix = skipped.length > 0 ? " The Farm packages in this project use local workspace or file references." : "";
2491
+ throw new Error(`No published @farm.js/* dependencies were found in ${packageJsonPath}.${suffix}`);
2492
+ }
2493
+ packages.sort((left, right) => left.name.localeCompare(right.name));
2494
+ skipped.sort((left, right) => left.name.localeCompare(right.name));
2495
+ return {
2496
+ root,
2497
+ packageJsonPath,
2498
+ packageManager,
2499
+ channel: options.channel,
2500
+ packages,
2501
+ skipped,
2502
+ commands: createUpgradeCommands(root, packageManager, packages)
2503
+ };
2504
+ }
2505
+ async function upgradeFarm(options) {
2506
+ const plan = await createFarmUpgradePlan(options);
2507
+ if (options.dryRun) return {
2508
+ plan,
2509
+ executed: false
2510
+ };
2511
+ const runCommand = options.runCommand || runFarmUpgradeCommand;
2512
+ for (const command of plan.commands) await runCommand(command);
2513
+ return {
2514
+ plan,
2515
+ executed: true
2516
+ };
2517
+ }
2518
+ function formatFarmUpgradePlan(plan) {
2519
+ const lines = [
2520
+ `Farm upgrade: ${plan.channel === "latest" ? "latest stable" : "latest beta"}`,
2521
+ `Project: ${plan.root}`,
2522
+ `Package manager: ${plan.packageManager}`,
2523
+ "Packages:"
2524
+ ];
2525
+ for (const entry of plan.packages) lines.push(` ${entry.name} (${entry.section}): ${entry.current} -> ${plan.channel}`);
2526
+ if (plan.skipped.length > 0) {
2527
+ lines.push("Skipped local packages:");
2528
+ for (const entry of plan.skipped) lines.push(` ${entry.name} (${entry.current})`);
2529
+ }
2530
+ lines.push("Commands:");
2531
+ for (const command of plan.commands) lines.push(` ${command.command} ${command.args.join(" ")}`);
2532
+ return lines.join("\n");
2533
+ }
2534
+ function detectFarmPackageManager(root, packageManagerField) {
2535
+ if (typeof packageManagerField === "string") {
2536
+ const name = packageManagerField.split("@", 1)[0];
2537
+ if (isFarmPackageManager(name)) return name;
2538
+ }
2539
+ for (const [packageManager, lockfiles] of [
2540
+ ["pnpm", ["pnpm-lock.yaml"]],
2541
+ ["yarn", ["yarn.lock"]],
2542
+ ["bun", ["bun.lock", "bun.lockb"]],
2543
+ ["npm", ["package-lock.json", "npm-shrinkwrap.json"]]
2544
+ ]) if (lockfiles.some((lockfile) => existsSync(path.join(root, lockfile)))) return packageManager;
2545
+ return "npm";
2546
+ }
2547
+ function createUpgradeCommands(root, packageManager, packages) {
2548
+ const command = packageManager === "npm" ? "install" : "add";
2549
+ return DEPENDENCY_SECTIONS.flatMap((section) => {
2550
+ const targets = packages.filter((entry) => entry.section === section).map((entry) => entry.target);
2551
+ if (targets.length === 0) return [];
2552
+ return [{
2553
+ command: packageManager,
2554
+ args: [
2555
+ command,
2556
+ ...getDependencySectionFlags(packageManager, section),
2557
+ ...targets
2558
+ ],
2559
+ cwd: root
2560
+ }];
2561
+ });
2562
+ }
2563
+ function getDependencySectionFlags(packageManager, section) {
2564
+ if (section === "dependencies") return [];
2565
+ if (packageManager === "yarn" || packageManager === "bun") {
2566
+ if (section === "devDependencies") return ["--dev"];
2567
+ if (section === "optionalDependencies") return ["--optional"];
2568
+ return ["--peer"];
2569
+ }
2570
+ if (section === "devDependencies") return ["--save-dev"];
2571
+ if (section === "optionalDependencies") return ["--save-optional"];
2572
+ return ["--save-peer"];
2573
+ }
2574
+ function runFarmUpgradeCommand(command) {
2575
+ return new Promise((resolve, reject) => {
2576
+ const executable = process.platform === "win32" ? `${command.command}.cmd` : command.command;
2577
+ const child = spawn(executable, command.args, {
2578
+ cwd: command.cwd,
2579
+ env: process.env,
2580
+ stdio: "inherit"
2581
+ });
2582
+ child.on("error", reject);
2583
+ child.on("close", (code, signal) => {
2584
+ if (code === 0) {
2585
+ resolve();
2586
+ return;
2587
+ }
2588
+ const termination = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
2589
+ reject(/* @__PURE__ */ new Error(`${command.command} ${command.args.join(" ")} failed with ${termination}.`));
2590
+ });
2591
+ });
2592
+ }
2593
+ function isLocalSpecifier(specifier) {
2594
+ return LOCAL_SPECIFIER_PREFIXES.some((prefix) => specifier.startsWith(prefix));
2595
+ }
2596
+ function isFarmPackageManager(value) {
2597
+ return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun";
2598
+ }
2599
+ function assertUpgradeChannel(channel) {
2600
+ if (channel !== "latest" && channel !== "beta") throw new Error("Farm upgrade channel must be \"latest\" or \"beta\".");
2601
+ }
2602
+ function formatError(error) {
2603
+ return error instanceof Error ? error.message : String(error);
2604
+ }
2605
+ //#endregion
2606
+ export { addFarmIntegration, buildFarm, createFarmUpgradePlan, createFrameworkMigrationPlan, createGatewaySession, createPreviewGatewayPlan, createPreviewTunnelPlan, createServer, deployFarm, detectFarmPackageManager, formatFarmCronJobs, formatFarmDoctorReport, formatFarmUpgradePlan, forwardGatewayRequest, generateFarmArtifacts, inspectFrameworkMigrations, listFarmCronJobs, listFarmIntegrationProviders, loadFarmCronConfig, migrateFarm, migrateFarmAuth, parsePreviewPublicUrl, previewFarm, resolveCloudflareAgentDeployPlan, resolvePreviewTarget, runFarmCronJob, runFarmDoctor, runPreviewGateway, startDevServer, startFarmCronScheduler, upgradeFarm };
2433
2607
 
2434
2608
  //# sourceMappingURL=index.mjs.map