naro-cli 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,10 +5,10 @@ import {
5
5
 
6
6
  // src/deploy.ts
7
7
  import { existsSync as existsSync10 } from "fs";
8
- import { mkdtemp as mkdtemp2, readFile as readFile15, rm as rm5 } from "fs/promises";
8
+ import { mkdtemp as mkdtemp2, readFile as readFile16, rm as rm6 } from "fs/promises";
9
9
  import { tmpdir as tmpdir2 } from "os";
10
- import { basename as basename5, dirname as dirname10, join as join31, resolve as resolve14 } from "path";
11
- import { fileURLToPath as fileURLToPath4 } from "url";
10
+ import { basename as basename4, dirname as dirname9, join as join31, resolve as resolve15 } from "path";
11
+ import { fileURLToPath as fileURLToPath5 } from "url";
12
12
 
13
13
  // ../../packages/build/src/artifact-upload.ts
14
14
  function artifactUploadEntries(artifact) {
@@ -41,7 +41,7 @@ function parseArtifactPutUrls(payload) {
41
41
  }
42
42
  async function eachWithinBudget(items, limit, budget, weigh, work) {
43
43
  if (items.length === 0) return;
44
- await new Promise((resolve15, reject) => {
44
+ await new Promise((resolve16, reject) => {
45
45
  let next = 0;
46
46
  let running = 0;
47
47
  let weight = 0;
@@ -58,7 +58,7 @@ async function eachWithinBudget(items, limit, budget, weigh, work) {
58
58
  running -= 1;
59
59
  weight -= itemWeight;
60
60
  finished += 1;
61
- if (finished === items.length) resolve15();
61
+ if (finished === items.length) resolve16();
62
62
  else pump();
63
63
  }, reject);
64
64
  }
@@ -287,9 +287,9 @@ function hashAsset(data, contentType2) {
287
287
  }
288
288
 
289
289
  // ../../packages/build/src/local-build-executor.ts
290
- import { access as access8, mkdtemp, readFile as readFile13, rm as rm3 } from "fs/promises";
290
+ import { access as access6, mkdtemp, readFile as readFile14, rm as rm4 } from "fs/promises";
291
291
  import { tmpdir } from "os";
292
- import { dirname as dirname4, join as join17, resolve as resolve7 } from "path";
292
+ import { dirname as dirname3, join as join17, resolve as resolve7 } from "path";
293
293
 
294
294
  // ../../packages/core/src/route-patterns.ts
295
295
  var ROUTE_PATTERN = /^\/[A-Za-z0-9\-._~%!$&()*+,;=:@/[\]]*$/;
@@ -527,11 +527,33 @@ var CAPABILITIES = [
527
527
  id: "team-usage",
528
528
  domain: "teams",
529
529
  title: "\uD300 \uC0AC\uC6A9\uB7C9",
530
- routes: ["GET teams/[teamSlug]/usage"],
530
+ routes: [
531
+ "GET teams/[teamSlug]/usage",
532
+ "GET teams/[teamSlug]/usage/hosting"
533
+ ],
531
534
  ui: { kind: "built" },
532
535
  cli: { kind: "built", commands: ["naro usage"] },
533
536
  mcp: { kind: "built", tools: ["get_team_usage"] }
534
537
  },
538
+ {
539
+ id: "team-backend-usage",
540
+ domain: "teams",
541
+ title: "\uD300 \uBC31\uC5D4\uB4DC \uC0AC\uC6A9\uB7C9\xB7\uC77C\uBCC4 \uCC28\uD2B8(\uD568\uC218 CPU\xB7DB\xB7\uC2A4\uD1A0\uB9AC\uC9C0\xB7\uB9AC\uC5BC\uD0C0\uC784)",
542
+ routes: [
543
+ "GET teams/[teamSlug]/backend-usage",
544
+ "GET teams/[teamSlug]/usage/series"
545
+ ],
546
+ dashboard: "/[teamSlug]/projects",
547
+ ui: { kind: "built" },
548
+ cli: {
549
+ kind: "planned",
550
+ note: "\uD300 \uAC1C\uC694 \uCE74\uB4DC\uB85C \uBA3C\uC800 \uB0C8\uB2E4. naro usage\uC5D0 \uBC31\uC5D4\uB4DC \uC904\uC744 \uB354\uD558\uB294 \uAC83\uC774 \uB2E4\uC74C\uC774\uB2E4."
551
+ },
552
+ mcp: {
553
+ kind: "planned",
554
+ note: "get_team_usage\uC5D0 \uBC31\uC5D4\uB4DC \uC0AC\uC6A9\uB7C9\uC744 \uB354\uD558\uB294 \uAC83\uC774 \uB2E4\uC74C\uC774\uB2E4."
555
+ }
556
+ },
535
557
  {
536
558
  id: "billing-view",
537
559
  domain: "billing",
@@ -902,47 +924,6 @@ var CAPABILITIES = [
902
924
  note: "\uBCF5\uD638\uD654\uD55C \uAC12\uC744 .env.local\uC5D0 \uC4F4\uB2E4. list_environment_variables\uB294 \uAC12\uC744 \uAC00\uB9B0\uB2E4. 2026-11-03 \uACB0\uC815."
903
925
  }
904
926
  },
905
- {
906
- id: "supabase-connect",
907
- domain: "env",
908
- title: "\uAE30\uC874 Supabase \uD504\uB85C\uC81D\uD2B8 \uC5F0\uACB0\xB7\uD0A4 \uB2E4\uC2DC \uBC1B\uAE30",
909
- routes: [
910
- "GET supabase/authorize",
911
- "GET supabase/callback",
912
- "GET projects/[projectId]/supabase/projects",
913
- "PUT projects/[projectId]/supabase"
914
- ],
915
- dashboard: "/[teamSlug]/projects/[projectSlug]/settings",
916
- ui: { kind: "built" },
917
- cli: {
918
- kind: "excluded",
919
- reason: "browser-required",
920
- note: "Supabase OAuth \uB9AC\uB2E4\uC774\uB809\uD2B8\uB85C \uB05D\uB09C\uB2E4. \uD1A0\uD070\uC740 10\uBD84 \uCFE0\uD0A4\uC5D0\uB9CC \uC788\uB2E4(\uC124\uACC4 2026-11-04 \xA72.1)."
921
- },
922
- mcp: {
923
- kind: "excluded",
924
- reason: "browser-required",
925
- note: "Supabase OAuth \uB9AC\uB2E4\uC774\uB809\uD2B8\uB85C \uB05D\uB09C\uB2E4. \uB300\uC2DC\uBCF4\uB4DC\uC758 \uD504\uB85C\uC81D\uD2B8 \uC124\uC815 \uD0ED\uC5D0\uC11C \uC5F0\uACB0\uD55C\uB2E4."
926
- }
927
- },
928
- {
929
- id: "supabase-link-list",
930
- domain: "env",
931
- title: "Supabase \uC5F0\uACB0 \uBCF4\uAE30",
932
- routes: ["GET projects/[projectId]/supabase"],
933
- ui: { kind: "built" },
934
- cli: { kind: "built", commands: ["naro supabase"] },
935
- mcp: { kind: "built", tools: ["list_supabase_links"] }
936
- },
937
- {
938
- id: "supabase-disconnect",
939
- domain: "env",
940
- title: "Supabase \uC5F0\uACB0 \uD574\uC81C",
941
- routes: ["DELETE projects/[projectId]/supabase"],
942
- ui: { kind: "built" },
943
- cli: { kind: "built", commands: ["naro supabase disconnect"] },
944
- mcp: { kind: "built", tools: ["disconnect_supabase"] }
945
- },
946
927
  {
947
928
  id: "environment-list",
948
929
  domain: "environments",
@@ -1895,6 +1876,19 @@ var CAPABILITIES = [
1895
1876
  cli: { kind: "built", commands: ["naro backend"] },
1896
1877
  mcp: { kind: "built", tools: ["get_backend"] }
1897
1878
  },
1879
+ {
1880
+ id: "backend-usage",
1881
+ domain: "backend",
1882
+ title: "Backend \uAC1C\uC694\uC758 \uC0AC\uC6A9\uB7C9(DB \uC4F0\uAE30\xB7\uD06C\uAE30, Storage \uC6A9\uB7C9\xB7\uC4F0\uAE30, \uD568\uC218 \uD638\uCD9C, Realtime \uBA54\uC2DC\uC9C0)",
1883
+ routes: ["GET projects/[projectId]/backend/usage"],
1884
+ ui: { kind: "built" },
1885
+ // 같은 숫자를 섹션마다 이미 돌려준다: naro db info(오늘 쓴 행·한도), naro
1886
+ // storage(용량·오늘 쓰기), naro functions(함수별 오늘 호출), naro realtime
1887
+ // (이번 달 메시지). 이 라우트는 개요가 한 번에 읽으려고 모은 것이다.
1888
+ cli: { kind: "built" },
1889
+ // get_database·get_storage·list_functions·get_realtime이 같은 숫자를 준다.
1890
+ mcp: { kind: "built" }
1891
+ },
1898
1892
  {
1899
1893
  id: "backend-enable",
1900
1894
  domain: "backend",
@@ -2270,7 +2264,7 @@ function parseDotenvDetailed(text3) {
2270
2264
  var STALL_WINDOW_MS = 10 * 6e4;
2271
2265
 
2272
2266
  // ../../packages/core/src/deployment-strategy.ts
2273
- var MIN_SUPPORTED_NEXT_MAJOR = 15;
2267
+ var MIN_SUPPORTED_NEXT_MAJOR = 16;
2274
2268
 
2275
2269
  // ../../packages/core/src/environment-guidance.ts
2276
2270
  var DATA_COMMANDS = /* @__PURE__ */ new Set([
@@ -2553,12 +2547,8 @@ var DEPLOYMENT_ERROR_CODES = [
2553
2547
  "ENVIRONMENT_DELETED",
2554
2548
  /** The edge refused the environment's protection change; the database change is rolled back. */
2555
2549
  "ENVIRONMENT_PROTECTION_PUBLISH_FAILED",
2556
- /** The variable came from the Supabase connection; only its card changes it (spec 2026-11-04 §6.3). */
2557
- "ENV_MANAGED",
2558
- /** The 10-minute Supabase sign-in cookie is gone or expired (spec 2026-11-04 §6.4). */
2559
- "SUPABASE_SESSION_EXPIRED",
2560
- /** The Supabase Management API refused or did not answer (spec 2026-11-04 §6.4). */
2561
- "SUPABASE_API_FAILED"
2550
+ /** The variable is owned by the Resend connection; only it changes it (spec 2026-11-04 resend §6.4). */
2551
+ "ENV_MANAGED"
2562
2552
  ];
2563
2553
  var DeploymentError = class extends Error {
2564
2554
  code;
@@ -17636,6 +17626,130 @@ var BUILDS = {
17636
17626
  }
17637
17627
  };
17638
17628
 
17629
+ // ../../packages/next-adapter/src/index.ts
17630
+ import { fileURLToPath } from "url";
17631
+
17632
+ // ../../packages/next-adapter/src/supported.ts
17633
+ var SUPPORTED_NEXT = [
17634
+ { minor: "16.2", next: "16.2.12", routing: "@next/routing-16.2" },
17635
+ { minor: "16.3", next: "16.3.8", routing: "@next/routing-16.3" },
17636
+ { minor: "16.4", next: "16.4.5", routing: "@next/routing-16.4" }
17637
+ ];
17638
+ var VERSION = /^(\d+)\.(\d+)\.(\d+)/;
17639
+ function judgeNextVersion(version3) {
17640
+ const match = VERSION.exec(version3.trim());
17641
+ if (!match) return null;
17642
+ const major = Number(match[1]);
17643
+ const minor = Number(match[2]);
17644
+ if (major < 16 || major === 16 && minor < 2) return { kind: "too-old" };
17645
+ const entry = SUPPORTED_NEXT.find(
17646
+ (candidate) => candidate.minor === `${String(major)}.${String(minor)}`
17647
+ );
17648
+ if (entry) return { kind: "supported", entry };
17649
+ const newest = SUPPORTED_NEXT[SUPPORTED_NEXT.length - 1];
17650
+ if (!newest) throw new Error("SUPPORTED_NEXT is empty.");
17651
+ return { kind: "newer", entry: newest };
17652
+ }
17653
+ function testedRange() {
17654
+ const first = SUPPORTED_NEXT[0];
17655
+ const last = SUPPORTED_NEXT[SUPPORTED_NEXT.length - 1];
17656
+ if (!first || !last) throw new Error("SUPPORTED_NEXT is empty.");
17657
+ return `${first.minor}\u2013${last.minor}`;
17658
+ }
17659
+
17660
+ // ../../packages/next-adapter/src/paths.ts
17661
+ var OUTPUT_DIR = ".naro";
17662
+ var BUILD_RECORD = ".naro/build.json";
17663
+ var WORKER_DIR = ".naro/worker";
17664
+ var ASSETS_DIR = ".naro/assets";
17665
+ var WRANGLER_CONFIG = ".naro/worker/wrangler.jsonc";
17666
+
17667
+ // ../../packages/next-adapter/src/bundle/wrangler.ts
17668
+ import { posix } from "path";
17669
+
17670
+ // ../../packages/next-adapter/src/config-wrapper.ts
17671
+ import { access, readFile as readFile2, rename, writeFile as writeFile2 } from "fs/promises";
17672
+ import { join as join2 } from "path";
17673
+ var PROJECT_CONFIG_BASENAME = "next.config.naro-user";
17674
+ var WRAPPABLE = {
17675
+ "next.config.js": {
17676
+ wrapper: "next.config.mjs",
17677
+ specifier: `./${PROJECT_CONFIG_BASENAME}.js`,
17678
+ typescript: false
17679
+ },
17680
+ "next.config.mjs": {
17681
+ wrapper: "next.config.mjs",
17682
+ specifier: `./${PROJECT_CONFIG_BASENAME}.mjs`,
17683
+ typescript: false
17684
+ },
17685
+ "next.config.ts": {
17686
+ wrapper: "next.config.ts",
17687
+ specifier: `./${PROJECT_CONFIG_BASENAME}`,
17688
+ typescript: true
17689
+ },
17690
+ "next.config.mts": {
17691
+ wrapper: "next.config.mts",
17692
+ specifier: `./${PROJECT_CONFIG_BASENAME}.mts`,
17693
+ typescript: true
17694
+ }
17695
+ };
17696
+ function nextConfigWrapperSource(projectSpecifier, typescript) {
17697
+ return `${typescript ? "// @ts-nocheck\n" : ""}// Written by Naro for this build only. The project's own next.config is
17698
+ // ${projectSpecifier}, unchanged; this wrapper swaps its Next.js adapter for
17699
+ // the Naro adapter (project setting "replace").
17700
+ import projectConfig from ${JSON.stringify(projectSpecifier)};
17701
+
17702
+ export default async function naroNextConfig(phase, context) {
17703
+ const config =
17704
+ typeof projectConfig === "function"
17705
+ ? await projectConfig(phase, context)
17706
+ : await projectConfig;
17707
+ const rest = { ...(config ?? {}) };
17708
+ delete rest.adapterPath;
17709
+ if (rest.experimental && typeof rest.experimental === "object") {
17710
+ rest.experimental = { ...rest.experimental };
17711
+ delete rest.experimental.adapterPath;
17712
+ }
17713
+ return { ...rest, adapterPath: process.env.NEXT_ADAPTER_PATH };
17714
+ }
17715
+ `;
17716
+ }
17717
+ async function exists(path) {
17718
+ try {
17719
+ await access(path);
17720
+ return true;
17721
+ } catch {
17722
+ return false;
17723
+ }
17724
+ }
17725
+ async function wrapNextConfig(appRoot, file2) {
17726
+ const plan = WRAPPABLE[file2];
17727
+ if (!plan) return null;
17728
+ const ext = file2.slice("next.config.".length);
17729
+ const project2 = `${PROJECT_CONFIG_BASENAME}.${ext}`;
17730
+ if (await exists(join2(appRoot, project2)))
17731
+ throw new Error(
17732
+ `${project2} already exists in the project; Naro needs that name to replace the project's Next.js adapter.`
17733
+ );
17734
+ await readFile2(join2(appRoot, file2));
17735
+ await rename(join2(appRoot, file2), join2(appRoot, project2));
17736
+ await writeFile2(
17737
+ join2(appRoot, plan.wrapper),
17738
+ nextConfigWrapperSource(plan.specifier, plan.typescript)
17739
+ );
17740
+ return { wrapper: plan.wrapper, project: project2 };
17741
+ }
17742
+
17743
+ // ../../packages/next-adapter/src/index.ts
17744
+ function nextAdapterPath(base2 = import.meta.url) {
17745
+ if (!base2) return void 0;
17746
+ try {
17747
+ return fileURLToPath(new URL("../dist/adapter.cjs", base2));
17748
+ } catch {
17749
+ return void 0;
17750
+ }
17751
+ }
17752
+
17639
17753
  // ../../packages/build/src/next-version.ts
17640
17754
  var NEXT_RELEASE_DATES = {
17641
17755
  16: "2025-10-21",
@@ -17645,7 +17759,6 @@ var NEXT_RELEASE_DATES = {
17645
17759
  12: "2021-10-26"
17646
17760
  };
17647
17761
  var SUPPORT_WINDOW_YEARS = 2;
17648
- var VERIFIED_15_FLOOR = { major: 15, minor: 5, patch: 21 };
17649
17762
  var VERIFIED_16_FLOOR = { major: 16, minor: 2, patch: 11 };
17650
17763
  function parseNextVersion(raw) {
17651
17764
  const match = /^\s*(\d+)\.(\d+)\.(\d+)/.exec(raw);
@@ -17673,7 +17786,6 @@ function compare(a, b) {
17673
17786
  return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
17674
17787
  }
17675
17788
  function withinVerifiedRange(version3) {
17676
- if (version3.major === 15) return compare(version3, VERIFIED_15_FLOOR) >= 0;
17677
17789
  if (version3.major === 16) return compare(version3, VERIFIED_16_FLOOR) >= 0;
17678
17790
  return version3.major > 16;
17679
17791
  }
@@ -17689,34 +17801,34 @@ function classifyNextVersion(raw, now) {
17689
17801
  reason: `Could not read a Next.js version from "${raw}".`
17690
17802
  };
17691
17803
  }
17692
- const windowEnd = supportEndsAt(version3.major);
17693
- if (!windowEnd || version3.major < VERIFIED_15_FLOOR.major) {
17804
+ if (version3.major < MIN_SUPPORTED_NEXT_MAJOR) {
17694
17805
  return {
17695
17806
  status: "too-old",
17696
17807
  version: version3,
17697
- reason: `Next.js ${raw} is too old to deploy. Naro deploys Next.js 15 or newer. Upgrade to Next.js 16.`
17808
+ reason: `Next.js ${raw} is too old to deploy. Naro deploys Next.js ${String(MIN_SUPPORTED_NEXT_MAJOR)} or newer. Upgrade to Next.js 16.`
17698
17809
  };
17699
17810
  }
17700
- if (now > windowEnd) {
17811
+ const windowEnd = supportEndsAt(version3.major);
17812
+ if (windowEnd && now > windowEnd) {
17701
17813
  return {
17702
- status: "needs-unsupported-flag",
17814
+ status: "past-support-window",
17703
17815
  version: version3,
17704
- reason: `Next.js ${version3.major} left its support window on ${formatDate(windowEnd)}. The build continues, but unsupported releases stop receiving security patches \u2014 upgrade to Next.js 16. See https://nextjs.org/support-policy`
17816
+ reason: `Next.js ${version3.major} left its support window on ${formatDate(windowEnd)}. The build continues, but unsupported releases stop receiving security patches \u2014 upgrade Next.js. See https://nextjs.org/support-policy`
17705
17817
  };
17706
17818
  }
17707
17819
  if (!withinVerifiedRange(version3)) {
17708
17820
  return {
17709
- status: "outside-peer-range",
17821
+ status: "outside-verified-range",
17710
17822
  version: version3,
17711
- reason: `Next.js ${raw} is outside the range Naro verifies deploys on (>=15.5.21 <16 || >=16.2.11). The build may fail in ways Naro cannot explain.`
17823
+ reason: `Next.js ${raw} is outside the range Naro verifies deploys on (>=16.2.11). The build may fail in ways Naro cannot explain.`
17712
17824
  };
17713
17825
  }
17714
17826
  return { status: "supported", version: version3, reason: "" };
17715
17827
  }
17716
17828
 
17717
17829
  // ../../packages/build/src/package-manager.ts
17718
- import { access, readFile as readFile2 } from "fs/promises";
17719
- import { join as join2 } from "path";
17830
+ import { access as access2, readFile as readFile3 } from "fs/promises";
17831
+ import { join as join3 } from "path";
17720
17832
  function cacheEnv(dir, manager) {
17721
17833
  return {
17722
17834
  ...manager === "pnpm" ? { npm_config_store_dir: dir } : {},
@@ -17736,9 +17848,9 @@ function withSharedCache(spec, dir) {
17736
17848
  addEnv: { ...spec.addEnv, ...cacheEnv(dir, spec.name) }
17737
17849
  };
17738
17850
  }
17739
- async function exists(path) {
17851
+ async function exists2(path) {
17740
17852
  try {
17741
- await access(path);
17853
+ await access2(path);
17742
17854
  return true;
17743
17855
  } catch {
17744
17856
  return false;
@@ -17746,7 +17858,7 @@ async function exists(path) {
17746
17858
  }
17747
17859
  async function usesModernYarn(root) {
17748
17860
  try {
17749
- const raw = await readFile2(join2(root, "package.json"), "utf8");
17861
+ const raw = await readFile3(join3(root, "package.json"), "utf8");
17750
17862
  const packageJson = JSON.parse(raw);
17751
17863
  if (typeof packageJson.packageManager !== "string") return false;
17752
17864
  const version3 = /^yarn@(\d+)/.exec(packageJson.packageManager)?.[1];
@@ -17756,7 +17868,7 @@ async function usesModernYarn(root) {
17756
17868
  }
17757
17869
  }
17758
17870
  async function detectPackageManager(root) {
17759
- if (await exists(join2(root, "pnpm-lock.yaml"))) {
17871
+ if (await exists2(join3(root, "pnpm-lock.yaml"))) {
17760
17872
  return {
17761
17873
  name: "pnpm",
17762
17874
  install: ["pnpm", "install", "--frozen-lockfile"],
@@ -17767,7 +17879,7 @@ async function detectPackageManager(root) {
17767
17879
  addArgs: (specs) => ["pnpm", "add", ...specs]
17768
17880
  };
17769
17881
  }
17770
- if (await exists(join2(root, "package-lock.json"))) {
17882
+ if (await exists2(join3(root, "package-lock.json"))) {
17771
17883
  return {
17772
17884
  name: "npm",
17773
17885
  install: ["npm", "ci"],
@@ -17793,7 +17905,7 @@ async function detectPackageManager(root) {
17793
17905
  sharedCacheAddArgs: ["--prefer-offline"]
17794
17906
  };
17795
17907
  }
17796
- if (await exists(join2(root, "yarn.lock"))) {
17908
+ if (await exists2(join3(root, "yarn.lock"))) {
17797
17909
  const modern = await usesModernYarn(root);
17798
17910
  return {
17799
17911
  name: "yarn",
@@ -17814,114 +17926,13 @@ async function detectPackageManager(root) {
17814
17926
  );
17815
17927
  }
17816
17928
 
17817
- // ../../packages/build/src/proxy-to-middleware.ts
17818
- import { access as access2, readFile as readFile3, rename, writeFile as writeFile2 } from "fs/promises";
17819
- import { join as join3 } from "path";
17820
- var EXTENSIONS = ["ts", "tsx", "js", "jsx"];
17821
- var DIRECTORIES = ["", "src"];
17822
- async function exists2(path) {
17823
- try {
17824
- await access2(path);
17825
- return true;
17826
- } catch {
17827
- return false;
17828
- }
17829
- }
17830
- function renameExport(source) {
17831
- return source.replace(
17832
- /(export\s+(?:default\s+)?(?:async\s+)?function\s+)proxy\b/g,
17833
- "$1middleware"
17834
- ).replace(/(export\s+const\s+)proxy\b/g, "$1middleware");
17835
- }
17836
- async function convertProxyToMiddleware(appRoot) {
17837
- for (const dir of DIRECTORIES) {
17838
- for (const ext of EXTENSIONS) {
17839
- const rel = join3(dir, `proxy.${ext}`);
17840
- const proxyPath = join3(appRoot, rel);
17841
- if (!await exists2(proxyPath)) continue;
17842
- const middlewarePath = join3(appRoot, dir, `middleware.${ext}`);
17843
- if (await exists2(middlewarePath)) return { converted: false };
17844
- const source = await readFile3(proxyPath, "utf8");
17845
- await writeFile2(middlewarePath, renameExport(source));
17846
- await rename(proxyPath, `${proxyPath}.converted`);
17847
- return { converted: true, file: rel };
17848
- }
17849
- }
17850
- return { converted: false };
17851
- }
17852
-
17853
17929
  // ../../packages/build/src/vinext.ts
17854
- import { access as access5, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
17855
- import { join as join6, resolve as resolve2 } from "path";
17930
+ import { access as access3, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
17931
+ import { join as join6, resolve } from "path";
17856
17932
 
17857
- // ../../packages/build/src/open-next.ts
17858
- import { access as access3, readdir, readFile as readFile4, realpath } from "fs/promises";
17859
- import {
17860
- basename,
17861
- dirname,
17862
- extname,
17863
- isAbsolute,
17864
- join as join4,
17865
- relative,
17866
- resolve,
17867
- sep
17868
- } from "path";
17869
- function parseJsonc(source) {
17870
- let output = "";
17871
- let index = 0;
17872
- let inString = false;
17873
- let escaped = false;
17874
- while (index < source.length) {
17875
- const current = source[index] ?? "";
17876
- const next = source[index + 1] ?? "";
17877
- if (inString) {
17878
- output += current;
17879
- if (escaped) escaped = false;
17880
- else if (current === "\\") escaped = true;
17881
- else if (current === '"') inString = false;
17882
- index += 1;
17883
- continue;
17884
- }
17885
- if (current === '"') {
17886
- inString = true;
17887
- output += current;
17888
- index += 1;
17889
- continue;
17890
- }
17891
- if (current === "/" && next === "/") {
17892
- index += 2;
17893
- while (index < source.length && source[index] !== "\n") index += 1;
17894
- continue;
17895
- }
17896
- if (current === "/" && next === "*") {
17897
- index += 2;
17898
- while (index < source.length && !(source[index] === "*" && source[index + 1] === "/"))
17899
- index += 1;
17900
- index += 2;
17901
- continue;
17902
- }
17903
- output += current;
17904
- index += 1;
17905
- }
17906
- return JSON.parse(output.replace(/,\s*([}\]])/g, "$1"));
17907
- }
17908
- function confinedPath(root, configuredPath, label2, base2 = root) {
17909
- if (isAbsolute(configuredPath)) {
17910
- throw new DeploymentError(
17911
- "ARTIFACT_INVALID",
17912
- `${label2} must be relative to the application root.`
17913
- );
17914
- }
17915
- const resolved = resolve(base2, configuredPath);
17916
- const relation = relative(root, resolved);
17917
- if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) {
17918
- throw new DeploymentError(
17919
- "ARTIFACT_INVALID",
17920
- `${label2} escapes the application root.`
17921
- );
17922
- }
17923
- return resolved;
17924
- }
17933
+ // ../../packages/build/src/artifact-files.ts
17934
+ import { readdir, readFile as readFile4, realpath } from "fs/promises";
17935
+ import { extname, isAbsolute, join as join4, relative, sep } from "path";
17925
17936
  function contentType(path) {
17926
17937
  switch (extname(path).toLowerCase()) {
17927
17938
  case ".html":
@@ -17996,7 +18007,7 @@ async function assertInsideOnDisk(appRoot, directory, label2) {
17996
18007
  );
17997
18008
  }
17998
18009
  }
17999
- async function listArtifactFiles(root, label2 = "OpenNext", appRoot) {
18010
+ async function listArtifactFiles(root, label2 = "Build", appRoot) {
18000
18011
  if (appRoot !== void 0) await assertInsideOnDisk(appRoot, root, label2);
18001
18012
  const files = [];
18002
18013
  async function visit(directory) {
@@ -18024,131 +18035,12 @@ async function listArtifactFiles(root, label2 = "OpenNext", appRoot) {
18024
18035
  await visit(root);
18025
18036
  return files;
18026
18037
  }
18027
- async function findWranglerConfig(root, configuredPath) {
18028
- if (configuredPath) {
18029
- const resolved = isAbsolute(configuredPath) ? configuredPath : resolve(root, configuredPath);
18030
- const relation = relative(root, resolved);
18031
- if (relation === ".." || relation.startsWith(`..${sep}`) || isAbsolute(relation)) {
18032
- throw new DeploymentError(
18033
- "ARTIFACT_INVALID",
18034
- "Wrangler config escapes the application root."
18035
- );
18036
- }
18037
- await access3(resolved);
18038
- return resolved;
18039
- }
18040
- for (const name of ["wrangler.jsonc", "wrangler.json"]) {
18041
- const path = join4(root, name);
18042
- try {
18043
- await access3(path);
18044
- return path;
18045
- } catch {
18046
- }
18047
- }
18048
- throw new DeploymentError(
18049
- "ARTIFACT_INVALID",
18050
- "A wrangler.jsonc or wrangler.json file is required."
18051
- );
18052
- }
18053
- var OPEN_NEXT_LAYOUT = {
18054
- outputRoot: ".open-next",
18055
- bundleDir: ".open-next/worker-bundle"
18056
- };
18057
- async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
18058
- const configPath = await findWranglerConfig(appRoot, wranglerConfigPath);
18059
- return readWorkerArtifact(appRoot, identity, {
18060
- wranglerConfigPath: configPath,
18061
- ...OPEN_NEXT_LAYOUT
18062
- });
18063
- }
18064
- async function readWorkerArtifact(appRoot, identity, layout) {
18065
- const listFiles = (root) => listArtifactFiles(root, "OpenNext", appRoot);
18066
- try {
18067
- const configPath = await findWranglerConfig(
18068
- appRoot,
18069
- layout.wranglerConfigPath
18070
- );
18071
- const metadata = parseJsonc(
18072
- await readFile4(configPath, "utf8")
18073
- );
18074
- if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
18075
- throw new DeploymentError(
18076
- "ARTIFACT_INVALID",
18077
- "Wrangler metadata must declare main and compatibility_date."
18078
- );
18079
- }
18080
- const configDir = dirname(configPath);
18081
- const outputRoot = confinedPath(appRoot, layout.outputRoot, "Output root");
18082
- const entrypointPath = confinedPath(
18083
- appRoot,
18084
- metadata.main,
18085
- "Worker entrypoint",
18086
- configDir
18087
- );
18088
- const entryRelation = relative(outputRoot, entrypointPath);
18089
- if (entryRelation === ".." || entryRelation.startsWith(`..${sep}`) || isAbsolute(entryRelation)) {
18090
- throw new DeploymentError(
18091
- "ARTIFACT_INVALID",
18092
- `Worker entrypoint must be inside ${layout.outputRoot}.`
18093
- );
18094
- }
18095
- await access3(entrypointPath);
18096
- const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(
18097
- appRoot,
18098
- metadata.assets.directory,
18099
- "Assets directory",
18100
- configDir
18101
- ) : join4(outputRoot, "assets");
18102
- const assetsRelation = relative(outputRoot, configuredAssets);
18103
- if (assetsRelation === ".." || assetsRelation.startsWith(`..${sep}`) || isAbsolute(assetsRelation)) {
18104
- throw new DeploymentError(
18105
- "ARTIFACT_INVALID",
18106
- `Assets directory must be inside ${layout.outputRoot}.`
18107
- );
18108
- }
18109
- const bundleRoot = confinedPath(appRoot, layout.bundleDir, "Bundle dir");
18110
- let bundleExists = true;
18111
- try {
18112
- await access3(bundleRoot);
18113
- } catch {
18114
- bundleExists = false;
18115
- }
18116
- const bundleFiles = bundleExists ? await listFiles(bundleRoot) : null;
18117
- const outputFiles = bundleFiles ?? await listFiles(outputRoot);
18118
- const entrypointName = bundleFiles ? basename(entrypointPath) : relative(outputRoot, entrypointPath).split(sep).join("/");
18119
- const assetsPrefix = `${relative(outputRoot, configuredAssets).split(sep).join("/")}/`;
18120
- const entrypoint = outputFiles.find((file2) => file2.name === entrypointName);
18121
- if (!entrypoint)
18122
- throw new DeploymentError(
18123
- "ARTIFACT_INVALID",
18124
- "Worker entrypoint is missing from the build output."
18125
- );
18126
- return {
18127
- ...identity,
18128
- entrypoint,
18129
- modules: outputFiles.filter(
18130
- (file2) => file2.name !== entrypointName && !file2.name.endsWith(".map") && file2.name !== "README.md" && (bundleFiles !== null || !file2.name.startsWith(assetsPrefix))
18131
- ),
18132
- assets: (await listFiles(configuredAssets)).map((file2) => ({ ...file2 })),
18133
- compatibilityDate: metadata.compatibility_date,
18134
- compatibilityFlags: Array.isArray(metadata.compatibility_flags) ? metadata.compatibility_flags.filter(
18135
- (flag) => typeof flag === "string"
18136
- ) : [],
18137
- assetsBinding: typeof metadata.assets?.binding === "string" ? metadata.assets.binding : "ASSETS"
18138
- };
18139
- } catch (error51) {
18140
- if (error51 instanceof DeploymentError) throw error51;
18141
- throw new DeploymentError(
18142
- "ARTIFACT_INVALID",
18143
- "Could not read the Worker build output.",
18144
- { cause: error51 }
18145
- );
18146
- }
18147
- }
18038
+
18039
+ // ../../packages/build/src/isr-cache-source.generated.ts
18040
+ var NARO_ISR_CACHE_SOURCE = '// Written by naro: vinext\'s data cache, shared across isolates and colos\n// through the dispatcher (spec 2026-11-06-isr-shared-cache \xA75).\n//\n// Layer 1 is this isolate\'s memory, with vinext\'s MemoryCacheHandler\n// semantics. Layer 2 is the store the dispatcher hands this request as\n// `ctx.props.NARO_CACHE` (an RPC stub, valid for this request only \u2014 never\n// kept). With no store (a build-time prerender, `vinext dev`, an older\n// dispatcher) this is a memory cache, as before.\n\nconst MEMORY_MAX_BYTES = 52_428_800;\nconst TAG_MEMO_TTL_MS = 5_000;\nconst TAG_MEMO_MAX = 10_000;\nconst ENTRY_MAX_CHARS = 2_097_152;\nconst ENTRY_MAX_TAGS = 256;\nconst TAG_MAX_CHARS = 1_024;\nconst TAG_LOOKUP_CHUNK = 256;\nconst SERIAL_VERSION = 1;\nconst WARN_INTERVAL_MS = 60_000;\nconst CONTEXT = Symbol.for("__cloudflare-context__");\n\n/** The store for the current request, or null. */\nexport function currentNaroCacheStore() {\n const context = globalThis[CONTEXT];\n return context?.ctx?.props?.NARO_CACHE ?? null;\n}\n\nfunction currentWaitUntil() {\n const ctx = globalThis[CONTEXT]?.ctx;\n return typeof ctx?.waitUntil === "function"\n ? (promise) => ctx.waitUntil(promise)\n : null;\n}\n\nconst isString = (value) => typeof value === "string";\n\nfunction unique(values) {\n return [...new Set(values)];\n}\n\nfunction readStringArray(ctx, field) {\n const value = ctx?.[field];\n return Array.isArray(value) ? value.filter(isString) : [];\n}\n\nfunction readPositiveNumber(ctx, field) {\n const value = ctx?.[field];\n return typeof value === "number" && value > 0 ? value : undefined;\n}\n\nfunction cacheControlOf(ctx) {\n const value = ctx?.cacheControl;\n return value !== null && typeof value === "object" ? value : undefined;\n}\n\nfunction readCacheControlNumber(ctx, field) {\n const value = cacheControlOf(ctx)?.[field] ?? ctx?.[field];\n return typeof value === "number" ? value : undefined;\n}\n\nfunction readCacheControlRevalidate(ctx) {\n const value = cacheControlOf(ctx)?.revalidate ?? ctx?.revalidate;\n return typeof value === "number" || value === false ? value : undefined;\n}\n\nfunction stringMapSize(map) {\n if (!map) return 0;\n let size = 0;\n for (const [key, value] of Object.entries(map)) {\n size += key.length;\n if (Array.isArray(value)) for (const item of value) size += item.length;\n else size += String(value).length;\n }\n return size;\n}\n\n/** vinext\'s estimate (shims/cache-handler.js), so the 50 MiB means the same. */\nfunction valueSize(value) {\n if (value === null || value === undefined) return 25;\n switch (value.kind) {\n case "FETCH":\n return JSON.stringify(value.data ?? "").length;\n case "PAGES":\n return (\n value.html.length +\n JSON.stringify(value.pageData ?? {}).length +\n stringMapSize(value.headers)\n );\n case "APP_PAGE":\n return (\n value.html.length +\n (value.rscData?.byteLength ?? 0) +\n (value.postponed?.length ?? 0) +\n stringMapSize(value.headers)\n );\n case "APP_ROUTE":\n return value.body.byteLength + stringMapSize(value.headers);\n case "REDIRECT":\n return JSON.stringify(value.props ?? {}).length;\n case "IMAGE":\n return (\n value.buffer.byteLength + value.extension.length + value.etag.length\n );\n default:\n return JSON.stringify(value).length;\n }\n}\n\nfunction entrySize(entry) {\n return (\n valueSize(entry.value) +\n entry.tags.reduce((sum, tag) => sum + tag.length, 0) +\n 64\n );\n}\n\nfunction toBase64(bytes) {\n let binary = "";\n for (let i = 0; i < bytes.length; i += 0x8000)\n binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));\n return btoa(binary);\n}\n\nfunction fromBase64(text) {\n const binary = atob(text);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return bytes;\n}\n\n/** One entry as the text layer 2 keeps. ArrayBuffers go as base64. */\nexport function serializeEntry(entry) {\n return JSON.stringify({ v: SERIAL_VERSION, ...entry }, (_key, value) => {\n if (value instanceof ArrayBuffer)\n return { __naroBytes: toBase64(new Uint8Array(value)) };\n if (ArrayBuffer.isView(value))\n return {\n __naroU8: toBase64(\n new Uint8Array(value.buffer, value.byteOffset, value.byteLength),\n ),\n };\n return value;\n });\n}\n\n/** The entry back, or null for another serial version. Throws on bad JSON. */\nexport function deserializeEntry(text) {\n const parsed = JSON.parse(text, (_key, value) => {\n if (value !== null && typeof value === "object") {\n if (isString(value.__naroBytes))\n return fromBase64(value.__naroBytes).buffer;\n if (isString(value.__naroU8)) return fromBase64(value.__naroU8);\n }\n return value;\n });\n if (\n parsed === null ||\n typeof parsed !== "object" ||\n parsed.v !== SERIAL_VERSION ||\n typeof parsed.lastModified !== "number" ||\n !Array.isArray(parsed.tags)\n )\n return null;\n return {\n value: parsed.value ?? null,\n tags: parsed.tags.filter(isString),\n lastModified: parsed.lastModified,\n revalidateAt:\n typeof parsed.revalidateAt === "number" ? parsed.revalidateAt : null,\n expireAt: typeof parsed.expireAt === "number" ? parsed.expireAt : null,\n cacheControl: parsed.cacheControl,\n };\n}\n\nfunction errorMessage(error) {\n return error instanceof Error && error.message\n ? error.message\n : String(error);\n}\n\nexport class NaroIsrCacheHandler {\n constructor({\n storeFor = currentNaroCacheStore,\n now = Date.now,\n waitUntilFor = currentWaitUntil,\n warn = (message) => console.warn(message),\n maxMemoryBytes = MEMORY_MAX_BYTES,\n tagMemoTtlMs = TAG_MEMO_TTL_MS,\n } = {}) {\n this.storeFor = storeFor;\n this.now = now;\n this.waitUntilFor = waitUntilFor;\n this.warn = warn;\n this.maxMemoryBytes = maxMemoryBytes;\n this.tagMemoTtlMs = tagMemoTtlMs;\n this.entries = new Map();\n this.memoryBytes = 0;\n this.tagMemo = new Map();\n this.lastWarnAt = -Infinity;\n }\n\n warnOnce(error) {\n const now = this.now();\n if (now - this.lastWarnAt < WARN_INTERVAL_MS) return;\n this.lastWarnAt = now;\n this.warn(`[naro-isr] shared cache unavailable: ${errorMessage(error)}`);\n }\n\n forget(key) {\n const existing = this.entries.get(key);\n if (!existing) return;\n this.memoryBytes -= existing.size;\n this.entries.delete(key);\n }\n\n remember(key, entry) {\n const size = entrySize(entry);\n this.forget(key);\n if (this.maxMemoryBytes <= 0 || size > this.maxMemoryBytes) return;\n this.entries.set(key, { ...entry, size });\n this.memoryBytes += size;\n while (this.memoryBytes > this.maxMemoryBytes) {\n const oldest = this.entries.keys().next().value;\n if (oldest === undefined) break;\n this.forget(oldest);\n }\n }\n\n setMemo(tag, time, fetchedAt) {\n this.tagMemo.delete(tag);\n this.tagMemo.set(tag, { time, fetchedAt });\n while (this.tagMemo.size > TAG_MEMO_MAX) {\n const oldest = this.tagMemo.keys().next().value;\n if (oldest === undefined) break;\n this.tagMemo.delete(oldest);\n }\n }\n\n /** Each tag\'s last revalidation as far as this isolate knows (ms, 0 = never). */\n async tagTimes(tags, store) {\n const now = this.now();\n const times = new Map();\n const missing = [];\n for (const tag of tags) {\n const memo = this.tagMemo.get(tag);\n // A tag over the cap never reaches layer 2 (set() does not write such\n // entries, the store ignores such tags): this isolate\'s own word only.\n if (\n memo &&\n (!store ||\n tag.length > TAG_MAX_CHARS ||\n now - memo.fetchedAt < this.tagMemoTtlMs)\n )\n times.set(tag, memo.time);\n else if (!store || tag.length > TAG_MAX_CHARS) times.set(tag, 0);\n else missing.push(tag);\n }\n if (missing.length === 0) return times;\n const chunks = [];\n for (let i = 0; i < missing.length; i += TAG_LOOKUP_CHUNK)\n chunks.push(missing.slice(i, i + TAG_LOOKUP_CHUNK));\n await Promise.all(\n chunks.map(async (chunk) => {\n let remote = null;\n try {\n remote = (await store.tagTimes(chunk)) ?? {};\n } catch (error) {\n this.warnOnce(error);\n }\n for (const tag of chunk) {\n const known = this.tagMemo.get(tag)?.time ?? 0;\n // A tag the store did not answer (failed read, or cut off by its\n // cap) keeps its old memo and fetch time, so the next get asks again.\n if (!remote || typeof remote[tag] !== "number") {\n times.set(tag, known);\n continue;\n }\n // Never forget a revalidation this isolate made: KV can answer an\n // older value for up to a minute after the write.\n const time = Math.max(known, remote[tag]);\n this.setMemo(tag, time, now);\n times.set(tag, time);\n }\n }),\n );\n return times;\n }\n\n /**\n * "valid", "own" (one of the entry\'s own tags was revalidated) or "soft"\n * (only a tag of the path asking was). vinext\'s MemoryCacheHandler evicts\n * only for an own tag: fetch entries are shared by several pages.\n */\n async validity(entry, softTags, store) {\n const tags = unique([...entry.tags, ...softTags]);\n if (tags.length === 0) return "valid";\n const times = await this.tagTimes(tags, store);\n const revalidated = (tag) => (times.get(tag) ?? 0) >= entry.lastModified;\n if (entry.tags.some(revalidated)) return "own";\n return softTags.some(revalidated) ? "soft" : "valid";\n }\n\n timing(entry, ctx) {\n const now = this.now();\n if (entry.expireAt !== null && now > entry.expireAt) return "expired";\n const requested = readPositiveNumber(ctx, "revalidate");\n const requestedAt =\n requested === undefined ? null : entry.lastModified + requested * 1000;\n if (\n (entry.revalidateAt !== null && now > entry.revalidateAt) ||\n (requestedAt !== null && now > requestedAt)\n )\n return "stale";\n return undefined;\n }\n\n answer(key, entry, cacheState) {\n // Most recently used goes last.\n const kept = this.entries.get(key);\n if (kept) {\n this.entries.delete(key);\n this.entries.set(key, kept);\n }\n return {\n lastModified: entry.lastModified,\n value: entry.value,\n ...(cacheState === undefined ? {} : { cacheState }),\n cacheControl: entry.cacheControl,\n };\n }\n\n async readRemote(store, key) {\n try {\n const found = await store.getEntry(key);\n if (!found || !isString(found.body)) return null;\n return deserializeEntry(found.body);\n } catch (error) {\n this.warnOnce(error);\n return null;\n }\n }\n\n async get(key, ctx) {\n const store = this.storeFor();\n const softTags = readStringArray(ctx, "softTags");\n const local = this.entries.get(key) ?? null;\n if (local) {\n const localValidity = await this.validity(local, softTags, store);\n if (localValidity === "valid") {\n const cacheState = this.timing(local, ctx);\n if (cacheState === undefined || !store)\n return this.answer(key, local, cacheState);\n // Stale here: another isolate may already have rendered a newer one.\n const remote = await this.readRemote(store, key);\n if (\n remote &&\n remote.lastModified > local.lastModified &&\n (await this.validity(remote, softTags, store)) === "valid"\n ) {\n this.remember(key, remote);\n return this.answer(key, remote, this.timing(remote, ctx));\n }\n return this.answer(key, local, cacheState);\n }\n // Only its own tags evict; a soft tag just makes this get a miss.\n if (localValidity === "own") this.forget(key);\n }\n if (!store) return null;\n const remote = await this.readRemote(store, key);\n if (!remote || (await this.validity(remote, softTags, store)) !== "valid")\n return null;\n this.remember(key, remote);\n return this.answer(key, remote, this.timing(remote, ctx));\n }\n\n async set(key, data, ctx) {\n const tags = unique([\n ...(data && Array.isArray(data.tags) ? data.tags.filter(isString) : []),\n ...readStringArray(ctx, "tags"),\n ]);\n let revalidate = readCacheControlRevalidate(ctx);\n const expire = readCacheControlNumber(ctx, "expire");\n const stale = readCacheControlNumber(ctx, "stale");\n if (data && typeof data.revalidate === "number")\n revalidate = data.revalidate;\n else if (data && data.revalidate === false) revalidate ??= false;\n if (revalidate === 0) return;\n const now = this.now();\n const entry = {\n value: data,\n tags,\n lastModified: now,\n revalidateAt:\n typeof revalidate === "number" && revalidate > 0\n ? now + revalidate * 1000\n : null,\n expireAt:\n typeof expire === "number" && expire > 0 ? now + expire * 1000 : null,\n cacheControl:\n typeof revalidate === "number" || revalidate === false\n ? {\n revalidate,\n ...(expire === undefined ? {} : { expire }),\n ...(stale === undefined ? {} : { stale }),\n }\n : undefined,\n };\n this.remember(key, entry);\n const store = this.storeFor();\n if (!store) return;\n if (\n tags.length > ENTRY_MAX_TAGS ||\n tags.some((tag) => tag.length > TAG_MAX_CHARS)\n )\n return;\n // The serialized text is never shorter than the size estimate (base64 is\n // 4/3 of the bytes, and the field names come on top), so an entry over the\n // limit by estimate is dropped without being serialized, which for a big\n // body would cost several times its size in memory.\n if (entrySize(entry) > ENTRY_MAX_CHARS) return;\n const body = serializeEntry(entry);\n if (body.length > ENTRY_MAX_CHARS) return;\n const write = Promise.resolve()\n .then(() => store.putEntry(key, body, { lastModified: now, tags }))\n .catch((error) => this.warnOnce(error));\n const waitUntil = this.waitUntilFor();\n if (waitUntil) waitUntil(write);\n else await write;\n }\n\n async revalidateTag(tags) {\n const list = unique((Array.isArray(tags) ? tags : [tags]).filter(isString));\n if (list.length === 0) return;\n const now = this.now();\n for (const tag of list)\n this.setMemo(tag, Math.max(this.tagMemo.get(tag)?.time ?? 0, now), now);\n const store = this.storeFor();\n if (!store) return;\n try {\n const recorded = await store.revalidateTags(list);\n if (typeof recorded === "number") {\n const fetchedAt = this.now();\n for (const tag of list)\n this.setMemo(\n tag,\n Math.max(this.tagMemo.get(tag)?.time ?? 0, recorded),\n fetchedAt,\n );\n }\n } catch (error) {\n this.warnOnce(error);\n }\n }\n\n resetRequestCache() {}\n}\n\n/** vinext calls this once per isolate with `{ env, options }`. */\nexport default function createNaroIsrCache({ options } = {}) {\n const ttl = options?.tagMemoTtlMs;\n return new NaroIsrCacheHandler(\n Number.isInteger(ttl) && ttl >= 0 && ttl <= 60_000\n ? { tagMemoTtlMs: ttl }\n : {},\n );\n}\n';
18148
18041
 
18149
18042
  // ../../packages/build/src/platform-config.ts
18150
- import { access as access4, writeFile as writeFile3 } from "fs/promises";
18151
- import { createRequire } from "module";
18043
+ import { writeFile as writeFile3 } from "fs/promises";
18152
18044
  import { join as join5 } from "path";
18153
18045
  var COMPATIBILITY_DATE = "2026-08-19";
18154
18046
  var NEXT_CONFIG_FILES = [
@@ -18157,14 +18049,6 @@ var NEXT_CONFIG_FILES = [
18157
18049
  "next.config.cjs",
18158
18050
  "next.config.ts"
18159
18051
  ];
18160
- async function pathExists(path) {
18161
- try {
18162
- await access4(path);
18163
- return true;
18164
- } catch {
18165
- return false;
18166
- }
18167
- }
18168
18052
  var BUILD_NPMRC = [
18169
18053
  "# Host package-manager credentials are intentionally unavailable.",
18170
18054
  "audit=false",
@@ -18179,61 +18063,40 @@ async function createPackageManagerConfig(appRoot, writer = async (path, content
18179
18063
  await writer(packageManagerConfigPath, BUILD_NPMRC);
18180
18064
  return packageManagerConfigPath;
18181
18065
  }
18182
- async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
18183
- await writeFile3(path, contents, "utf8");
18184
- }, cloudflareModulePath = createRequire(import.meta.url).resolve(
18185
- "@opennextjs/cloudflare"
18186
- ), exists5 = pathExists) {
18187
- const openNextConfigPath = join5(appRoot, ".next-deploy-open-next.config.mjs");
18188
- const wranglerConfigPath = join5(appRoot, ".next-deploy-wrangler.json");
18189
- const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
18190
- let hasNextConfig = false;
18191
- for (const name of NEXT_CONFIG_FILES) {
18192
- if (await exists5(join5(appRoot, name))) {
18193
- hasNextConfig = true;
18194
- break;
18066
+
18067
+ // ../../packages/build/src/wrangler-output.ts
18068
+ function globToRegExp(glob) {
18069
+ let source = "";
18070
+ for (let index = 0; index < glob.length; index += 1) {
18071
+ const char = glob[index];
18072
+ if (char !== "*") {
18073
+ source += char?.replace(/[.+?^${}()|[\]\\]/g, String.raw`\$&`) ?? "";
18074
+ continue;
18075
+ }
18076
+ if (glob[index + 1] !== "*") {
18077
+ source += "[^/]*";
18078
+ continue;
18079
+ }
18080
+ if (glob[index + 2] === "/") {
18081
+ source += "(?:[^/]*/)*";
18082
+ index += 2;
18083
+ } else {
18084
+ source += ".*";
18085
+ index += 1;
18195
18086
  }
18196
18087
  }
18197
- if (!hasNextConfig) {
18198
- await writer(
18199
- join5(appRoot, "next.config.mjs"),
18200
- 'export default { output: "standalone" };\n'
18201
- );
18202
- }
18203
- await writer(
18204
- openNextConfigPath,
18205
- `import { defineCloudflareConfig } from ${JSON.stringify(cloudflareModulePath)};
18206
-
18207
- export default defineCloudflareConfig({});
18208
- `
18209
- );
18210
- await writer(
18211
- wranglerConfigPath,
18212
- `${JSON.stringify(
18213
- {
18214
- name: `next-deploy-${safeDeploymentId}`,
18215
- main: ".open-next/worker.js",
18216
- compatibility_date: COMPATIBILITY_DATE,
18217
- compatibility_flags: ["nodejs_compat"],
18218
- assets: {
18219
- directory: ".open-next/assets",
18220
- binding: "ASSETS"
18221
- }
18222
- },
18223
- null,
18224
- 2
18225
- )}
18226
- `
18227
- );
18228
- const packageManagerConfigPath = await createPackageManagerConfig(
18229
- appRoot,
18230
- writer
18231
- );
18232
- return {
18233
- openNextConfigPath,
18234
- wranglerConfigPath,
18235
- packageManagerConfigPath
18236
- };
18088
+ return new RegExp(`^${source}$`);
18089
+ }
18090
+ function moduleRules(rules) {
18091
+ if (!Array.isArray(rules)) return [];
18092
+ return rules.flatMap((rule) => {
18093
+ const { type, globs } = rule;
18094
+ if (!Array.isArray(globs)) return [];
18095
+ return globs.filter((glob) => typeof glob === "string").map((glob) => ({
18096
+ type: typeof type === "string" ? type : "",
18097
+ test: globToRegExp(glob)
18098
+ }));
18099
+ });
18237
18100
  }
18238
18101
 
18239
18102
  // ../../packages/build/src/vinext.ts
@@ -18256,6 +18119,8 @@ var VITE_CONFIG_NAMES = [
18256
18119
  ];
18257
18120
  var GENERATED_VITE_CONFIG = "vite.config.js";
18258
18121
  var GENERATED_WORKER_ENTRY = "naro-worker.js";
18122
+ var GENERATED_ISR_CACHE = "naro-isr-cache.js";
18123
+ var ISR_SHARED_CACHE_OFF_NOTE = "ISR shared cache is off: the project builds with its own vite.config.";
18259
18124
  var VINEXT_WORKER_ENTRY_SOURCE = `// Written by naro: vinext's Worker, with getCloudflareContext() given its env.
18260
18125
  import { AsyncLocalStorage } from "node:async_hooks";
18261
18126
  import handler from "vinext/server/fetch-handler";
@@ -18290,7 +18155,7 @@ function vinextWranglerConfigPath(appRoot) {
18290
18155
  }
18291
18156
  async function exists3(path) {
18292
18157
  try {
18293
- await access5(path);
18158
+ await access3(path);
18294
18159
  return true;
18295
18160
  } catch {
18296
18161
  return false;
@@ -18314,6 +18179,13 @@ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (pa
18314
18179
  `The project already has a ${GENERATED_WORKER_ENTRY}. Naro writes its vinext Worker entry under that name and will not overwrite a file of the project's.`
18315
18180
  );
18316
18181
  }
18182
+ const isrCachePath = join6(appRoot, GENERATED_ISR_CACHE);
18183
+ if (await exists3(isrCachePath)) {
18184
+ throw new DeploymentError(
18185
+ "ARTIFACT_INVALID",
18186
+ `The project already has a ${GENERATED_ISR_CACHE}. Naro writes its ISR cache handler under that name and will not overwrite a file of the project's.`
18187
+ );
18188
+ }
18317
18189
  const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
18318
18190
  const viteConfigPath = join6(appRoot, GENERATED_VITE_CONFIG);
18319
18191
  const wranglerConfigPath = join6(appRoot, "wrangler.jsonc");
@@ -18326,7 +18198,7 @@ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (pa
18326
18198
  "",
18327
18199
  "export default defineConfig({",
18328
18200
  " plugins: [",
18329
- " vinext(),",
18201
+ ` vinext({ cache: { data: { adapter: ${JSON.stringify(isrCachePath)} } } }),`,
18330
18202
  ' cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] } }),',
18331
18203
  " ],",
18332
18204
  "});",
@@ -18334,6 +18206,7 @@ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (pa
18334
18206
  ].join("\n")
18335
18207
  );
18336
18208
  await writer(workerEntryPath, VINEXT_WORKER_ENTRY_SOURCE);
18209
+ await writer(isrCachePath, NARO_ISR_CACHE_SOURCE);
18337
18210
  await writer(
18338
18211
  wranglerConfigPath,
18339
18212
  `${JSON.stringify(
@@ -18386,8 +18259,8 @@ function parseVinextCheckReport(output) {
18386
18259
  function vinextRouteFromCheck(report) {
18387
18260
  if (!report)
18388
18261
  return {
18389
- eligible: false,
18390
- reason: "vinext check output could not be read; staying on OpenNext. Expected if vinext changed its report format."
18262
+ eligible: true,
18263
+ reason: "vinext check output could not be read; trying the build anyway. Expected if vinext changed its report format."
18391
18264
  };
18392
18265
  const autoFixed = report.issueLines.filter(
18393
18266
  (line) => AUTO_FIXED_ISSUES.some((pattern) => pattern.test(line))
@@ -18399,7 +18272,7 @@ function vinextRouteFromCheck(report) {
18399
18272
  if (blocking > 0)
18400
18273
  return {
18401
18274
  eligible: false,
18402
- reason: `vinext check reported ${blocking} unsupported item(s) (${report.percent}% compatible); staying on OpenNext.`
18275
+ reason: `vinext check reported ${blocking} unsupported item(s) (${report.percent}% compatible).`
18403
18276
  };
18404
18277
  if (autoFixed.length > 0)
18405
18278
  return {
@@ -18411,38 +18284,12 @@ function vinextRouteFromCheck(report) {
18411
18284
  reason: report.partial > 0 ? `vinext check found no blocking issues (${report.partial} partially supported); attempting a vinext build.` : "vinext check found no issues; attempting a vinext build."
18412
18285
  };
18413
18286
  }
18414
- function globToRegExp(glob) {
18415
- let source = "";
18416
- for (let index = 0; index < glob.length; index += 1) {
18417
- const char = glob[index];
18418
- if (char !== "*") {
18419
- source += char?.replace(/[.+?^${}()|[\]\\]/g, String.raw`\$&`) ?? "";
18420
- continue;
18421
- }
18422
- if (glob[index + 1] !== "*") {
18423
- source += "[^/]*";
18424
- continue;
18425
- }
18426
- if (glob[index + 2] === "/") {
18427
- source += "(?:[^/]*/)*";
18428
- index += 2;
18429
- } else {
18430
- source += ".*";
18431
- index += 1;
18432
- }
18433
- }
18434
- return new RegExp(`^${source}$`);
18435
- }
18436
18287
  function moduleMatchers(rules) {
18437
- if (!Array.isArray(rules)) return [];
18438
- return rules.flatMap((rule) => {
18439
- const globs = rule.globs;
18440
- return Array.isArray(globs) ? globs : [];
18441
- }).filter((glob) => typeof glob === "string").map(globToRegExp);
18288
+ return moduleRules(rules).map((rule) => rule.test);
18442
18289
  }
18443
18290
  async function readVinextArtifact(appRoot, identity) {
18444
18291
  try {
18445
- const outputRoot = resolve2(appRoot, VINEXT_OUTPUT_DIR);
18292
+ const outputRoot = resolve(appRoot, VINEXT_OUTPUT_DIR);
18446
18293
  const serverRoot = join6(outputRoot, SERVER_DIR);
18447
18294
  const clientRoot = join6(outputRoot, CLIENT_DIR);
18448
18295
  const configPath = join6(serverRoot, WRANGLER_OUTPUT_NAME);
@@ -18503,7 +18350,7 @@ var FRAMEWORK_PRESETS = {
18503
18350
  id: "next",
18504
18351
  detectDependency: "next",
18505
18352
  defaultBuildScript: "build",
18506
- defaultOutputDirectory: ".open-next",
18353
+ defaultOutputDirectory: "dist",
18507
18354
  needsServerRuntime: true,
18508
18355
  spaFallback: false,
18509
18356
  cachePath: ".next/cache"
@@ -18841,9 +18688,85 @@ async function detectFramework(root, overrides = {}) {
18841
18688
  };
18842
18689
  }
18843
18690
 
18691
+ // ../../packages/build/src/naro-adapter-artifact.ts
18692
+ import { readFile as readFile7 } from "fs/promises";
18693
+ import { join as join8 } from "path";
18694
+ var RULE_CONTENT_TYPES = {
18695
+ ESModule: "application/javascript+module",
18696
+ CommonJS: "application/javascript",
18697
+ Text: "text/plain",
18698
+ Data: "application/octet-stream",
18699
+ CompiledWasm: "application/wasm"
18700
+ };
18701
+ var CONFIG_NAME = "wrangler.jsonc";
18702
+ async function readNaroAdapterArtifact(appRoot, identity) {
18703
+ try {
18704
+ const workerRoot = join8(appRoot, WORKER_DIR);
18705
+ let metadata;
18706
+ try {
18707
+ metadata = JSON.parse(
18708
+ await readFile7(join8(workerRoot, CONFIG_NAME), "utf8")
18709
+ );
18710
+ } catch (error51) {
18711
+ throw new DeploymentError(
18712
+ "ARTIFACT_INVALID",
18713
+ `The Naro adapter produced no ${WORKER_DIR}/${CONFIG_NAME}.`,
18714
+ { cause: error51 }
18715
+ );
18716
+ }
18717
+ if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string")
18718
+ throw new DeploymentError(
18719
+ "ARTIFACT_INVALID",
18720
+ "Naro adapter wrangler output must declare main and compatibility_date."
18721
+ );
18722
+ const rules = moduleRules(metadata.rules);
18723
+ const files = await listArtifactFiles(workerRoot, "naro-adapter", appRoot);
18724
+ const entry = files.find((file2) => file2.name === metadata.main);
18725
+ if (!entry)
18726
+ throw new DeploymentError(
18727
+ "ARTIFACT_INVALID",
18728
+ `Naro adapter worker entrypoint "${metadata.main}" is missing from the build output.`
18729
+ );
18730
+ const modules = files.filter(
18731
+ (file2) => file2.name !== metadata.main && file2.name !== CONFIG_NAME && !file2.name.endsWith(".map")
18732
+ ).map((file2) => {
18733
+ const rule = rules.find((candidate) => candidate.test.test(file2.name));
18734
+ const contentType2 = rule ? RULE_CONTENT_TYPES[rule.type] : void 0;
18735
+ if (!contentType2)
18736
+ throw new DeploymentError(
18737
+ "ARTIFACT_INVALID",
18738
+ `Naro adapter output file "${file2.name}" matches no module rule.`
18739
+ );
18740
+ return { ...file2, contentType: contentType2 };
18741
+ });
18742
+ return {
18743
+ ...identity,
18744
+ entrypoint: { ...entry, contentType: "application/javascript+module" },
18745
+ modules,
18746
+ assets: await listArtifactFiles(
18747
+ join8(appRoot, ASSETS_DIR),
18748
+ "naro-adapter",
18749
+ appRoot
18750
+ ),
18751
+ compatibilityDate: metadata.compatibility_date,
18752
+ compatibilityFlags: Array.isArray(metadata.compatibility_flags) ? metadata.compatibility_flags.filter(
18753
+ (flag) => typeof flag === "string"
18754
+ ) : [],
18755
+ assetsBinding: typeof metadata.assets?.binding === "string" ? metadata.assets.binding : "ASSETS"
18756
+ };
18757
+ } catch (error51) {
18758
+ if (error51 instanceof DeploymentError) throw error51;
18759
+ throw new DeploymentError(
18760
+ "ARTIFACT_INVALID",
18761
+ "Could not read the Naro adapter output.",
18762
+ { cause: error51 }
18763
+ );
18764
+ }
18765
+ }
18766
+
18844
18767
  // ../../packages/build/src/next-routes.ts
18845
18768
  import { readdir as readdir2, stat as stat2 } from "fs/promises";
18846
- import { join as join8 } from "path";
18769
+ import { join as join9 } from "path";
18847
18770
  var APP_PAGE_FILE = /^page\.(?:js|jsx|ts|tsx|md|mdx)$/;
18848
18771
  var PAGES_ROUTER_FILE = /^(.+)\.(?:js|jsx|ts|tsx)$/;
18849
18772
  var DECLARATION_FILE = /\.d\.ts$/;
@@ -18857,7 +18780,7 @@ function errorCode(error51) {
18857
18780
  }
18858
18781
  async function firstDirectory(root, candidates) {
18859
18782
  for (const candidate of candidates) {
18860
- const path = join8(root, candidate);
18783
+ const path = join9(root, candidate);
18861
18784
  try {
18862
18785
  if ((await stat2(path)).isDirectory()) return path;
18863
18786
  } catch (error51) {
@@ -18887,7 +18810,7 @@ async function readAppRouter(dir, segments, routes) {
18887
18810
  const name = entry.name;
18888
18811
  if (name.startsWith("_") || INTERCEPTING.test(name)) continue;
18889
18812
  const next = ROUTE_GROUP.test(name) || name.startsWith("@") ? segments : [...segments, urlSegment(name, "app")];
18890
- await readAppRouter(join8(dir, name), next, routes);
18813
+ await readAppRouter(join9(dir, name), next, routes);
18891
18814
  }
18892
18815
  }
18893
18816
  async function readPagesRouter(dir, segments, routes) {
@@ -18896,7 +18819,7 @@ async function readPagesRouter(dir, segments, routes) {
18896
18819
  if (entry.isDirectory()) {
18897
18820
  if (segments.length === 0 && entry.name === "api") continue;
18898
18821
  await readPagesRouter(
18899
- join8(dir, entry.name),
18822
+ join9(dir, entry.name),
18900
18823
  [...segments, urlSegment(entry.name, "pages")],
18901
18824
  routes
18902
18825
  );
@@ -18914,8 +18837,8 @@ async function readPagesRouter(dir, segments, routes) {
18914
18837
  }
18915
18838
  }
18916
18839
  async function listNextRoutes(appRoot) {
18917
- const app = await firstDirectory(appRoot, ["app", join8("src", "app")]);
18918
- const pages = await firstDirectory(appRoot, ["pages", join8("src", "pages")]);
18840
+ const app = await firstDirectory(appRoot, ["app", join9("src", "app")]);
18841
+ const pages = await firstDirectory(appRoot, ["pages", join9("src", "pages")]);
18919
18842
  if (!app && !pages) return null;
18920
18843
  const routes = /* @__PURE__ */ new Set();
18921
18844
  if (app) await readAppRouter(app, [], routes);
@@ -19152,14 +19075,14 @@ async function runCommand(spec, observer) {
19152
19075
 
19153
19076
  // ../../packages/build/src/repository.ts
19154
19077
  import { realpath as realpath2 } from "fs/promises";
19155
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
19078
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
19156
19079
  function resolveAppRoot(checkout, configuredRoot) {
19157
- const checkoutRoot = resolve3(checkout);
19080
+ const checkoutRoot = resolve2(checkout);
19158
19081
  if (!configuredRoot) return checkoutRoot;
19159
19082
  if (isAbsolute2(configuredRoot)) {
19160
19083
  throw new Error("Configured root directory escapes repository");
19161
19084
  }
19162
- const appRoot = resolve3(checkoutRoot, configuredRoot);
19085
+ const appRoot = resolve2(checkoutRoot, configuredRoot);
19163
19086
  const relativeRoot = relative2(checkoutRoot, appRoot);
19164
19087
  if (relativeRoot === ".." || relativeRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute2(relativeRoot)) {
19165
19088
  throw new Error("Configured root directory escapes repository");
@@ -19190,8 +19113,8 @@ async function assertAppRootInsideCheckout(checkout, appRoot) {
19190
19113
  }
19191
19114
 
19192
19115
  // ../../packages/build/src/static-artifact.ts
19193
- import { readdir as readdir3, readFile as readFile7 } from "fs/promises";
19194
- import { isAbsolute as isAbsolute3, join as join9, relative as relative3, resolve as resolve4, sep as sep3 } from "path";
19116
+ import { readdir as readdir3, readFile as readFile8 } from "fs/promises";
19117
+ import { isAbsolute as isAbsolute3, join as join10, relative as relative3, resolve as resolve3, sep as sep3 } from "path";
19195
19118
  var SHIM_WORKER_SOURCE = "export default { fetch: (request, env) => env.ASSETS.fetch(request) };\n";
19196
19119
  var EXCLUDED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules"]);
19197
19120
  var ALLOWED_DOTFILE = ".well-known";
@@ -19205,7 +19128,7 @@ async function readStaticArtifact(appRoot, outputDirectory, identity, options, o
19205
19128
  "ARTIFACT_INVALID",
19206
19129
  "Output directory must be relative to the application root."
19207
19130
  );
19208
- const outputRoot = resolve4(appRoot, outputDirectory);
19131
+ const outputRoot = resolve3(appRoot, outputDirectory);
19209
19132
  const relation = relative3(appRoot, outputRoot);
19210
19133
  if (relation === ".." || relation.startsWith(`..${sep3}`) || isAbsolute3(relation))
19211
19134
  throw new DeploymentError(
@@ -19224,7 +19147,7 @@ async function readStaticArtifact(appRoot, outputDirectory, identity, options, o
19224
19147
  (left, right) => left.name.localeCompare(right.name)
19225
19148
  )) {
19226
19149
  if (excluded(entry.name)) continue;
19227
- const path = join9(directory, entry.name);
19150
+ const path = join10(directory, entry.name);
19228
19151
  if (entry.isSymbolicLink())
19229
19152
  throw new DeploymentError(
19230
19153
  "ARTIFACT_INVALID",
@@ -19234,7 +19157,7 @@ async function readStaticArtifact(appRoot, outputDirectory, identity, options, o
19234
19157
  else if (entry.isFile()) {
19235
19158
  assets.push({
19236
19159
  name: relative3(outputRoot, path).split(sep3).join("/"),
19237
- data: await readFile7(path),
19160
+ data: await readFile8(path),
19238
19161
  contentType: contentType(path)
19239
19162
  });
19240
19163
  }
@@ -19280,8 +19203,8 @@ async function readStaticArtifact(appRoot, outputDirectory, identity, options, o
19280
19203
 
19281
19204
  // ../../packages/build/src/strategy/fingerprint.ts
19282
19205
  import { createHash as createHash3 } from "crypto";
19283
- import { readFile as readFile8 } from "fs/promises";
19284
- import { join as join10, relative as relative4, sep as sep4 } from "path";
19206
+ import { readFile as readFile9 } from "fs/promises";
19207
+ import { join as join11, relative as relative4, sep as sep4 } from "path";
19285
19208
  var APP_ROOT_FILES = [
19286
19209
  "package.json",
19287
19210
  "next.config.js",
@@ -19318,18 +19241,18 @@ function sha256(data) {
19318
19241
  }
19319
19242
  async function hashFile(path) {
19320
19243
  try {
19321
- return sha256(await readFile8(path));
19244
+ return sha256(await readFile9(path));
19322
19245
  } catch {
19323
19246
  return null;
19324
19247
  }
19325
19248
  }
19326
19249
  async function hashStrategyInputs(checkout, appRoot) {
19327
19250
  const candidates = /* @__PURE__ */ new Set();
19328
- for (const name of CHECKOUT_FILES) candidates.add(join10(checkout, name));
19329
- for (const name of APP_ROOT_FILES) candidates.add(join10(appRoot, name));
19251
+ for (const name of CHECKOUT_FILES) candidates.add(join11(checkout, name));
19252
+ for (const name of APP_ROOT_FILES) candidates.add(join11(appRoot, name));
19330
19253
  for (const base2 of SOURCE_FILES) {
19331
19254
  for (const ext of SOURCE_EXTENSIONS) {
19332
- candidates.add(join10(appRoot, `${base2}.${ext}`));
19255
+ candidates.add(join11(appRoot, `${base2}.${ext}`));
19333
19256
  }
19334
19257
  }
19335
19258
  const entries = [];
@@ -19357,12 +19280,12 @@ async function computeStrategyFingerprint(input) {
19357
19280
  }
19358
19281
 
19359
19282
  // ../../packages/build/src/strategy/project-facts.ts
19360
- import { access as access6, readFile as readFile10 } from "fs/promises";
19361
- import { join as join12 } from "path";
19283
+ import { access as access4, readFile as readFile11 } from "fs/promises";
19284
+ import { join as join13 } from "path";
19362
19285
 
19363
19286
  // ../../packages/build/src/strategy/user-adapter.ts
19364
- import { readFile as readFile9 } from "fs/promises";
19365
- import { join as join11 } from "path";
19287
+ import { readFile as readFile10 } from "fs/promises";
19288
+ import { join as join12 } from "path";
19366
19289
  var ADAPTER_PATH = /\badapterPath\b/;
19367
19290
  var EXPERIMENTAL_ADAPTER_PATH = /\bexperimental\s*:\s*\{[^{}]*\badapterPath\b/;
19368
19291
  function adapterKeyFromConfigSource(source) {
@@ -19394,8 +19317,8 @@ async function adapterInBuildOutput(appRoot, nextVersion) {
19394
19317
  let manifest2;
19395
19318
  try {
19396
19319
  const parsed = JSON.parse(
19397
- await readFile9(
19398
- join11(appRoot, ".next", "required-server-files.json"),
19320
+ await readFile10(
19321
+ join12(appRoot, ".next", "required-server-files.json"),
19399
19322
  "utf8"
19400
19323
  )
19401
19324
  );
@@ -19406,10 +19329,14 @@ async function adapterInBuildOutput(appRoot, nextVersion) {
19406
19329
  }
19407
19330
  const topLevel = manifest2.config?.adapterPath;
19408
19331
  if (keys.includes("adapterPath") && typeof topLevel === "string" && topLevel)
19409
- return { source: "build-output", key: "adapterPath" };
19332
+ return { source: "build-output", key: "adapterPath", path: topLevel };
19410
19333
  const experimental = manifest2.config?.experimental?.adapterPath;
19411
19334
  if (keys.includes("experimental.adapterPath") && typeof experimental === "string" && experimental)
19412
- return { source: "build-output", key: "experimental.adapterPath" };
19335
+ return {
19336
+ source: "build-output",
19337
+ key: "experimental.adapterPath",
19338
+ path: experimental
19339
+ };
19413
19340
  return null;
19414
19341
  }
19415
19342
  function describeUserAdapter(adapter) {
@@ -19422,9 +19349,6 @@ function describeUserAdapter(adapter) {
19422
19349
  return `${adapter.key} in the Next.js build output`;
19423
19350
  }
19424
19351
  }
19425
- function openNextAdapterWarning(adapter) {
19426
- return `P2 opennext-cloudflare: The project sets its own Next.js adapter (${describeUserAdapter(adapter)}); its modifyConfig and onBuildComplete also run in the OpenNext build.`;
19427
- }
19428
19352
 
19429
19353
  // ../../packages/build/src/strategy/project-facts.ts
19430
19354
  var SOURCE_EXTENSIONS2 = ["ts", "tsx", "js", "jsx", "mjs", "mts"];
@@ -19437,17 +19361,17 @@ var VITE_CONFIGS = [
19437
19361
  ];
19438
19362
  async function exists4(path) {
19439
19363
  try {
19440
- await access6(path);
19364
+ await access4(path);
19441
19365
  return true;
19442
19366
  } catch {
19443
19367
  return false;
19444
19368
  }
19445
19369
  }
19446
- async function findSourceFile(appRoot, basename6) {
19370
+ async function findSourceFile(appRoot, basename5) {
19447
19371
  for (const dir of SOURCE_DIRECTORIES) {
19448
19372
  for (const ext of SOURCE_EXTENSIONS2) {
19449
- const rel = dir ? `${dir}/${basename6}.${ext}` : `${basename6}.${ext}`;
19450
- if (await exists4(join12(appRoot, rel))) return rel;
19373
+ const rel = dir ? `${dir}/${basename5}.${ext}` : `${basename5}.${ext}`;
19374
+ if (await exists4(join13(appRoot, rel))) return rel;
19451
19375
  }
19452
19376
  }
19453
19377
  return null;
@@ -19467,14 +19391,15 @@ function matcherRoutesFromSource(source) {
19467
19391
  }
19468
19392
  async function readIfExists(path) {
19469
19393
  try {
19470
- return await readFile10(path, "utf8");
19394
+ return await readFile11(path, "utf8");
19471
19395
  } catch {
19472
19396
  return null;
19473
19397
  }
19474
19398
  }
19475
19399
  async function findAdapterConfig(appRoot) {
19476
19400
  for (const file2 of NEXT_CONFIG_FILES) {
19477
- const source = await readIfExists(join12(appRoot, file2));
19401
+ if (file2 === "next.config.cjs") continue;
19402
+ const source = await readIfExists(join13(appRoot, file2));
19478
19403
  if (source === null) continue;
19479
19404
  const key = adapterKeyFromConfigSource(source);
19480
19405
  return key ? { file: file2, key } : null;
@@ -19485,10 +19410,10 @@ async function collectProjectFacts(appRoot) {
19485
19410
  const proxyFile = await findSourceFile(appRoot, "proxy");
19486
19411
  const middlewareFile = await findSourceFile(appRoot, "middleware");
19487
19412
  const routeSourceFile = middlewareFile ?? proxyFile;
19488
- const routeSource = routeSourceFile ? await readIfExists(join12(appRoot, routeSourceFile)) : null;
19413
+ const routeSource = routeSourceFile ? await readIfExists(join13(appRoot, routeSourceFile)) : null;
19489
19414
  let usesVinext = false;
19490
19415
  let nextSpec = null;
19491
- const packageJsonSource = await readIfExists(join12(appRoot, "package.json"));
19416
+ const packageJsonSource = await readIfExists(join13(appRoot, "package.json"));
19492
19417
  if (packageJsonSource) {
19493
19418
  try {
19494
19419
  const packageJson = JSON.parse(packageJsonSource);
@@ -19499,7 +19424,7 @@ async function collectProjectFacts(appRoot) {
19499
19424
  nextSpec = dependency("next");
19500
19425
  if (dependency("vinext") !== null) {
19501
19426
  for (const name of VITE_CONFIGS) {
19502
- const config2 = await readIfExists(join12(appRoot, name));
19427
+ const config2 = await readIfExists(join13(appRoot, name));
19503
19428
  if (config2 && /\bvinext\s*\(/.test(config2) && /@cloudflare\/vite-plugin/.test(config2)) {
19504
19429
  usesVinext = true;
19505
19430
  break;
@@ -19768,7 +19693,8 @@ async function fetchStatus(fetchImpl, url2, timeoutMs) {
19768
19693
  clearTimeout(timer);
19769
19694
  }
19770
19695
  }
19771
- function probeOk(status) {
19696
+ function probeOk(status, expect) {
19697
+ if (expect === "found" && status === 404) return false;
19772
19698
  return status < 500;
19773
19699
  }
19774
19700
  function createSmokeTester(options = {}) {
@@ -19855,7 +19781,11 @@ function createSmokeTester(options = {}) {
19855
19781
  `${origin}${probe.path}`,
19856
19782
  probeTimeoutMs
19857
19783
  );
19858
- const result = "status" in answer ? { ...probe, status: answer.status, ok: probeOk(answer.status) } : { ...probe, status: null, ok: false, detail: answer.error };
19784
+ const result = "status" in answer ? {
19785
+ ...probe,
19786
+ status: answer.status,
19787
+ ok: probeOk(answer.status, probe.expect)
19788
+ } : { ...probe, status: null, ok: false, detail: answer.error };
19859
19789
  probes2.push(result);
19860
19790
  await observer.onLog({
19861
19791
  level: result.ok ? "INFO" : "ERROR",
@@ -19900,13 +19830,40 @@ function defaultSmokeProbes(matcherRoutes) {
19900
19830
  return probes2;
19901
19831
  }
19902
19832
 
19903
- // ../../packages/build/src/strategy/strategies/opennext-aws.ts
19904
- import { writeFile as writeFile6 } from "fs/promises";
19905
- import { join as join14 } from "path";
19833
+ // ../../packages/build/src/strategy/strategies/unavailable.ts
19834
+ function createUnavailableStrategy(options) {
19835
+ return {
19836
+ id: options.id,
19837
+ priority: options.priority,
19838
+ target: options.target,
19839
+ supports: () => ({ supported: false, reason: options.reason }),
19840
+ evaluate: async () => ({ status: "skipped", reason: options.reason })
19841
+ };
19842
+ }
19843
+ function createSplitCloudflareStrategy() {
19844
+ return createUnavailableStrategy({
19845
+ id: "split-cloudflare",
19846
+ priority: 3,
19847
+ target: "cloudflare-split",
19848
+ reason: "Split Cloudflare deployment needs the dispatcher to chain a middleware Worker in front of the app Worker; the runtime plane routes one Worker per hostname today."
19849
+ });
19850
+ }
19851
+ function createAwsMiddlewareCloudflareAppStrategy() {
19852
+ return createUnavailableStrategy({
19853
+ id: "aws-middleware-cloudflare-app",
19854
+ priority: 4,
19855
+ target: "aws-hybrid",
19856
+ reason: "Hybrid AWS middleware + Cloudflare app needs an AWS deploy target and cross-cloud request chaining; neither exists on this control plane."
19857
+ });
19858
+ }
19859
+
19860
+ // ../../packages/build/src/strategy/strategies/naro-adapter-cloudflare.ts
19861
+ import { readFile as readFile13, rm as rm2 } from "fs/promises";
19862
+ import { join as join15, resolve as resolve5 } from "path";
19906
19863
 
19907
19864
  // ../../packages/build/src/strategy/strategies/shared.ts
19908
- import { access as access7, readFile as readFile11, rm, writeFile as writeFile5 } from "fs/promises";
19909
- import { dirname as dirname2, join as join13, resolve as resolve5 } from "path";
19865
+ import { access as access5, readFile as readFile12, rm, writeFile as writeFile5 } from "fs/promises";
19866
+ import { dirname, join as join14, resolve as resolve4 } from "path";
19910
19867
  function capturingObserver(observer) {
19911
19868
  const lines = [];
19912
19869
  return {
@@ -19942,31 +19899,28 @@ function describeFailure(step) {
19942
19899
  function failed(stage, reason) {
19943
19900
  return { status: "failed", stage, reason };
19944
19901
  }
19945
- function appBuildFailed(reason) {
19946
- return { status: "failed", stage: "build", reason, appBuild: true };
19947
- }
19948
19902
  async function info(observer, message) {
19949
19903
  await observer.onLog({ level: "INFO", stage: "BUILDING", message });
19950
19904
  }
19951
19905
  async function warn(observer, message) {
19952
19906
  await observer.onLog({ level: "WARN", stage: "BUILDING", message });
19953
19907
  }
19954
- async function pathExists2(path) {
19908
+ async function pathExists(path) {
19955
19909
  try {
19956
- await access7(path);
19910
+ await access5(path);
19957
19911
  return true;
19958
19912
  } catch {
19959
19913
  return false;
19960
19914
  }
19961
19915
  }
19962
19916
  async function findProjectBin(appRoot, checkout, name) {
19963
- let dir = resolve5(appRoot);
19964
- const stop = resolve5(checkout);
19917
+ let dir = resolve4(appRoot);
19918
+ const stop = resolve4(checkout);
19965
19919
  for (; ; ) {
19966
- const candidate = join13(dir, "node_modules", ".bin", name);
19967
- if (await pathExists2(candidate)) return candidate;
19920
+ const candidate = join14(dir, "node_modules", ".bin", name);
19921
+ if (await pathExists(candidate)) return candidate;
19968
19922
  if (dir === stop) return null;
19969
- const parent = dirname2(dir);
19923
+ const parent = dirname(dir);
19970
19924
  if (parent === dir) return null;
19971
19925
  dir = parent;
19972
19926
  }
@@ -19977,7 +19931,7 @@ function dotenvQuote(value) {
19977
19931
  return `"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r")}"`;
19978
19932
  }
19979
19933
  async function withDevVars(wranglerConfigPath, env, run) {
19980
- const path = join13(dirname2(wranglerConfigPath), ".dev.vars");
19934
+ const path = join14(dirname(wranglerConfigPath), ".dev.vars");
19981
19935
  const keys = Object.keys(env).filter(
19982
19936
  (key) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(key)
19983
19937
  );
@@ -19994,36 +19948,7 @@ async function withDevVars(wranglerConfigPath, env, run) {
19994
19948
  await rm(path, { force: true });
19995
19949
  }
19996
19950
  }
19997
- async function bundleWorker(context, layout, observer, errorCode2) {
19998
- return runStep(
19999
- context,
20000
- {
20001
- command: context.toolchain.wrangler,
20002
- args: [
20003
- "deploy",
20004
- "--dry-run",
20005
- "--config",
20006
- layout.wranglerConfigPath,
20007
- "--outdir",
20008
- layout.bundleDir
20009
- ],
20010
- cwd: layout.appRoot,
20011
- env: {
20012
- CI: "1",
20013
- WRANGLER_LOG_PATH: join13(
20014
- layout.appRoot,
20015
- layout.outputRoot,
20016
- "wrangler.log"
20017
- )
20018
- },
20019
- timeoutMs: 5 * 6e4,
20020
- stage: "BUILDING",
20021
- errorCode: errorCode2
20022
- },
20023
- observer
20024
- );
20025
- }
20026
- async function smokeWorker(context, layout, observer) {
19951
+ async function smokeWorker(context, layout, observer, extraProbes = []) {
20027
19952
  return withDevVars(
20028
19953
  layout.wranglerConfigPath,
20029
19954
  context.env,
@@ -20043,19 +19968,22 @@ async function smokeWorker(context, layout, observer) {
20043
19968
  "--show-interactive-dev-session=false",
20044
19969
  "--log-level=warn",
20045
19970
  "--persist-to",
20046
- join13(layout.appRoot, layout.outputRoot, ".naro-smoke-state")
19971
+ join14(layout.appRoot, layout.outputRoot, ".naro-smoke-state")
20047
19972
  ],
20048
19973
  cwd: layout.appRoot,
20049
19974
  env: {
20050
19975
  CI: "1",
20051
- WRANGLER_LOG_PATH: join13(
19976
+ WRANGLER_LOG_PATH: join14(
20052
19977
  layout.appRoot,
20053
19978
  layout.outputRoot,
20054
19979
  "wrangler-dev.log"
20055
19980
  )
20056
19981
  }
20057
19982
  }),
20058
- probes: defaultSmokeProbes(context.facts.matcherRoutes),
19983
+ probes: [
19984
+ ...defaultSmokeProbes(context.facts.matcherRoutes),
19985
+ ...extraProbes
19986
+ ],
20059
19987
  stage: "BUILDING",
20060
19988
  errorCode: "BUILD_FAILED"
20061
19989
  },
@@ -20064,10 +19992,10 @@ async function smokeWorker(context, layout, observer) {
20064
19992
  );
20065
19993
  }
20066
19994
  async function approvePnpmBuilds(checkout, packages) {
20067
- const path = join13(checkout, "pnpm-workspace.yaml");
19995
+ const path = join14(checkout, "pnpm-workspace.yaml");
20068
19996
  let source = "";
20069
19997
  try {
20070
- source = await readFile11(path, "utf8");
19998
+ source = await readFile12(path, "utf8");
20071
19999
  } catch {
20072
20000
  }
20073
20001
  let next = source;
@@ -20114,412 +20042,191 @@ ${listEntries}
20114
20042
  }
20115
20043
  }
20116
20044
 
20117
- // ../../packages/build/src/strategy/strategies/opennext-aws.ts
20118
- var CONFIG_NAME = ".next-deploy-open-next-aws.config.ts";
20119
- var SERVER_FUNCTION = ".open-next/server-functions/default/index.mjs";
20120
- function smokeConfig(buildCommand) {
20121
- return [
20122
- "const config = {",
20123
- ` buildCommand: ${JSON.stringify(buildCommand)},`,
20124
- " default: {",
20125
- ' override: { wrapper: "node", converter: "node", incrementalCache: "dummy", tagCache: "dummy", queue: "dummy" },',
20126
- " },",
20127
- ' imageOptimization: { loader: "dummy" },',
20128
- " dangerous: { disableIncrementalCache: true, disableTagCache: true },",
20129
- "};",
20130
- "export default config;",
20131
- ""
20132
- ].join("\n");
20045
+ // ../../packages/build/src/strategy/strategies/naro-adapter-cloudflare.ts
20046
+ var NARO_ADAPTER_REASONS = {
20047
+ notInstalled: "The Naro adapter is not installed in this build environment.",
20048
+ unknownVersion: "The installed Next.js version could not be read; the Naro adapter needs Next.js 16.2 or newer.",
20049
+ missingOutput: "next build finished without the Naro adapter's output; another Next.js adapter may have run instead.",
20050
+ userAdapterRan: (key) => `next build ran the project's own Next.js adapter (${key} in the Next.js build output) instead of the Naro adapter. Naro replaces an adapter only when next.config sets adapterPath itself; this one is set somewhere Naro does not read before the build, such as an imported file.`
20051
+ };
20052
+ var REPLACE_TAIL = "Your repository is unchanged, and your adapter's modifyConfig and onBuildComplete do not run. To keep your adapter (vinext only), change the project's Next.js adapter setting.";
20053
+ function replaceWarning(adapter) {
20054
+ return adapter.source === "next-config" ? `${adapter.file} sets its own Next.js adapter (${adapter.key}); this build replaces it with the Naro adapter in a temporary copy. ${REPLACE_TAIL}` : `NEXT_ADAPTER_PATH sets its own Next.js adapter; this build replaces it with the Naro adapter. ${REPLACE_TAIL}`;
20055
+ }
20056
+ async function readRecord(appRoot) {
20057
+ try {
20058
+ const parsed = JSON.parse(
20059
+ await readFile13(join15(appRoot, BUILD_RECORD), "utf8")
20060
+ );
20061
+ if (typeof parsed.buildId !== "string" || typeof parsed.distDir !== "string")
20062
+ return null;
20063
+ const staticFiles = Array.isArray(parsed.outputs?.staticFiles) ? parsed.outputs.staticFiles : [];
20064
+ return {
20065
+ buildId: parsed.buildId,
20066
+ distDir: parsed.distDir,
20067
+ basePath: typeof parsed.config?.basePath === "string" ? parsed.config.basePath : "",
20068
+ staticPathnames: staticFiles.flatMap((file2) => {
20069
+ const pathname = file2?.pathname;
20070
+ return typeof pathname === "string" ? [pathname] : [];
20071
+ })
20072
+ };
20073
+ } catch {
20074
+ return null;
20075
+ }
20076
+ }
20077
+ function naroAdapterSmokeProbes(record3, assetNames) {
20078
+ const { basePath } = record3;
20079
+ const probes2 = [];
20080
+ const assetPrefix = `${basePath}/_next/static/`;
20081
+ const script = assetNames.find(
20082
+ (name) => `/${name}`.startsWith(assetPrefix) && name.endsWith(".js")
20083
+ );
20084
+ if (script)
20085
+ probes2.push({ path: `/${script}`, label: "static asset", expect: "found" });
20086
+ const notPages = new Set(
20087
+ ["/404", "/500", "/index"].map((path) => `${basePath}${path}`)
20088
+ );
20089
+ const page2 = record3.staticPathnames.find(
20090
+ (pathname) => !pathname.startsWith(assetPrefix) && !pathname.endsWith(".rsc") && !notPages.has(pathname)
20091
+ );
20092
+ if (page2) probes2.push({ path: page2, label: "static page", expect: "found" });
20093
+ return probes2;
20133
20094
  }
20134
- function createOpenNextAwsStrategy() {
20095
+ async function readBuildId(distDir) {
20096
+ try {
20097
+ return (await readFile13(join15(distDir, "BUILD_ID"), "utf8")).trim();
20098
+ } catch {
20099
+ return null;
20100
+ }
20101
+ }
20102
+ function createNaroAdapterCloudflareStrategy(options) {
20135
20103
  return {
20136
- id: "opennext-aws",
20137
- priority: 5,
20138
- target: "aws",
20104
+ id: "naro-adapter-cloudflare",
20105
+ priority: 2,
20106
+ target: "cloudflare",
20139
20107
  supports(context) {
20140
- if (!context.targets.aws)
20108
+ if (!context.toolchain.nextAdapter)
20109
+ return { supported: false, reason: NARO_ADAPTER_REASONS.notInstalled };
20110
+ const verdict = judgeNextVersion(context.nextVersion);
20111
+ if (!verdict)
20112
+ return {
20113
+ supported: false,
20114
+ reason: NARO_ADAPTER_REASONS.unknownVersion
20115
+ };
20116
+ if (verdict.kind === "too-old")
20141
20117
  return {
20142
20118
  supported: false,
20143
- reason: "No AWS deploy target is configured on this control plane (no provider, credentials, or routing exist for it)."
20119
+ reason: `The Naro adapter needs Next.js 16.2 or newer; this project runs Next.js ${context.nextVersion}.`
20144
20120
  };
20145
- if (!context.toolchain.openNextAws)
20121
+ const adapter = userAdapterOf(context);
20122
+ if (adapter && context.userAdapter === "keep")
20146
20123
  return {
20147
20124
  supported: false,
20148
- reason: "The @opennextjs/aws toolchain is not installed on this build runner."
20125
+ reason: `The project keeps its own Next.js adapter (${describeUserAdapter(adapter)}) by its Next.js adapter setting, so the Naro adapter is skipped.`
20149
20126
  };
20150
20127
  return { supported: true };
20151
20128
  },
20152
20129
  async evaluate(context, observer) {
20153
- await info(
20154
- observer,
20155
- "P5 opennext-aws: building with the OpenNext AWS adapter."
20156
- );
20130
+ const adapterPath = context.toolchain.nextAdapter;
20131
+ if (!adapterPath)
20132
+ return { status: "skipped", reason: NARO_ADAPTER_REASONS.notInstalled };
20133
+ if (judgeNextVersion(context.nextVersion)?.kind === "newer")
20134
+ await warn(
20135
+ observer,
20136
+ `Next.js ${context.nextVersion} is newer than the versions the Naro adapter is tested with (${testedRange()}); building anyway.`
20137
+ );
20157
20138
  let fork;
20158
20139
  try {
20159
- fork = await context.workspace.fork("opennext-aws");
20140
+ fork = await context.workspace.fork("naro-adapter");
20160
20141
  } catch (error51) {
20161
20142
  return failed("prepare", describeError(error51));
20162
20143
  }
20163
- const configPath = join14(fork.appRoot, CONFIG_NAME);
20164
- try {
20165
- await writeFile6(
20166
- configPath,
20167
- smokeConfig(
20168
- `${context.packageManager.name} run ${context.buildScript}`
20169
- ),
20170
- "utf8"
20171
- );
20172
- } catch (error51) {
20173
- return failed("prepare", describeError(error51));
20144
+ await rm2(join15(fork.appRoot, OUTPUT_DIR), {
20145
+ recursive: true,
20146
+ force: true
20147
+ });
20148
+ const userAdapter = userAdapterOf(context);
20149
+ if (userAdapter && userAdapter.source !== "build-output") {
20150
+ let replaced = true;
20151
+ if (userAdapter.source === "next-config") {
20152
+ try {
20153
+ replaced = await wrapNextConfig(fork.appRoot, userAdapter.file) !== null;
20154
+ } catch (error51) {
20155
+ return failed("prepare", describeError(error51));
20156
+ }
20157
+ }
20158
+ if (replaced) await warn(observer, replaceWarning(userAdapter));
20174
20159
  }
20175
20160
  const build = await runStep(
20176
20161
  context,
20177
20162
  {
20178
- command: context.toolchain.openNextAws ?? "open-next",
20179
- args: ["build", "--config-path", configPath],
20163
+ command: context.packageManager.name,
20164
+ args: ["run", context.buildScript],
20180
20165
  cwd: fork.appRoot,
20181
20166
  env: {
20182
20167
  ...context.env,
20183
- NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
20168
+ NEXT_ADAPTER_PATH: adapterPath,
20169
+ NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath,
20170
+ CI: "1"
20184
20171
  },
20185
- timeoutMs: 20 * 6e4,
20186
- stage: "BUILDING",
20187
- errorCode: "BUILD_FAILED"
20188
- },
20189
- observer
20190
- );
20191
- if (!build.ok) return failed("build", describeFailure(build));
20192
- const serverFunction = join14(fork.appRoot, SERVER_FUNCTION);
20193
- if (!await pathExists2(serverFunction))
20194
- return failed("bundle", `OpenNext AWS produced no ${SERVER_FUNCTION}.`);
20195
- const smoke = await context.ports.smoke.run(
20196
- {
20197
- boot: (port) => ({
20198
- command: process.execPath,
20199
- args: [serverFunction],
20200
- cwd: fork.appRoot,
20201
- env: { ...context.env, PORT: String(port), NODE_ENV: "production" }
20202
- }),
20203
- probes: defaultSmokeProbes(context.facts.matcherRoutes),
20172
+ timeoutMs: 15 * 6e4,
20204
20173
  stage: "BUILDING",
20205
20174
  errorCode: "BUILD_FAILED"
20206
20175
  },
20207
20176
  observer
20208
20177
  );
20209
- if (!smoke.ok) return failed("smoke", smoke.reason);
20210
- return {
20211
- status: "success",
20212
- artifact: {
20213
- kind: "opennext-aws",
20214
- outputRoot: join14(fork.appRoot, ".open-next"),
20215
- serverFunction
20216
- },
20217
- notes: [
20218
- "Verified with the Node wrapper; an AWS deploy needs a Lambda-wrapper build against provisioned resources."
20219
- ]
20220
- };
20221
- }
20222
- };
20223
- }
20224
-
20225
- // ../../packages/build/src/strategy/strategies/opennext-cloudflare.ts
20226
- import { readFile as readFile12 } from "fs/promises";
20227
- import { join as join15 } from "path";
20228
- var PROXY_TO_MIDDLEWARE_VARIANT = "proxy-to-middleware";
20229
- var NODE_MIDDLEWARE_REFUSAL = /Node\.js middleware is not (?:currently )?supported/i;
20230
- var NODE_MIDDLEWARE_EXPERIMENTAL = /Node\.js middleware support is experimental/i;
20231
- var NODE_MIDDLEWARE_NOT_DEPLOYED = 'Node.js-runtime middleware (runtime: "nodejs") is only an experiment in OpenNext Cloudflare, and Naro does not deploy it. Remove runtime: "nodejs" to build it as Edge middleware';
20232
- async function buildDeclaresNodeMiddleware(appRoot) {
20233
- try {
20234
- const manifest2 = JSON.parse(
20235
- await readFile12(
20236
- join15(appRoot, ".next", "server", "functions-config-manifest.json"),
20237
- "utf8"
20238
- )
20239
- );
20240
- return Boolean(manifest2.functions?.["/_middleware"]);
20241
- } catch {
20242
- return false;
20243
- }
20244
- }
20245
- function createOpenNextCloudflareStrategy(options) {
20246
- async function attempt(context, roots, observer, ownBuild, adapterNotice) {
20247
- let platform;
20248
- try {
20249
- platform = await options.createPlatformBuildConfig(
20250
- roots.appRoot,
20251
- context.identity.deploymentId
20252
- );
20253
- } catch (error51) {
20254
- return {
20255
- ok: false,
20256
- nodeMiddleware: null,
20257
- outcome: failed("prepare", describeError(error51))
20258
- };
20259
- }
20260
- const nextBuild = await runStep(
20261
- context,
20262
- {
20263
- command: context.packageManager.name,
20264
- args: ["run", context.buildScript],
20265
- cwd: roots.appRoot,
20266
- env: {
20267
- ...context.env,
20268
- NEXT_PRIVATE_STANDALONE: "true",
20269
- NPM_CONFIG_USERCONFIG: platform.packageManagerConfigPath
20270
- },
20271
- timeoutMs: 15 * 6e4,
20272
- stage: "BUILDING",
20273
- errorCode: "BUILD_FAILED"
20274
- },
20275
- observer
20276
- );
20277
- if (!nextBuild.ok) {
20278
- return {
20279
- ok: false,
20280
- nodeMiddleware: null,
20281
- outcome: ownBuild ? appBuildFailed(describeFailure(nextBuild)) : failed("build", describeFailure(nextBuild))
20282
- };
20283
- }
20284
- if (!adapterNotice.warned) {
20285
- const adapter = userAdapterOf(context) ?? await adapterInBuildOutput(roots.appRoot, context.nextVersion);
20286
- if (adapter) {
20287
- adapterNotice.warned = true;
20288
- await warn(observer, openNextAdapterWarning(adapter));
20289
- }
20290
- }
20291
- if (await buildDeclaresNodeMiddleware(roots.appRoot)) {
20292
- return {
20293
- ok: false,
20294
- nodeMiddleware: "experimental",
20295
- outcome: failed("build", NODE_MIDDLEWARE_NOT_DEPLOYED)
20296
- };
20297
- }
20298
- const openNext = await runStep(
20299
- context,
20300
- {
20301
- command: context.toolchain.openNextCloudflare,
20302
- args: [
20303
- "build",
20304
- "--skip-next-build",
20305
- "--openNextConfigPath",
20306
- platform.openNextConfigPath,
20307
- "--config",
20308
- platform.wranglerConfigPath,
20309
- ...context.nextSupport.status === "needs-unsupported-flag" ? ["--dangerouslyUseUnsupportedNextVersion"] : []
20310
- ],
20311
- cwd: roots.appRoot,
20312
- timeoutMs: 15 * 6e4,
20313
- stage: "BUILDING",
20314
- errorCode: "OPENNEXT_FAILED"
20315
- },
20316
- observer
20317
- );
20318
- if (!openNext.ok) {
20319
- const refused = openNext.output.some(
20320
- (line) => NODE_MIDDLEWARE_REFUSAL.test(line)
20321
- );
20322
- return {
20323
- ok: false,
20324
- nodeMiddleware: refused ? "refused" : null,
20325
- outcome: failed(
20326
- "build",
20327
- refused ? NODE_MIDDLEWARE_NOT_DEPLOYED : describeFailure(openNext)
20328
- )
20329
- };
20330
- }
20331
- if (openNext.output.some((line) => NODE_MIDDLEWARE_EXPERIMENTAL.test(line))) {
20332
- return {
20333
- ok: false,
20334
- nodeMiddleware: "experimental",
20335
- outcome: failed("build", NODE_MIDDLEWARE_NOT_DEPLOYED)
20336
- };
20337
- }
20338
- const layout = {
20339
- appRoot: roots.appRoot,
20340
- wranglerConfigPath: platform.wranglerConfigPath,
20341
- ...OPEN_NEXT_LAYOUT
20342
- };
20343
- const bundle = await bundleWorker(
20344
- context,
20345
- layout,
20346
- observer,
20347
- "OPENNEXT_FAILED"
20348
- );
20349
- if (!bundle.ok) {
20350
- return {
20351
- ok: false,
20352
- nodeMiddleware: null,
20353
- outcome: failed("bundle", describeFailure(bundle))
20354
- };
20355
- }
20356
- let artifact;
20357
- try {
20358
- artifact = await options.readOpenNextArtifact(
20359
- roots.appRoot,
20360
- context.identity,
20361
- platform.wranglerConfigPath
20362
- );
20363
- } catch (error51) {
20364
- return {
20365
- ok: false,
20366
- nodeMiddleware: null,
20367
- outcome: failed("bundle", describeError(error51))
20368
- };
20369
- }
20370
- const smoke = await smokeWorker(context, layout, observer);
20371
- if (!smoke.ok) {
20372
- return {
20373
- ok: false,
20374
- nodeMiddleware: null,
20375
- outcome: failed("smoke", smoke.reason)
20376
- };
20377
- }
20378
- return { ok: true, artifact };
20379
- }
20380
- return {
20381
- id: "opennext-cloudflare",
20382
- priority: 2,
20383
- target: "cloudflare",
20384
- supports: () => ({ supported: true }),
20385
- async evaluate(context, observer) {
20386
- const adapterNotice = { warned: false };
20387
- const rememberedRewrite = context.pinnedVariant === PROXY_TO_MIDDLEWARE_VARIANT && context.facts.proxyFile !== null;
20388
- if (rememberedRewrite) {
20389
- await info(
20390
- observer,
20391
- `P2 opennext-cloudflare: the stored strategy already needed ${context.facts.proxyFile ?? "proxy"} rewritten to Edge middleware; building that way directly.`
20392
- );
20393
- } else {
20394
- await info(
20395
- observer,
20396
- "P2 opennext-cloudflare: building the project as written."
20397
- );
20398
- const first = await attempt(
20399
- context,
20400
- context.workspace,
20401
- observer,
20402
- true,
20403
- adapterNotice
20404
- );
20405
- if (first.ok) {
20178
+ if (!build.ok) {
20179
+ if (!await pathExists(join15(fork.appRoot, BUILD_RECORD)))
20406
20180
  return {
20407
- status: "success",
20408
- artifact: { kind: "cloudflare-worker", artifact: first.artifact },
20409
- notes: []
20181
+ status: "failed",
20182
+ stage: "build",
20183
+ reason: describeFailure(build),
20184
+ appBuild: true
20410
20185
  };
20411
- }
20412
- if (!first.nodeMiddleware || !context.facts.proxyFile)
20413
- return first.outcome;
20414
- await warn(
20415
- observer,
20416
- first.nodeMiddleware === "experimental" ? `P2 opennext-cloudflare: OpenNext supports the Node.js middleware compiled from ${context.facts.proxyFile} only as an experiment it does not maintain. Retrying in a temporary workspace with it rewritten to Edge middleware.` : `P2 opennext-cloudflare: OpenNext refused the Node.js middleware compiled from ${context.facts.proxyFile}. Retrying in a temporary workspace with it rewritten to Edge middleware.`
20186
+ return failed("bundle", describeFailure(build));
20187
+ }
20188
+ const record3 = await readRecord(fork.appRoot);
20189
+ if (!record3 || await readBuildId(record3.distDir) !== record3.buildId) {
20190
+ const ran = await adapterInBuildOutput(
20191
+ fork.appRoot,
20192
+ context.nextVersion
20193
+ );
20194
+ return failed(
20195
+ "build",
20196
+ ran?.source === "build-output" && resolve5(fork.appRoot, ran.path) !== resolve5(adapterPath) ? NARO_ADAPTER_REASONS.userAdapterRan(ran.key) : NARO_ADAPTER_REASONS.missingOutput
20417
20197
  );
20418
20198
  }
20419
- let fork;
20199
+ let artifact;
20420
20200
  try {
20421
- fork = await context.workspace.fork("opennext-proxy-to-middleware");
20201
+ artifact = await options.readArtifact(fork.appRoot, context.identity);
20422
20202
  } catch (error51) {
20423
- return failed("prepare", describeError(error51));
20424
- }
20425
- const conversion = await options.convertProxyToMiddleware(fork.appRoot);
20426
- if (!conversion.converted) {
20427
- return failed(
20428
- "prepare",
20429
- `Could not rewrite ${context.facts.proxyFile} to middleware (a middleware file already exists).`
20430
- );
20203
+ return failed("bundle", describeError(error51));
20431
20204
  }
20432
- await warn(
20433
- observer,
20434
- `Converted ${conversion.file ?? context.facts.proxyFile} to edge middleware for this build only. Next.js 16 proxy runs on the Node.js runtime, which Cloudflare does not support; middleware runs on the Edge runtime, so Node.js-only APIs are unavailable. Your repository is unchanged.`
20435
- );
20436
- const second = await attempt(
20205
+ const smoke = await smokeWorker(
20437
20206
  context,
20438
- fork,
20207
+ {
20208
+ appRoot: fork.appRoot,
20209
+ wranglerConfigPath: join15(fork.appRoot, WRANGLER_CONFIG),
20210
+ outputRoot: OUTPUT_DIR
20211
+ },
20439
20212
  observer,
20440
- rememberedRewrite,
20441
- adapterNotice
20213
+ naroAdapterSmokeProbes(
20214
+ record3,
20215
+ artifact.assets.map((asset) => asset.name)
20216
+ )
20442
20217
  );
20443
- if (!second.ok) {
20444
- if (!rememberedRewrite || second.outcome.status !== "failed" || !second.outcome.appBuild)
20445
- return second.outcome;
20446
- await info(
20447
- observer,
20448
- `P2 opennext-cloudflare: the rewritten copy did not build; building the project as written once to tell the app's own error from the rewrite's.`
20449
- );
20450
- const asWritten = await attempt(
20451
- context,
20452
- context.workspace,
20453
- observer,
20454
- true,
20455
- adapterNotice
20456
- );
20457
- if (asWritten.ok) {
20458
- await info(
20459
- observer,
20460
- "P2 opennext-cloudflare: the project as written builds and ships as it is; the rewrite is no longer needed."
20461
- );
20462
- return {
20463
- status: "success",
20464
- artifact: {
20465
- kind: "cloudflare-worker",
20466
- artifact: asWritten.artifact
20467
- },
20468
- notes: []
20469
- };
20470
- }
20471
- if (asWritten.outcome.status === "failed" && asWritten.outcome.appBuild) {
20472
- await warn(
20473
- observer,
20474
- "P2 opennext-cloudflare: the project as written does not build either, so the error is the app's own."
20475
- );
20476
- return asWritten.outcome;
20477
- }
20478
- await warn(
20479
- observer,
20480
- "P2 opennext-cloudflare: the project as written builds, so the rewritten copy's error is the Edge rewrite's (a Node-only API reached from proxy.ts), not the app's code."
20481
- );
20482
- return failed("build", second.outcome.reason);
20483
- }
20218
+ if (!smoke.ok) return failed("smoke", smoke.reason);
20484
20219
  return {
20485
20220
  status: "success",
20486
- artifact: { kind: "cloudflare-worker", artifact: second.artifact },
20487
- variant: PROXY_TO_MIDDLEWARE_VARIANT,
20221
+ artifact: { kind: "cloudflare-worker", artifact },
20488
20222
  notes: [
20489
- `${conversion.file ?? "proxy"} was rewritten to Edge middleware for the build.`
20223
+ "Built with the Naro adapter (Next.js Adapter API) in a temporary workspace."
20490
20224
  ]
20491
20225
  };
20492
20226
  }
20493
20227
  };
20494
20228
  }
20495
20229
 
20496
- // ../../packages/build/src/strategy/strategies/unavailable.ts
20497
- function createUnavailableStrategy(options) {
20498
- return {
20499
- id: options.id,
20500
- priority: options.priority,
20501
- target: options.target,
20502
- supports: () => ({ supported: false, reason: options.reason }),
20503
- evaluate: async () => ({ status: "skipped", reason: options.reason })
20504
- };
20505
- }
20506
- function createSplitCloudflareStrategy() {
20507
- return createUnavailableStrategy({
20508
- id: "split-cloudflare",
20509
- priority: 3,
20510
- target: "cloudflare-split",
20511
- reason: "Split Cloudflare deployment needs the dispatcher to chain a middleware Worker in front of the app Worker; the runtime plane routes one Worker per hostname today."
20512
- });
20513
- }
20514
- function createAwsMiddlewareCloudflareAppStrategy() {
20515
- return createUnavailableStrategy({
20516
- id: "aws-middleware-cloudflare-app",
20517
- priority: 4,
20518
- target: "aws-hybrid",
20519
- reason: "Hybrid AWS middleware + Cloudflare app needs an AWS deploy target and cross-cloud request chaining; neither exists on this control plane."
20520
- });
20521
- }
20522
-
20523
20230
  // ../../packages/build/src/strategy/strategies/vinext-cloudflare.ts
20524
20231
  var VINEXT_MIN_NEXT_MAJOR = 16;
20525
20232
  var VINEXT_BUILD_SCRIPTS = ["esbuild", "workerd"];
@@ -20594,6 +20301,7 @@ function createVinextCloudflareStrategy(options) {
20594
20301
  observer,
20595
20302
  "P1 vinext-cloudflare: the project already builds with vinext; using its own configuration."
20596
20303
  );
20304
+ await info(observer, ISR_SHARED_CACHE_OFF_NOTE);
20597
20305
  }
20598
20306
  let fork;
20599
20307
  try {
@@ -20691,13 +20399,13 @@ function createVinextCloudflareStrategy(options) {
20691
20399
 
20692
20400
  // ../../packages/build/src/strategy/workspace.ts
20693
20401
  import { execFile } from "child_process";
20694
- import { cp, mkdir as mkdir2, readdir as readdir4, rm as rm2 } from "fs/promises";
20695
- import { basename as basename2, dirname as dirname3, join as join16, relative as relative5, resolve as resolve6 } from "path";
20402
+ import { cp, mkdir as mkdir2, readdir as readdir4, rm as rm3 } from "fs/promises";
20403
+ import { basename, dirname as dirname2, join as join16, relative as relative5, resolve as resolve6 } from "path";
20696
20404
  import { promisify } from "util";
20697
20405
  var execFileAsync = promisify(execFile);
20698
20406
  var SKIPPED_ANYWHERE = /* @__PURE__ */ new Set(["node_modules"]);
20699
20407
  function shouldCopy(path) {
20700
- const name = basename2(path);
20408
+ const name = basename(path);
20701
20409
  if (SKIPPED_ANYWHERE.has(name)) return false;
20702
20410
  return !name.startsWith(".next-deploy-");
20703
20411
  }
@@ -20725,13 +20433,13 @@ async function copyInstalledPackages(source, target2) {
20725
20433
  await copyTree(install, join16(target2, relative5(origin, install)));
20726
20434
  }
20727
20435
  async function copyTree(from, to) {
20728
- await mkdir2(dirname3(to), { recursive: true });
20436
+ await mkdir2(dirname2(to), { recursive: true });
20729
20437
  if (process.platform !== "win32") {
20730
20438
  try {
20731
20439
  await execFileAsync("cp", ["-a", from, to]);
20732
20440
  return;
20733
20441
  } catch {
20734
- await rm2(to, { recursive: true, force: true });
20442
+ await rm3(to, { recursive: true, force: true });
20735
20443
  }
20736
20444
  }
20737
20445
  await cp(from, to, { recursive: true, verbatimSymlinks: true });
@@ -20759,27 +20467,26 @@ async function withRouteList(artifact, list, appRoot, observer) {
20759
20467
  return artifact;
20760
20468
  }
20761
20469
  }
20470
+ var NEXT_ADAPTER_PATH = nextAdapterPath();
20762
20471
  var defaultLocalBuildDependencies = {
20763
20472
  createWorkspace: () => mkdtemp(join17(tmpdir(), "next-deploy-build-")),
20764
- removeWorkspace: (path) => rm3(path, { recursive: true, force: true }),
20473
+ removeWorkspace: (path) => rm4(path, { recursive: true, force: true }),
20765
20474
  runCommand,
20766
20475
  detectPackageManager,
20767
20476
  resolveAppRoot,
20768
20477
  detectFramework,
20769
20478
  readStaticArtifact,
20770
20479
  createPackageManagerConfig,
20771
- createPlatformBuildConfig,
20772
- readOpenNextArtifact,
20773
- openNextCommand: "opennextjs-cloudflare",
20774
20480
  wranglerCommand: "wrangler",
20775
20481
  cacheFetch: (input, init) => fetch(input, init),
20776
- convertProxyToMiddleware,
20777
20482
  commandAvailable: hasCommand,
20778
20483
  readInstalledNextVersion,
20779
20484
  now: () => /* @__PURE__ */ new Date(),
20780
20485
  createVinextBuildConfig,
20781
20486
  readVinextArtifact,
20782
20487
  vinextCommand: "vinext",
20488
+ ...NEXT_ADAPTER_PATH ? { nextAdapterPath: NEXT_ADAPTER_PATH } : {},
20489
+ readNaroAdapterArtifact,
20783
20490
  forkWorkspace: copyCheckout,
20784
20491
  copyInstalledPackages,
20785
20492
  collectProjectFacts,
@@ -20794,7 +20501,7 @@ async function readInstalledNextVersion(appRoot, checkout) {
20794
20501
  const stop = resolve7(checkout);
20795
20502
  for (; ; ) {
20796
20503
  try {
20797
- const source = await readFile13(
20504
+ const source = await readFile14(
20798
20505
  join17(dir, "node_modules", "next", "package.json"),
20799
20506
  "utf8"
20800
20507
  );
@@ -20803,7 +20510,7 @@ async function readInstalledNextVersion(appRoot, checkout) {
20803
20510
  } catch {
20804
20511
  }
20805
20512
  if (dir === stop) return null;
20806
- const parent = dirname4(dir);
20513
+ const parent = dirname3(dir);
20807
20514
  if (parent === dir) return null;
20808
20515
  dir = parent;
20809
20516
  }
@@ -20826,9 +20533,9 @@ async function hasCommand(command) {
20826
20533
  return false;
20827
20534
  }
20828
20535
  }
20829
- async function pathExists3(path) {
20536
+ async function pathExists2(path) {
20830
20537
  try {
20831
- await access8(path);
20538
+ await access6(path);
20832
20539
  return true;
20833
20540
  } catch {
20834
20541
  return false;
@@ -20872,14 +20579,11 @@ function defaultStrategies(dependencies) {
20872
20579
  createVinextBuildConfig: dependencies.createVinextBuildConfig ?? createVinextBuildConfig,
20873
20580
  readVinextArtifact: dependencies.readVinextArtifact ?? readVinextArtifact
20874
20581
  }),
20875
- createOpenNextCloudflareStrategy({
20876
- createPlatformBuildConfig: dependencies.createPlatformBuildConfig,
20877
- convertProxyToMiddleware: dependencies.convertProxyToMiddleware ?? convertProxyToMiddleware,
20878
- readOpenNextArtifact: dependencies.readOpenNextArtifact
20582
+ createNaroAdapterCloudflareStrategy({
20583
+ readArtifact: dependencies.readNaroAdapterArtifact ?? readNaroAdapterArtifact
20879
20584
  }),
20880
20585
  createSplitCloudflareStrategy(),
20881
- createAwsMiddlewareCloudflareAppStrategy(),
20882
- createOpenNextAwsStrategy()
20586
+ createAwsMiddlewareCloudflareAppStrategy()
20883
20587
  ];
20884
20588
  }
20885
20589
  var LocalBuildExecutor = class {
@@ -21158,7 +20862,7 @@ var LocalBuildExecutor = class {
21158
20862
  "NEXT_VERSION_UNSUPPORTED",
21159
20863
  nextSupport.reason
21160
20864
  );
21161
- if (nextSupport.status === "needs-unsupported-flag" || nextSupport.status === "outside-peer-range") {
20865
+ if (nextSupport.status === "past-support-window" || nextSupport.status === "outside-verified-range") {
21162
20866
  await observer.onLog({
21163
20867
  level: "WARN",
21164
20868
  stage: "BUILDING",
@@ -21213,7 +20917,6 @@ var LocalBuildExecutor = class {
21213
20917
  };
21214
20918
  };
21215
20919
  const vinextCommand = this.dependencies.vinextCommand;
21216
- const openNextAwsCommand = this.dependencies.openNextAwsCommand;
21217
20920
  const context = {
21218
20921
  identity,
21219
20922
  branch: input.branch,
@@ -21225,15 +20928,14 @@ var LocalBuildExecutor = class {
21225
20928
  buildScript: resolved.buildScript,
21226
20929
  nextVersion: nextVersion ? `${String(nextVersion.major)}.${String(nextVersion.minor)}.${String(nextVersion.patch)}` : "unknown",
21227
20930
  nextMajor,
21228
- nextSupport,
21229
20931
  vinextEnabled: input.vinextEnabled !== false,
20932
+ userAdapter: input.userAdapter === "keep" ? "keep" : "replace",
21230
20933
  facts,
21231
20934
  workspace: { checkout, appRoot, fork: forkWorkspace },
21232
20935
  toolchain: {
21233
- openNextCloudflare: this.dependencies.openNextCommand,
21234
20936
  wrangler: this.dependencies.wranglerCommand,
21235
20937
  ...vinextCommand && await commandAvailable(vinextCommand) ? { vinext: vinextCommand } : {},
21236
- ...openNextAwsCommand && await commandAvailable(openNextAwsCommand) ? { openNextAws: openNextAwsCommand } : {}
20938
+ ...this.dependencies.nextAdapterPath && await pathExists2(this.dependencies.nextAdapterPath) ? { nextAdapter: this.dependencies.nextAdapterPath } : {}
21237
20939
  },
21238
20940
  targets: this.dependencies.targets ?? { cloudflare: true },
21239
20941
  ports: {
@@ -21288,7 +20990,7 @@ var LocalBuildExecutor = class {
21288
20990
  if (depsKey && !depsRestored) {
21289
20991
  await trySave("deps", depsKey, pmCacheDir, cachePorts, observer);
21290
20992
  }
21291
- if (buildCacheDir && await pathExists3(buildCacheDir)) {
20993
+ if (buildCacheDir && await pathExists2(buildCacheDir)) {
21292
20994
  await trySave(
21293
20995
  "next",
21294
20996
  buildCacheKey,
@@ -21688,8 +21390,8 @@ function sendPage(res, status, html) {
21688
21390
  async function startCallbackServer() {
21689
21391
  let resolveCode;
21690
21392
  let rejectCode;
21691
- const codePromise = new Promise((resolve15, reject) => {
21692
- resolveCode = resolve15;
21393
+ const codePromise = new Promise((resolve16, reject) => {
21394
+ resolveCode = resolve16;
21693
21395
  rejectCode = reject;
21694
21396
  });
21695
21397
  const server = createServer2((req, res) => {
@@ -21718,7 +21420,7 @@ async function startCallbackServer() {
21718
21420
  sendPage(res, 200, successPage());
21719
21421
  resolveCode(code);
21720
21422
  });
21721
- await new Promise((resolve15) => server.listen(0, "127.0.0.1", resolve15));
21423
+ await new Promise((resolve16) => server.listen(0, "127.0.0.1", resolve16));
21722
21424
  const port = server.address().port;
21723
21425
  return {
21724
21426
  port,
@@ -22136,32 +21838,6 @@ var ObservabilityApi = class {
22136
21838
  }
22137
21839
  };
22138
21840
 
22139
- // ../../packages/sdk/src/supabase-links.ts
22140
- var SupabaseApi = class {
22141
- constructor(client) {
22142
- this.client = client;
22143
- }
22144
- async links(projectId) {
22145
- const body = await this.client.request(
22146
- `/api/projects/${encodeURIComponent(projectId)}/supabase`,
22147
- { context: "Read Supabase links" }
22148
- );
22149
- return body.links;
22150
- }
22151
- /**
22152
- * Removes the link of one target (production, preview or an environment's
22153
- * slug), or every link the role may write when `target` is left out, with
22154
- * the env rows they hold. Answers how many went.
22155
- */
22156
- async disconnect(projectId, options = {}) {
22157
- const query = options.target === void 0 ? "" : `?target=${encodeURIComponent(options.target)}`;
22158
- return this.client.request(
22159
- `/api/projects/${encodeURIComponent(projectId)}/supabase${query}`,
22160
- { method: "DELETE", context: "Disconnect Supabase" }
22161
- );
22162
- }
22163
- };
22164
-
22165
21841
  // ../../packages/sdk/src/environments.ts
22166
21842
  function isReservedEnvTarget(value) {
22167
21843
  return RESERVED_ENVIRONMENT_SLUGS.includes(value);
@@ -22242,7 +21918,8 @@ function projectInfo(row) {
22242
21918
  repositoryUrl: row.repository_url,
22243
21919
  repositoryFullName: row.repository_full_name ?? null,
22244
21920
  createdAt: row.created_at,
22245
- updatedAt: row.updated_at
21921
+ updatedAt: row.updated_at,
21922
+ userAdapter: row.user_adapter ?? null
22246
21923
  };
22247
21924
  }
22248
21925
  function deploymentInfo(row) {
@@ -22301,7 +21978,6 @@ var NaroClient = class _NaroClient {
22301
21978
  flags;
22302
21979
  billing;
22303
21980
  observability;
22304
- supabase;
22305
21981
  logs;
22306
21982
  db;
22307
21983
  backend;
@@ -22330,7 +22006,6 @@ var NaroClient = class _NaroClient {
22330
22006
  this.flags = new FlagsApi(this);
22331
22007
  this.billing = new BillingApi(this);
22332
22008
  this.observability = new ObservabilityApi(this);
22333
- this.supabase = new SupabaseApi(this);
22334
22009
  this.logs = new LogsApi(this);
22335
22010
  this.db = new DbApi(this);
22336
22011
  this.backend = new BackendApi(this);
@@ -22415,6 +22090,19 @@ var TeamsApi = class {
22415
22090
  { context: "Read team usage" }
22416
22091
  );
22417
22092
  }
22093
+ /**
22094
+ * The team's hosting compute (CPU) per project and environment for a UTC
22095
+ * month (spec 2026-11-06-hosting-cpu-usage §7). Observed, never billed.
22096
+ */
22097
+ async usageHosting(teamSlug, query = {}) {
22098
+ const params = new URLSearchParams();
22099
+ if (query.month !== void 0) params.set("month", query.month);
22100
+ const text3 = params.toString();
22101
+ return this.client.request(
22102
+ `/api/teams/${encodeURIComponent(teamSlug)}/usage/hosting${text3 ? `?${text3}` : ""}`,
22103
+ { context: "Read team hosting usage" }
22104
+ );
22105
+ }
22418
22106
  path(teamSlug, suffix = "") {
22419
22107
  return `/api/teams/${encodeURIComponent(teamSlug)}${suffix}`;
22420
22108
  }
@@ -22790,7 +22478,7 @@ var DeploymentsApi = class {
22790
22478
  async wait(deploymentId, options = {}) {
22791
22479
  const intervalMs = options.intervalMs ?? 2500;
22792
22480
  const timeoutMs = options.timeoutMs ?? 15 * 6e4;
22793
- const sleep2 = options.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
22481
+ const sleep2 = options.sleep ?? ((ms) => new Promise((resolve16) => setTimeout(resolve16, ms)));
22794
22482
  const startedAt = Date.now();
22795
22483
  let lastStatus = null;
22796
22484
  let lastSequence = -1;
@@ -23391,7 +23079,7 @@ var DbApi = class {
23391
23079
  */
23392
23080
  async export(projectId, options = {}, wait = {}) {
23393
23081
  const timeoutMs = wait.timeoutMs ?? EXPORT_TIMEOUT_MS;
23394
- const sleep2 = wait.sleep ?? ((ms) => new Promise((resolve15) => setTimeout(resolve15, ms)));
23082
+ const sleep2 = wait.sleep ?? ((ms) => new Promise((resolve16) => setTimeout(resolve16, ms)));
23395
23083
  const given = wait.pollIntervalMs;
23396
23084
  const firstPollMs = given !== void 0 && Number.isFinite(given) && given > 0 ? given : EXPORT_FIRST_POLL_MS;
23397
23085
  const maxPollMs = Math.max(EXPORT_MAX_POLL_MS, firstPollMs);
@@ -26854,8 +26542,8 @@ function readAuth(state, start, allowBareJwt) {
26854
26542
  stream.expectOperator("(");
26855
26543
  stream.expectOperator(")");
26856
26544
  if (fn !== "jwt") return { kind: "auth", fn };
26857
- const access9 = readJwtAccess(stream);
26858
- if (access9) return access9;
26545
+ const access7 = readJwtAccess(stream);
26546
+ if (access7) return access7;
26859
26547
  if (allowBareJwt) return "jwt";
26860
26548
  throw new PolicySyntaxError(
26861
26549
  "auth.jwt() is read one claim at a time: auth.jwt() ->> 'email'",
@@ -26923,13 +26611,13 @@ function readPrimary(state) {
26923
26611
  const inner2 = readAuth(state, auth, true);
26924
26612
  stream.expectOperator(")");
26925
26613
  if (inner2 !== "jwt") return inner2;
26926
- const access9 = readJwtAccess(stream);
26927
- if (!access9)
26614
+ const access7 = readJwtAccess(stream);
26615
+ if (!access7)
26928
26616
  throw new PolicySyntaxError(
26929
26617
  "auth.jwt() is read one claim at a time: (select auth.jwt()) ->> 'email'",
26930
26618
  auth.position
26931
26619
  );
26932
- return access9;
26620
+ return access7;
26933
26621
  }
26934
26622
  const inner = readOr(state);
26935
26623
  stream.expectOperator(")");
@@ -34695,10 +34383,10 @@ var SUPPORTED_REMOTE = [
34695
34383
  /^ssh:\/\/[^\s]+$/,
34696
34384
  /^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
34697
34385
  ];
34698
- var defaultExec = (args, cwd) => new Promise((resolve15, reject) => {
34386
+ var defaultExec = (args, cwd) => new Promise((resolve16, reject) => {
34699
34387
  execFile2("git", args, { cwd }, (error51, stdout) => {
34700
34388
  if (error51) reject(error51);
34701
- else resolve15(stdout);
34389
+ else resolve16(stdout);
34702
34390
  });
34703
34391
  });
34704
34392
  async function tryExec(exec, args, cwd) {
@@ -34847,15 +34535,52 @@ async function uploadArtifact(artifact, opts) {
34847
34535
  };
34848
34536
  }
34849
34537
 
34538
+ // src/user-adapter.ts
34539
+ import { resolve as resolve10 } from "path";
34540
+ async function detectUserAdapter(directory, rootDirectory) {
34541
+ return findAdapterConfig(
34542
+ rootDirectory ? resolve10(directory, rootDirectory) : directory
34543
+ );
34544
+ }
34545
+ function parseUserAdapter(value) {
34546
+ return value === "replace" || value === "keep" ? value : null;
34547
+ }
34548
+ var USER_ADAPTER_OPTIONS = [
34549
+ {
34550
+ value: "replace",
34551
+ label: "Replace it with the Naro adapter on Naro's builders (recommended) \u2014 your repository stays as it is"
34552
+ },
34553
+ { value: "keep", label: "Keep my adapter \u2014 Naro builds with vinext only" }
34554
+ ];
34555
+ function userAdapterQuestion(adapter) {
34556
+ return `${adapter.file} sets its own Next.js adapter (${adapter.key}). How should Naro build this project?`;
34557
+ }
34558
+ function userAdapterOutcome(choice) {
34559
+ return choice === "replace" ? "\u25B8 This local build uses vinext only; if vinext cannot build the app, build it on Naro with the Naro adapter: naro deployments create (needs a connected repository)." : "\u25B8 Naro keeps your adapter: builds use vinext only.";
34560
+ }
34561
+ function userAdapterStored(adapter, choice) {
34562
+ return choice === "replace" ? `\u25B8 ${adapter.file} sets its own Next.js adapter; this project replaces it with the Naro adapter on Naro's builders.` : `\u25B8 ${adapter.file} sets its own Next.js adapter; this project keeps it and builds with vinext only.`;
34563
+ }
34564
+ function userAdapterUnasked(adapter) {
34565
+ return `\u26A0 ${adapter.file} sets its own Next.js adapter (${adapter.key}). Naro's builders replace it with the Naro adapter unless you choose otherwise: naro deploy --user-adapter keep, or naro project set --user-adapter keep.`;
34566
+ }
34567
+ var CHOOSE_USER_ADAPTER = {
34568
+ cli: "naro project set --user-adapter replace|keep",
34569
+ mcp: "update_project with userAdapter replace|keep"
34570
+ };
34571
+ function userAdapterUnreadable(adapter, surface = "cli") {
34572
+ return `\u26A0 ${adapter.file} sets its own Next.js adapter (${adapter.key}), but the project's adapter setting could not be read, so nothing was asked. Choose with ${CHOOSE_USER_ADAPTER[surface]}.`;
34573
+ }
34574
+
34850
34575
  // src/version.ts
34851
34576
  import { readFileSync as readFileSync6 } from "fs";
34852
- import { dirname as dirname5, join as join24 } from "path";
34853
- import { fileURLToPath } from "url";
34577
+ import { dirname as dirname4, join as join24 } from "path";
34578
+ import { fileURLToPath as fileURLToPath2 } from "url";
34854
34579
  function version2() {
34855
34580
  try {
34856
34581
  const pkg = JSON.parse(
34857
34582
  readFileSync6(
34858
- join24(dirname5(fileURLToPath(import.meta.url)), "..", "package.json"),
34583
+ join24(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json"),
34859
34584
  "utf8"
34860
34585
  )
34861
34586
  );
@@ -34871,22 +34596,22 @@ import {
34871
34596
  cp as cp2,
34872
34597
  lstat,
34873
34598
  mkdir as mkdir3,
34874
- readFile as readFile14,
34599
+ readFile as readFile15,
34875
34600
  readlink,
34876
34601
  realpath as realpath3,
34877
- rm as rm4,
34602
+ rm as rm5,
34878
34603
  stat as stat3,
34879
34604
  symlink,
34880
34605
  unlink,
34881
- writeFile as writeFile7
34606
+ writeFile as writeFile6
34882
34607
  } from "fs/promises";
34883
34608
  import {
34884
- basename as basename3,
34885
- dirname as dirname6,
34609
+ basename as basename2,
34610
+ dirname as dirname5,
34886
34611
  isAbsolute as isAbsolute4,
34887
34612
  join as join25,
34888
34613
  relative as relative6,
34889
- resolve as resolve10,
34614
+ resolve as resolve11,
34890
34615
  sep as sep5
34891
34616
  } from "path";
34892
34617
  var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.turbo|\.naro)([\\/]|$)/;
@@ -34916,7 +34641,7 @@ async function committedEnvFiles(sourceDir) {
34916
34641
  if (tab < 0) return [];
34917
34642
  const [mode] = entry.slice(0, tab).split(" ");
34918
34643
  const path = entry.slice(tab + 1);
34919
- if (!ENV_FILE.test(basename3(path)) || COPY_SKIP.test(path)) return [];
34644
+ if (!ENV_FILE.test(basename2(path)) || COPY_SKIP.test(path)) return [];
34920
34645
  if (mode === "100644" || mode === "100755")
34921
34646
  return [{ path, symlink: false }];
34922
34647
  if (mode === "120000") return [{ path, symlink: true }];
@@ -34948,14 +34673,14 @@ async function keptOnly(items, keep, remove) {
34948
34673
  async function readLocal(path) {
34949
34674
  const found = await stat3(path).catch(() => null);
34950
34675
  if (!found?.isFile()) return null;
34951
- return readFile14(path).catch(() => null);
34676
+ return readFile15(path).catch(() => null);
34952
34677
  }
34953
34678
  async function realAncestor(dir) {
34954
- for (let current = dir; ; current = dirname6(current)) {
34679
+ for (let current = dir; ; current = dirname5(current)) {
34955
34680
  try {
34956
34681
  return await realpath3(current);
34957
34682
  } catch {
34958
- if (dirname6(current) === current)
34683
+ if (dirname5(current) === current)
34959
34684
  throw new Error(`No ancestor of ${dir}`);
34960
34685
  }
34961
34686
  }
@@ -34974,7 +34699,7 @@ async function copyWorkingTree(source, checkout) {
34974
34699
  recursive: true,
34975
34700
  // Relative symlinks must stay relative: resolved, they would point
34976
34701
  // back into the developer's real tree, and a strategy's temporary
34977
- // rewrite (proxy -> middleware, vinext toolchain) would land there.
34702
+ // rewrite (the vinext toolchain install) would land there.
34978
34703
  verbatimSymlinks: true,
34979
34704
  filter: async (src) => {
34980
34705
  if (COPY_SKIP.test(src)) return false;
@@ -34982,7 +34707,7 @@ async function copyWorkingTree(source, checkout) {
34982
34707
  const entry = await lstat(src);
34983
34708
  if (entry.isFIFO() || entry.isSocket() || entry.isCharacterDevice() || entry.isBlockDevice())
34984
34709
  return false;
34985
- if (ENV_FILE.test(basename3(src)) && !entry.isDirectory()) {
34710
+ if (ENV_FILE.test(basename2(src)) && !entry.isDirectory()) {
34986
34711
  inWorkingTree.push(relative6(sourceDir, src).split(sep5).join("/"));
34987
34712
  return false;
34988
34713
  }
@@ -34993,7 +34718,7 @@ async function copyWorkingTree(source, checkout) {
34993
34718
  absoluteLinks.push(path);
34994
34719
  return false;
34995
34720
  }
34996
- if (!inside(resolve10(dirname6(src), target2), sourceDir)) {
34721
+ if (!inside(resolve11(dirname5(src), target2), sourceDir)) {
34997
34722
  linksOutside.push(path);
34998
34723
  return false;
34999
34724
  }
@@ -35031,21 +34756,21 @@ async function copyWorkingTree(source, checkout) {
35031
34756
  continue;
35032
34757
  }
35033
34758
  const target2 = join25(checkout, path);
35034
- if (!inside(await realAncestor(dirname6(target2)), root)) {
34759
+ if (!inside(await realAncestor(dirname5(target2)), root)) {
35035
34760
  outsideProject.push(path);
35036
34761
  continue;
35037
34762
  }
35038
34763
  try {
35039
- await mkdir3(dirname6(target2), { recursive: true });
34764
+ await mkdir3(dirname5(target2), { recursive: true });
35040
34765
  } catch {
35041
34766
  outsideProject.push(path);
35042
34767
  continue;
35043
34768
  }
35044
- await rm4(target2, { recursive: true, force: true });
34769
+ await rm5(target2, { recursive: true, force: true });
35045
34770
  const content = await git(sourceDir, ["show", `HEAD:./${path}`]);
35046
34771
  if (isLink) {
35047
34772
  const linkTarget = content.toString("utf8");
35048
- const pointsAt = resolve10(dirname6(target2), linkTarget);
34773
+ const pointsAt = resolve11(dirname5(target2), linkTarget);
35049
34774
  if (isAbsolute4(linkTarget) || !inside(pointsAt, checkout)) {
35050
34775
  outsideProject.push(path);
35051
34776
  continue;
@@ -35054,7 +34779,7 @@ async function copyWorkingTree(source, checkout) {
35054
34779
  envLinks.push(path);
35055
34780
  continue;
35056
34781
  }
35057
- await writeFile7(target2, content, { flag: "wx" });
34782
+ await writeFile6(target2, content, { flag: "wx" });
35058
34783
  const local = await readLocal(join25(sourceDir, path));
35059
34784
  if (!local || !sameText(local, content)) committedVersion.push(path);
35060
34785
  }
@@ -35149,7 +34874,7 @@ var TtyPrompter = class {
35149
34874
  });
35150
34875
  try {
35151
34876
  return await new Promise(
35152
- (resolve15) => rl.question(question, resolve15)
34877
+ (resolve16) => rl.question(question, resolve16)
35153
34878
  );
35154
34879
  } finally {
35155
34880
  rl.close();
@@ -35170,7 +34895,7 @@ var TtyPrompter = class {
35170
34895
  });
35171
34896
  try {
35172
34897
  const answer = new Promise(
35173
- (resolve15) => rl.question(question, resolve15)
34898
+ (resolve16) => rl.question(question, resolve16)
35174
34899
  );
35175
34900
  muted = true;
35176
34901
  const value = await answer;
@@ -35506,7 +35231,7 @@ async function runDbLint(context) {
35506
35231
  }
35507
35232
 
35508
35233
  // src/functions/deploy.ts
35509
- import { relative as relative11, resolve as resolve13, sep as sep10 } from "path";
35234
+ import { relative as relative11, resolve as resolve14, sep as sep10 } from "path";
35510
35235
 
35511
35236
  // ../../packages/functions-bundler/src/errors.ts
35512
35237
  var FunctionBundleError = class extends Error {
@@ -35563,7 +35288,7 @@ var BUNDLE_MESSAGES = {
35563
35288
 
35564
35289
  // ../../packages/functions-bundler/src/discover.ts
35565
35290
  import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
35566
- import { join as join27, relative as relative8, resolve as resolve11, sep as sep6 } from "path";
35291
+ import { join as join27, relative as relative8, resolve as resolve12, sep as sep6 } from "path";
35567
35292
 
35568
35293
  // ../../packages/functions-bundler/src/limits.ts
35569
35294
  var FUNCTION_BUNDLE_MAX_BYTES = 67108864;
@@ -35585,9 +35310,9 @@ function isEntrypoint(value) {
35585
35310
  );
35586
35311
  }
35587
35312
  function functionsFolder(options) {
35588
- const root = resolve11(options.cwd, options.root ?? ".");
35313
+ const root = resolve12(options.cwd, options.root ?? ".");
35589
35314
  const explicit = options.dir !== void 0;
35590
- const dir = explicit ? resolve11(options.cwd, options.dir) : join27(root, "naro", "functions");
35315
+ const dir = explicit ? resolve12(options.cwd, options.dir) : join27(root, "naro", "functions");
35591
35316
  const label2 = relative8(options.cwd, dir).split(sep6).join("/") || ".";
35592
35317
  return { dir, label: label2, root, explicit };
35593
35318
  }
@@ -35706,7 +35431,7 @@ function closesNext(text3, from) {
35706
35431
  while (i < text3.length && /\s/.test(text3[i] ?? "")) i += 1;
35707
35432
  return text3[i] === "}" || text3[i] === "]";
35708
35433
  }
35709
- function parseJsonc2(text3) {
35434
+ function parseJsonc(text3) {
35710
35435
  const uncommented = stripComments2(text3);
35711
35436
  let out = "";
35712
35437
  let inString = false;
@@ -35767,11 +35492,11 @@ function stripComments2(text3) {
35767
35492
  }
35768
35493
 
35769
35494
  // ../../packages/functions-bundler/src/node-compat.ts
35770
- import { createRequire as createRequire2 } from "module";
35771
- import { dirname as dirname7, resolve as resolvePath } from "path";
35772
- import { fileURLToPath as fileURLToPath2 } from "url";
35773
- var require2 = createRequire2(import.meta.url);
35774
- var MODULE_DIR = dirname7(fileURLToPath2(import.meta.url));
35495
+ import { createRequire } from "module";
35496
+ import { dirname as dirname6, resolve as resolvePath } from "path";
35497
+ import { fileURLToPath as fileURLToPath3 } from "url";
35498
+ var require2 = createRequire(import.meta.url);
35499
+ var MODULE_DIR = dirname6(fileURLToPath3(import.meta.url));
35775
35500
  var REQUIRED_NODE_BUILT_IN_NAMESPACE = "node-built-in-modules";
35776
35501
  var REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
35777
35502
  var SKIPPED_ALIASES = /* @__PURE__ */ new Set(["debug"]);
@@ -35901,12 +35626,12 @@ ${injects.map(
35901
35626
 
35902
35627
  // ../../packages/functions-bundler/src/deno-imports.ts
35903
35628
  import { existsSync as existsSync9, readFileSync as readFileSync10, realpathSync as realpathSync2 } from "fs";
35904
- import { dirname as dirname9, isAbsolute as isAbsolute5, join as join29, relative as relative9, sep as sep8 } from "path";
35629
+ import { dirname as dirname8, isAbsolute as isAbsolute5, join as join29, relative as relative9, sep as sep8 } from "path";
35905
35630
 
35906
35631
  // ../../packages/functions-bundler/src/import-map.ts
35907
35632
  import { existsSync as existsSync8, readFileSync as readFileSync9, realpathSync } from "fs";
35908
- import { basename as basename4, dirname as dirname8, join as join28, resolve as resolve12, sep as sep7 } from "path";
35909
- import { fileURLToPath as fileURLToPath3 } from "url";
35633
+ import { basename as basename3, dirname as dirname7, join as join28, resolve as resolve13, sep as sep7 } from "path";
35634
+ import { fileURLToPath as fileURLToPath4 } from "url";
35910
35635
 
35911
35636
  // ../../packages/functions-bundler/src/printable.ts
35912
35637
  var UNPRINTABLE2 = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
@@ -35939,7 +35664,7 @@ function expandBareKeys(imports) {
35939
35664
  function load(file2, label2) {
35940
35665
  let parsed;
35941
35666
  try {
35942
- parsed = parseJsonc2(readFileSync9(file2, "utf8"));
35667
+ parsed = parseJsonc(readFileSync9(file2, "utf8"));
35943
35668
  } catch {
35944
35669
  throw new FunctionBundleError(
35945
35670
  "VALIDATION_FAILED",
@@ -35962,8 +35687,8 @@ function load(file2, label2) {
35962
35687
  printable2(BUNDLE_MESSAGES.importMapPrefix(label2, key, value))
35963
35688
  );
35964
35689
  return {
35965
- imports: DENO_CONFIGS.has(basename4(file2)) ? expandBareKeys(written) : written,
35966
- base: dirname8(file2),
35690
+ imports: DENO_CONFIGS.has(basename3(file2)) ? expandBareKeys(written) : written,
35691
+ base: dirname7(file2),
35967
35692
  label: label2
35968
35693
  };
35969
35694
  }
@@ -35996,7 +35721,7 @@ var isPathLike = (text3) => text3.startsWith("./") || text3.startsWith("../") ||
35996
35721
  function filePath(text3) {
35997
35722
  if (!text3.startsWith("file:")) return null;
35998
35723
  try {
35999
- return fileURLToPath3(text3);
35724
+ return fileURLToPath4(text3);
36000
35725
  } catch {
36001
35726
  return null;
36002
35727
  }
@@ -36014,7 +35739,7 @@ function keysOf(map2) {
36014
35739
  for (const [key, target2] of Object.entries(map2.imports)) {
36015
35740
  const trailing = key.endsWith("/");
36016
35741
  const file2 = filePath(key);
36017
- const name = file2 !== null ? realKey(file2, trailing) : isPathLike(key) ? realKey(resolve12(base2, key), trailing) : key;
35742
+ const name = file2 !== null ? realKey(file2, trailing) : isPathLike(key) ? realKey(resolve13(base2, key), trailing) : key;
36018
35743
  if (!keys.has(name)) keys.set(name, target2);
36019
35744
  }
36020
35745
  normalized.set(map2, keys);
@@ -36026,10 +35751,10 @@ function mapSpecifier(map2, specifier, importerDir) {
36026
35751
  const file2 = filePath(specifier);
36027
35752
  if (file2 !== null) lookup = realKey(file2, trailing);
36028
35753
  else if (specifier.startsWith("/"))
36029
- lookup = realKey(resolve12(specifier), trailing);
35754
+ lookup = realKey(resolve13(specifier), trailing);
36030
35755
  else if (isPathLike(specifier)) {
36031
35756
  if (importerDir === void 0) return null;
36032
- lookup = realKey(resolve12(importerDir, specifier), trailing);
35757
+ lookup = realKey(resolve13(importerDir, specifier), trailing);
36033
35758
  }
36034
35759
  const keys = keysOf(map2);
36035
35760
  let target2 = keys.get(lookup);
@@ -36042,7 +35767,7 @@ function mapSpecifier(map2, specifier, importerDir) {
36042
35767
  target2 = `${keys.get(prefix) ?? ""}${lookup.slice(prefix.length)}`;
36043
35768
  }
36044
35769
  if (target2.startsWith("./") || target2.startsWith("../"))
36045
- return resolve12(map2.base, target2);
35770
+ return resolve13(map2.base, target2);
36046
35771
  return filePath(target2) ?? target2;
36047
35772
  }
36048
35773
 
@@ -36122,7 +35847,7 @@ function packageDir(from, projectRoot) {
36122
35847
  const inNodeModules = fromRoot(projectRoot, current).split("/").includes("node_modules");
36123
35848
  if (!inNodeModules && existsSync9(join29(current, "package.json")))
36124
35849
  return fromRoot(projectRoot, current) || ".";
36125
- const up = dirname9(current);
35850
+ const up = dirname8(current);
36126
35851
  if (up === current) break;
36127
35852
  current = up;
36128
35853
  }
@@ -36133,14 +35858,14 @@ function isInstalled(from, pkg) {
36133
35858
  let current = from;
36134
35859
  for (; ; ) {
36135
35860
  if (existsSync9(join29(current, "node_modules", pkg))) return true;
36136
- const up = dirname9(current);
35861
+ const up = dirname8(current);
36137
35862
  if (up === current) return false;
36138
35863
  current = up;
36139
35864
  }
36140
35865
  }
36141
35866
  var NODE_MODULES = /[\\/]node_modules[\\/]/;
36142
35867
  function installedVersion(resolved, pkg) {
36143
- let current = dirname9(resolved);
35868
+ let current = dirname8(resolved);
36144
35869
  for (; ; ) {
36145
35870
  const file2 = join29(current, "package.json");
36146
35871
  if (existsSync9(file2)) {
@@ -36151,7 +35876,7 @@ function installedVersion(resolved, pkg) {
36151
35876
  } catch {
36152
35877
  }
36153
35878
  }
36154
- const up = dirname9(current);
35879
+ const up = dirname8(current);
36155
35880
  if (up === current) return null;
36156
35881
  current = up;
36157
35882
  }
@@ -36581,7 +36306,7 @@ var FUNCTION_BUSY_WAIT_MS = 5e3;
36581
36306
  var FUNCTION_RATE_LIMIT_DEFAULT_SECONDS = 60;
36582
36307
  var FUNCTION_RATE_LIMIT_MAX_SECONDS = 300;
36583
36308
  function defaultSleep(ms) {
36584
- return new Promise((resolve15) => setTimeout(resolve15, ms));
36309
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
36585
36310
  }
36586
36311
  function retryAfterSeconds(error51) {
36587
36312
  const value = error51.details?.retryAfter;
@@ -36778,7 +36503,7 @@ async function deployOne(options, folder, name, importMap, secretNames, io, budg
36778
36503
  }
36779
36504
  function readFlagImportMap(options, folder) {
36780
36505
  if (options.importMap === void 0) return void 0;
36781
- const file2 = resolve13(options.cwd, options.importMap);
36506
+ const file2 = resolve14(options.cwd, options.importMap);
36782
36507
  try {
36783
36508
  return readImportMapFile(
36784
36509
  file2,
@@ -36896,7 +36621,7 @@ function eventsObserver(cp3, deploymentId, token, fetchImpl = fetch, logIngestUr
36896
36621
  } catch (error51) {
36897
36622
  lastError = error51;
36898
36623
  await new Promise(
36899
- (resolve15) => setTimeout(resolve15, 500 * (attempt + 1))
36624
+ (resolve16) => setTimeout(resolve16, 500 * (attempt + 1))
36900
36625
  );
36901
36626
  }
36902
36627
  }
@@ -36972,19 +36697,19 @@ function slugify2(value) {
36972
36697
  async function projectName(sourceDir) {
36973
36698
  try {
36974
36699
  const pkg = JSON.parse(
36975
- await readFile15(join31(sourceDir, "package.json"), "utf8")
36700
+ await readFile16(join31(sourceDir, "package.json"), "utf8")
36976
36701
  );
36977
36702
  if (pkg.name) return pkg.name;
36978
36703
  } catch {
36979
36704
  }
36980
- return basename5(sourceDir);
36705
+ return basename4(sourceDir);
36981
36706
  }
36982
36707
  function resolveBin(name) {
36983
- let dir = dirname10(fileURLToPath4(import.meta.url));
36708
+ let dir = dirname9(fileURLToPath5(import.meta.url));
36984
36709
  for (; ; ) {
36985
36710
  const candidate = join31(dir, "node_modules", ".bin", name);
36986
36711
  if (existsSync10(candidate)) return candidate;
36987
- const parent = dirname10(dir);
36712
+ const parent = dirname9(dir);
36988
36713
  if (parent === dir) return name;
36989
36714
  dir = parent;
36990
36715
  }
@@ -37050,7 +36775,7 @@ function workingTreeNotices(copy) {
37050
36775
  function localDependencies(sourceDir, say = () => void 0) {
37051
36776
  return {
37052
36777
  createWorkspace: () => mkdtemp2(join31(tmpdir2(), "naro-build-")),
37053
- removeWorkspace: (path) => rm5(path, { recursive: true, force: true }),
36778
+ removeWorkspace: (path) => rm6(path, { recursive: true, force: true }),
37054
36779
  async runCommand(spec, observer) {
37055
36780
  if (spec.command === "git" && spec.args[0] === "clone") {
37056
36781
  const checkout = spec.args.at(-1);
@@ -37075,9 +36800,6 @@ function localDependencies(sourceDir, say = () => void 0) {
37075
36800
  detectFramework,
37076
36801
  readStaticArtifact,
37077
36802
  createPackageManagerConfig,
37078
- createPlatformBuildConfig,
37079
- readOpenNextArtifact,
37080
- openNextCommand: resolveBin("opennextjs-cloudflare"),
37081
36803
  wranglerCommand: resolveBin("wrangler"),
37082
36804
  // `vinext check` runs on the bare checkout, before the toolchain is added
37083
36805
  // to the user's tree, so this binary has to come from the CLI's own deps.
@@ -37099,7 +36821,7 @@ async function deployDirectory(sourceDir, options, log2) {
37099
36821
  "Pass production: true for production; preview is the default."
37100
36822
  );
37101
36823
  const surface = production ? "production" : environment ?? "preview";
37102
- const appRoot = options.rootDirectory ? resolve14(sourceDir, options.rootDirectory) : sourceDir;
36824
+ const appRoot = options.rootDirectory ? resolve15(sourceDir, options.rootDirectory) : sourceDir;
37103
36825
  const framework = detectFramework2(appRoot);
37104
36826
  if (!framework.deployable && !options.force) {
37105
36827
  throw new NaroError(
@@ -37187,6 +36909,19 @@ async function deployDirectory(sourceDir, options, log2) {
37187
36909
  });
37188
36910
  if (ctx.envSkipped?.length)
37189
36911
  log2(`\u26A0 skipped env: ${ctx.envSkipped.join(", ")}`);
36912
+ const storedAdapter = ctx.userAdapter ?? null;
36913
+ if (options.userAdapter && options.userAdapter !== storedAdapter) {
36914
+ try {
36915
+ await client.projects.update(ctx.projectId, {
36916
+ userAdapter: options.userAdapter
36917
+ });
36918
+ } catch (error51) {
36919
+ log2(
36920
+ `\u26A0 Could not save the Next.js adapter choice: ${error51 instanceof Error ? error51.message : String(error51)}`
36921
+ );
36922
+ }
36923
+ }
36924
+ if (options.userAdapter) log2(userAdapterOutcome(options.userAdapter));
37190
36925
  const executor = options.executor ?? new LocalBuildExecutor(localDependencies(sourceDir, log2));
37191
36926
  const observer = eventsObserver(
37192
36927
  cpUrl,
@@ -37215,7 +36950,8 @@ async function deployDirectory(sourceDir, options, log2) {
37215
36950
  // one, as before.
37216
36951
  env: ctx.environment ? { ...envToSend, ...ctx.env } : { ...ctx.env, ...envToSend },
37217
36952
  strategy: ctx.strategy ?? null,
37218
- ...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled }
36953
+ ...ctx.vinextEnabled === void 0 ? {} : { vinextEnabled: ctx.vinextEnabled },
36954
+ ...options.userAdapter ?? storedAdapter ? { userAdapter: options.userAdapter ?? storedAdapter } : {}
37219
36955
  },
37220
36956
  observer,
37221
36957
  createCacheTransport({
@@ -37397,6 +37133,13 @@ export {
37397
37133
  promptForMissingEnv,
37398
37134
  pullEnv,
37399
37135
  detectGitInfo,
37136
+ detectUserAdapter,
37137
+ parseUserAdapter,
37138
+ USER_ADAPTER_OPTIONS,
37139
+ userAdapterQuestion,
37140
+ userAdapterStored,
37141
+ userAdapterUnasked,
37142
+ userAdapterUnreadable,
37400
37143
  version2 as version,
37401
37144
  DATA_ENV_MESSAGES,
37402
37145
  dataEnvironment,
@@ -37426,7 +37169,7 @@ export {
37426
37169
  runDbLint,
37427
37170
  BUNDLE_MESSAGES,
37428
37171
  functionsFolder,
37429
- parseJsonc2 as parseJsonc,
37172
+ parseJsonc,
37430
37173
  defaultSleep,
37431
37174
  retryAfterSeconds,
37432
37175
  withFunctionRetries,