diff --git a/.changeset/port-pr-1083.md b/.changeset/port-pr-1083.md new file mode 100644 index 00000000..d0c20b54 --- /dev/null +++ b/.changeset/port-pr-1083.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1083 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1083 diff --git a/.changeset/port-pr-1097.md b/.changeset/port-pr-1097.md new file mode 100644 index 00000000..969f751c --- /dev/null +++ b/.changeset/port-pr-1097.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1097 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1097 diff --git a/.changeset/port-pr-1105.md b/.changeset/port-pr-1105.md new file mode 100644 index 00000000..d2ebdaa0 --- /dev/null +++ b/.changeset/port-pr-1105.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1105 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1105 diff --git a/.changeset/port-pr-1122-aws.md b/.changeset/port-pr-1122-aws.md new file mode 100644 index 00000000..8477026c --- /dev/null +++ b/.changeset/port-pr-1122-aws.md @@ -0,0 +1,9 @@ +--- +"@opennextjs/aws": patch +--- + +Ported PR #1122 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1122 + +Changed `checkRunningInsideNextjsApp` function signature to accept `{ appPath: string }` instead of full `BuildOptions` object, making it more flexible for use in the migrate command. diff --git a/.changeset/port-pr-1122-cloudflare.md b/.changeset/port-pr-1122-cloudflare.md new file mode 100644 index 00000000..a7f15deb --- /dev/null +++ b/.changeset/port-pr-1122-cloudflare.md @@ -0,0 +1,13 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1122 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1122 + +Applied bugfixes and improvements to the `migrate` command: + +- Fixed extra newlines when appending to files (updated `conditionalAppendFileSync` function signature to use options object with `appendIf` and `appendPrefix`) +- Fixed error when `public` directory is missing (now creates parent directories automatically) +- Fixed Next.js config file update to check if the file exists before attempting to update diff --git a/.changeset/port-pr-1126.md b/.changeset/port-pr-1126.md new file mode 100644 index 00000000..d6ef4453 --- /dev/null +++ b/.changeset/port-pr-1126.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1126 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1126 diff --git a/.changeset/port-pr-1127.md b/.changeset/port-pr-1127.md new file mode 100644 index 00000000..71335f2c --- /dev/null +++ b/.changeset/port-pr-1127.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1127 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1127 diff --git a/.changeset/port-pr-1133.md b/.changeset/port-pr-1133.md new file mode 100644 index 00000000..2f861e68 --- /dev/null +++ b/.changeset/port-pr-1133.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1133 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1133 diff --git a/.changeset/port-pr-1138.md b/.changeset/port-pr-1138.md new file mode 100644 index 00000000..2c25319d --- /dev/null +++ b/.changeset/port-pr-1138.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1138 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1138 diff --git a/.changeset/port-pr-1142.md b/.changeset/port-pr-1142.md new file mode 100644 index 00000000..19aeb7f0 --- /dev/null +++ b/.changeset/port-pr-1142.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1142 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1142 diff --git a/.changeset/port-pr-1146.md b/.changeset/port-pr-1146.md new file mode 100644 index 00000000..e02c3cc6 --- /dev/null +++ b/.changeset/port-pr-1146.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1146 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1146 diff --git a/.changeset/port-pr-1147.md b/.changeset/port-pr-1147.md new file mode 100644 index 00000000..48131951 --- /dev/null +++ b/.changeset/port-pr-1147.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1147 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1147 diff --git a/.changeset/port-pr-1150.md b/.changeset/port-pr-1150.md new file mode 100644 index 00000000..779ebfda --- /dev/null +++ b/.changeset/port-pr-1150.md @@ -0,0 +1,7 @@ +--- +"@opennextjs/cloudflare": patch +--- + +Ported PR #1150 from source repository + +https://github.com/opennextjs/opennextjs-cloudflare/pull/1150 diff --git a/.claude/skills/port-pr/SKILL.md b/.claude/skills/port-pr/SKILL.md index 410432ee..e8083cf8 100644 --- a/.claude/skills/port-pr/SKILL.md +++ b/.claude/skills/port-pr/SKILL.md @@ -64,17 +64,17 @@ Apply similar changes following this repository's conventions: After implementation, run: ```bash -pnpm code:checks +pnpm code:checks 2>&1 | tail -20 ``` -This runs formatting, linting, and TypeScript checks. +This runs formatting, linting, and TypeScript checks. Only the tail of the output is shown to see the result. ### 7. Run Unit Tests After code checks pass, run only the unit tests from the tests-unit package: ```bash -pnpm --filter tests-unit test +pnpm --filter tests-unit test 2>&1 | tail -20 ``` **Important:** Do NOT run `pnpm test` (which runs all tests), `pnpm e2e:test`, or any full test suite. Only unit tests from the tests-unit package should be run during the porting process. @@ -105,32 +105,46 @@ Ported PR #$PR_NUMBER from source repository $ARGUMENTS" > .changeset/port-pr-$PR_NUMBER.md ``` -### 10. Stage Changes and Prepare Commit +### 10. Stage All Files -Stage the changeset file and prepare a commit message (but do not commit): +Stage all files that were modified or created during the port: ```bash -# Stage the changeset file -git add .changeset/port-pr-$PR_NUMBER.md +git status --short +git add .changeset/port-pr-$PR_NUMBER.md +``` -# Prepare the commit message with the PR link (stored for later) -echo "chore: port PR #$PR_NUMBER from source repository +### 11. Ask for Commit -$ARGUMENTS +Ask the user if they want to commit the changes directly: -Changeset: .changeset/port-pr-$PR_NUMBER.md" > /tmp/commit-message-port-pr-$PR_NUMBER.txt +Ask the user: **"Should I stage all files and commit the ported PR changes now?"** -# Display the prepared commit message -cat /tmp/commit-message-port-pr-$PR_NUMBER.txt -``` +Options: -The changeset is staged and ready to commit. The commit message is saved at `/tmp/commit-message-port-pr-$PR_NUMBER.txt` for reference. +- "Yes, commit now" - Stage all files and commit with the prepared message +- "No, I'll commit manually" - Let the user stage and commit themselves + +If the user chooses to commit: + +```bash +# Stage all modified and new files +git add + +# Commit with the prepared message +git commit -m "chore: port PR #$PR_NUMBER from source repository + +$ARGUMENTS + +Changeset: .changeset/port-pr-$PR_NUMBER.md" +``` -### 11. Summary +### 12. Summary Provide a summary of: - What was ported - Any adaptations made - Files modified/created +- Whether the changes were committed - Any remaining TODOs or follow-up items diff --git a/create-cloudflare/next/package.json b/create-cloudflare/next/package.json index 70136d6b..06e2cbe7 100644 --- a/create-cloudflare/next/package.json +++ b/create-cloudflare/next/package.json @@ -13,7 +13,7 @@ "cf-typegen": "wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts" }, "dependencies": { - "@opennextjs/cloudflare": "^1.15.1", + "@opennextjs/cloudflare": "^1.17.1", "next": "16.1.4", "react": "19.1.4", "react-dom": "19.1.4" diff --git a/create-cloudflare/next/wrangler.jsonc b/create-cloudflare/next/wrangler.jsonc index b003c9ad..1c90bdb4 100644 --- a/create-cloudflare/next/wrangler.jsonc +++ b/create-cloudflare/next/wrangler.jsonc @@ -6,7 +6,7 @@ "$schema": "node_modules/wrangler/config-schema.json", "name": "worker_name", "main": ".open-next/worker.js", - "compatibility_date": "2025-12-01", + "compatibility_date": "", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], "assets": { "binding": "ASSETS", diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index 7aa29cf3..4430bd40 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -54,6 +54,7 @@ "@dotenvx/dotenvx": "catalog:", "@opennextjs/aws": "workspace:*", "cloudflare": "^4.4.1", + "comment-json": "^4.5.1", "enquirer": "^2.4.1", "glob": "catalog:", "ts-tqdm": "^0.8.6", @@ -62,6 +63,7 @@ "devDependencies": { "@cloudflare/workers-types": "catalog:", "@tsconfig/strictest": "catalog:", + "@types/comment-json": "^2.4.5", "@types/mock-fs": "catalog:", "@types/node": "catalog:", "@types/picomatch": "^4.0.0", diff --git a/packages/cloudflare/src/api/overrides/asset-resolver/index.spec.ts b/packages/cloudflare/src/api/overrides/asset-resolver/index.spec.ts index e58e451b..defc4573 100644 --- a/packages/cloudflare/src/api/overrides/asset-resolver/index.spec.ts +++ b/packages/cloudflare/src/api/overrides/asset-resolver/index.spec.ts @@ -1,7 +1,80 @@ -import { describe, expect, test } from "vitest"; +import { beforeEach, describe, expect, test, vi } from "vitest"; import { isUserWorkerFirst } from "./index.js"; +const mockAssetsFetch = vi.fn(); + +vi.mock("../../cloudflare-context.js", () => ({ + getCloudflareContext: () => ({ + env: { + ASSETS: { fetch: mockAssetsFetch }, + }, + }), +})); + +describe("maybeGetAssetResult", () => { + let resolver: typeof import("./index.js").default; + + beforeEach(async () => { + vi.resetModules(); + mockAssetsFetch.mockReset(); + globalThis.__ASSETS_RUN_WORKER_FIRST__ = true; + resolver = (await import("./index.js")).default; + }); + + const makeEvent = (method: string, rawPath: string) => + ({ + method, + rawPath, + headers: { accept: "*/*" }, + }) as Parameters[0]; + + test("GET request returns response body", async () => { + const body = new ReadableStream(); + mockAssetsFetch.mockResolvedValue(new Response(body, { status: 200 })); + + const result = await resolver.maybeGetAssetResult(makeEvent("GET", "/style.css")); + + expect(result).toBeDefined(); + expect(result!.statusCode).toBe(200); + expect(result!.body).not.toBeNull(); + }); + + test("HEAD request returns null body", async () => { + mockAssetsFetch.mockResolvedValue(new Response(null, { status: 200 })); + + const result = await resolver.maybeGetAssetResult(makeEvent("HEAD", "/style.css")); + + expect(result).toBeDefined(); + expect(result!.statusCode).toBe(200); + expect(result!.body).toBeNull(); + }); + + test("returns undefined for 404 responses", async () => { + mockAssetsFetch.mockResolvedValue(new Response(null, { status: 404 })); + + const result = await resolver.maybeGetAssetResult(makeEvent("GET", "/missing.css")); + + expect(result).toBeUndefined(); + }); + + test("returns undefined for POST requests", async () => { + const result = await resolver.maybeGetAssetResult(makeEvent("POST", "/style.css")); + + expect(result).toBeUndefined(); + expect(mockAssetsFetch).not.toHaveBeenCalled(); + }); + + test("returns undefined when run_worker_first is false", async () => { + globalThis.__ASSETS_RUN_WORKER_FIRST__ = false; + + const result = await resolver.maybeGetAssetResult(makeEvent("GET", "/style.css")); + + expect(result).toBeUndefined(); + expect(mockAssetsFetch).not.toHaveBeenCalled(); + }); +}); + describe("isUserWorkerFirst", () => { test("run_worker_first = false", () => { expect(isUserWorkerFirst(false, "/test")).toBe(false); diff --git a/packages/cloudflare/src/api/overrides/asset-resolver/index.ts b/packages/cloudflare/src/api/overrides/asset-resolver/index.ts index 88d7e88d..e3b17945 100644 --- a/packages/cloudflare/src/api/overrides/asset-resolver/index.ts +++ b/packages/cloudflare/src/api/overrides/asset-resolver/index.ts @@ -44,14 +44,33 @@ const resolver: AssetResolver = { type: "core", statusCode: response.status, headers: Object.fromEntries(response.headers.entries()), - // Workers and Node types differ. - // oxlint-disable-next-line @typescript-eslint/no-explicit-any - body: response.body || (new ReadableStream() as any), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + body: getResponseBody(method, response) as any, isBase64Encoded: false, } satisfies InternalResult; }, }; +/** + * Returns the response body for an asset result. + * + * HEAD responses must return `null` because `response.body` is `null` per the HTTP spec + * and the `new ReadableStream()` fallback would create a stream that never closes, hanging the Worker. + * + * @param method - The HTTP method of the request. + * @param response - The response from the ASSETS binding. + * @returns The body to use in the internal result. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getResponseBody(method: string, response: Response): ReadableStream | null { + if (method === "HEAD") { + return null; + } + // Workers and Node ReadableStream types differ. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return response.body || (new ReadableStream() as any); +} + /** * @param runWorkerFirst `run_worker_first` config * @param pathname pathname of the request diff --git a/packages/cloudflare/src/api/overrides/incremental-cache/regional-cache.ts b/packages/cloudflare/src/api/overrides/incremental-cache/regional-cache.ts index ee857609..d6537c11 100644 --- a/packages/cloudflare/src/api/overrides/incremental-cache/regional-cache.ts +++ b/packages/cloudflare/src/api/overrides/incremental-cache/regional-cache.ts @@ -9,8 +9,6 @@ import { import { getCloudflareContext } from "../../cloudflare-context.js"; import { debugCache, FALLBACK_BUILD_ID, IncrementalCacheEntry, isPurgeCacheEnabled } from "../internal.js"; -import { NAME as KV_CACHE_NAME } from "./kv-incremental-cache.js"; - const ONE_MINUTE_IN_SECONDS = 60; const THIRTY_MINUTES_IN_SECONDS = ONE_MINUTE_IN_SECONDS * 30; @@ -79,20 +77,17 @@ class RegionalCache implements IncrementalCache { private store: IncrementalCache, private opts: Options ) { - if (this.store.name === KV_CACHE_NAME) { - throw new Error("The KV incremental cache does not need a regional cache."); - } this.name = this.store.name; + // `shouldLazilyUpdateOnCacheHit` is not needed when cache purge is enabled. this.opts.shouldLazilyUpdateOnCacheHit ??= this.opts.mode === "long-lived" && !isPurgeCacheEnabled(); } get #bypassTagCacheOnCacheHit(): boolean { if (this.opts.bypassTagCacheOnCacheHit !== undefined) { - // If the bypassTagCacheOnCacheHit option is set we return that one return this.opts.bypassTagCacheOnCacheHit; } - // Otherwise we default to whether the automatic cache purging is enabled or not + // When `bypassTagCacheOnCacheHit` is not set, we default to whether the automatic cache purging is enabled or not return isPurgeCacheEnabled(); } @@ -238,17 +233,7 @@ class RegionalCache implements IncrementalCache { * a request is made to another region that has an entry stored in its regional cache. * * @param cache Incremental cache instance. - * @param opts.mode The mode to use for the regional cache. - * - `short-lived`: Re-use a cache entry for up to a minute after it has been retrieved. - * - `long-lived`: Re-use a fetch cache entry until it is revalidated (per-region), - * or an ISR/SSG entry for up to 30 minutes. - * @param opts.shouldLazilyUpdateOnCacheHit Whether the regional cache entry should be updated in - * the background or not when it experiences a cache hit. - * @param opts.defaultLongLivedTtlSec The default age to use for long-lived cache entries. - * When no revalidate is provided, the default age will be used. - * @default `THIRTY_MINUTES_IN_SECONDS` - * - * @default `false` for the `short-lived` mode, and `true` for the `long-lived` mode. + * @param opts Options for the regional cache. */ export function withRegionalCache(cache: IncrementalCache, opts: Options) { return new RegionalCache(cache, opts); diff --git a/packages/cloudflare/src/cli/build/build.ts b/packages/cloudflare/src/cli/build/build.ts index f006f429..a95280b5 100644 --- a/packages/cloudflare/src/cli/build/build.ts +++ b/packages/cloudflare/src/cli/build/build.ts @@ -4,6 +4,7 @@ import { printHeader } from "@opennextjs/aws/build/utils.js"; import logger from "@opennextjs/aws/logger.js"; import type { ProjectOptions } from "../project-options.js"; +import { ensureNextjsVersionSupported } from "../utils/nextjs-support.js"; import { getVersion } from "./utils/version.js"; @@ -43,25 +44,3 @@ export async function build(options: buildHelper.BuildOptions, projectOpts: Proj logger.info("Using adapter outputs for building OpenNext bundle."); } - -async function ensureNextjsVersionSupported({ nextVersion }: buildHelper.BuildOptions) { - if (buildHelper.compareSemver(nextVersion, "<", "14.2.0")) { - logger.error("Next.js version unsupported, please upgrade to version 14.2 or greater."); - process.exit(1); - } - - const { - default: { version: wranglerVersion }, - } = await import("wrangler/package.json", { with: { type: "json" } }); - - // We need a version of workerd that has a fix for setImmediate for Next.js 16.1+ - // See: - // - https://github.com/cloudflare/workerd/pull/5869 - // - https://github.com/opennextjs/opennextjs-cloudflare/issues/1049 - if ( - buildHelper.compareSemver(nextVersion, ">=", "16.1.0") && - buildHelper.compareSemver(wranglerVersion, "<", "4.59.2") - ) { - logger.warn(`Next.js 16.1+ requires wrangler 4.59.2 or greater (${wranglerVersion} detected).`); - } -} diff --git a/packages/cloudflare/src/cli/build/bundle-server.ts b/packages/cloudflare/src/cli/build/bundle-server.ts index 02810f17..7aee1a66 100644 --- a/packages/cloudflare/src/cli/build/bundle-server.ts +++ b/packages/cloudflare/src/cli/build/bundle-server.ts @@ -10,6 +10,7 @@ import { build, type Plugin } from "esbuild"; import { getOpenNextConfig } from "../../api/config.js"; import type { ProjectOptions } from "../project-options.js"; +import { normalizePath } from "../utils/normalize-path.js"; import { patchVercelOgLibrary } from "./patches/ast/patch-vercel-og-library.js"; import { patchWebpackRuntime } from "./patches/ast/webpack-runtime.js"; @@ -27,7 +28,8 @@ import { fixRequire } from "./patches/plugins/require.js"; import { patchRouteModules } from "./patches/plugins/route-module.js"; import { shimReact } from "./patches/plugins/shim-react.js"; import { setWranglerExternal } from "./patches/plugins/wrangler-external.js"; -import { copyPackageCliFiles, needsExperimentalReact, normalizePath } from "./utils/index.js"; +import { copyPackageCliFiles } from "./utils/copy-package-cli-files.js"; +import { needsExperimentalReact } from "./utils/needs-experimental-react.js"; /** The dist directory of the Cloudflare adapter package */ const packageDistDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "../.."); diff --git a/packages/cloudflare/src/cli/build/open-next/compile-env-files.ts b/packages/cloudflare/src/cli/build/open-next/compile-env-files.ts index 63204b0c..9da91d3e 100644 --- a/packages/cloudflare/src/cli/build/open-next/compile-env-files.ts +++ b/packages/cloudflare/src/cli/build/open-next/compile-env-files.ts @@ -3,7 +3,7 @@ import path from "node:path"; import { BuildOptions } from "@opennextjs/aws/build/helper.js"; -import { extractProjectEnvVars } from "../utils/index.js"; +import { extractProjectEnvVars } from "../../utils/extract-project-env-vars.js"; /** * Compiles the values extracted from the project's env files to the output directory for use in the worker. diff --git a/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts b/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts index 4d990f52..5478411c 100644 --- a/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts +++ b/packages/cloudflare/src/cli/build/open-next/createServerBundle.ts @@ -25,10 +25,10 @@ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; import type { Plugin } from "esbuild"; import type { BuildCompleteCtx } from "../../adapter.js"; +import { normalizePath } from "../../utils/normalize-path.js"; import { patchResRevalidate } from "../patches/plugins/res-revalidate.js"; import { patchTurbopackRuntime } from "../patches/plugins/turbopack.js"; import { patchUseCacheIO } from "../patches/plugins/use-cache.js"; -import { normalizePath } from "../utils/index.js"; interface CodeCustomization { // These patches are meant to apply on user and next generated code diff --git a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts index 116cb010..f4e80b0f 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/dynamic-requires.ts @@ -6,7 +6,7 @@ import { patchCode, type RuleConfig } from "@opennextjs/aws/build/patch/astCodeP import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; -import { normalizePath } from "../../utils/normalize-path.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; async function getPagesManifests(serverDir: string): Promise { try { diff --git a/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts b/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts index 2bfc1f77..3796699f 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/instrumentation.ts @@ -6,7 +6,7 @@ import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; -import { normalizePath } from "../../utils/normalize-path.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; export function patchInstrumentation(updater: ContentUpdater, buildOpts: BuildOptions): Plugin { const builtInstrumentationPath = getBuiltInstrumentationPath(buildOpts); diff --git a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts index 7b831c8e..f31c4122 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/load-manifest.ts @@ -13,7 +13,7 @@ import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-upd import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; import { glob } from "glob"; -import { normalizePath } from "../../utils/normalize-path.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; export function inlineLoadManifest(updater: ContentUpdater, buildOpts: BuildOptions): Plugin { return updater.updateContent("inline-load-manifest", [ diff --git a/packages/cloudflare/src/cli/build/patches/plugins/next-server.ts b/packages/cloudflare/src/cli/build/patches/plugins/next-server.ts index 5b4ff247..6bac7c11 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/next-server.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/next-server.ts @@ -14,7 +14,7 @@ import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; -import { normalizePath } from "../../utils/index.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; export function patchNextServer(updater: ContentUpdater, buildOpts: BuildOptions): Plugin { return updater.updateContent("next-server", [ diff --git a/packages/cloudflare/src/cli/build/patches/plugins/route-module.ts b/packages/cloudflare/src/cli/build/patches/plugins/route-module.ts index d0990198..e4727451 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/route-module.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/route-module.ts @@ -13,7 +13,7 @@ import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; import type { ContentUpdater, Plugin } from "@opennextjs/aws/plugins/content-updater.js"; import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js"; -import { normalizePath } from "../../utils/index.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; export function patchRouteModules(updater: ContentUpdater, buildOpts: BuildOptions): Plugin { return updater.updateContent("route-module", [ diff --git a/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts b/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts index 74b0405c..ce7a3429 100644 --- a/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts +++ b/packages/cloudflare/src/cli/build/patches/plugins/wrangler-external.ts @@ -18,7 +18,7 @@ import { dirname, resolve } from "node:path"; import type { PluginBuild } from "esbuild"; -import { normalizePath } from "../../utils/normalize-path.js"; +import { normalizePath } from "../../../utils/normalize-path.js"; export function setWranglerExternal() { return { diff --git a/packages/cloudflare/src/cli/build/utils/create-config-files.ts b/packages/cloudflare/src/cli/build/utils/create-config-files.ts deleted file mode 100644 index 0f706155..00000000 --- a/packages/cloudflare/src/cli/build/utils/create-config-files.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; - -import { getPackageTemplatesDirPath } from "../../../utils/get-package-templates-dir-path.js"; -import type { ProjectOptions } from "../../project-options.js"; -import { askConfirmation } from "../../utils/ask-confirmation.js"; - -/** - * Creates a `wrangler.jsonc` file for the user if a wrangler config file doesn't already exist, - * but only after asking for the user's confirmation. - * - * If the user refuses a warning is shown (which offers ways to opt out of this check to the user). - * - * @param projectOpts The options for the project - */ -export async function createWranglerConfigIfNotExistent(projectOpts: ProjectOptions): Promise { - const possibleExts = ["toml", "json", "jsonc"]; - - const wranglerConfigFileExists = possibleExts.some((ext) => - existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`)) - ); - if (wranglerConfigFileExists) { - return; - } - - const answer = await askConfirmation( - "No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?" - ); - - if (!answer) { - console.warn( - "No Wrangler config file created" + - "\n" + - "(to avoid this check use the `--skipWranglerConfigCheck` flag or set a `SKIP_WRANGLER_CONFIG_CHECK` environment variable to `yes`)" - ); - return; - } - - let wranglerConfig = readFileSync(join(getPackageTemplatesDirPath(), "wrangler.jsonc"), "utf8"); - - const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name"; - - wranglerConfig = wranglerConfig.replaceAll('""', JSON.stringify(appName.replaceAll("_", "-"))); - - const compatDate = await getLatestCompatDate(); - if (compatDate) { - wranglerConfig = wranglerConfig.replace( - /"compatibility_date": "\d{4}-\d{2}-\d{2}"/, - `"compatibility_date": ${JSON.stringify(compatDate)}` - ); - } - - writeFileSync(join(projectOpts.sourceDir, "wrangler.jsonc"), wranglerConfig); -} - -function getAppNameFromPackageJson(sourceDir: string): string | undefined { - try { - const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8"); - const packageJson: Record = JSON.parse(packageJsonStr); - if (typeof packageJson.name === "string") return packageJson.name; - } catch { - /* empty */ - } -} - -export async function getLatestCompatDate(): Promise { - try { - const resp = await fetch(`https://registry.npmjs.org/workerd`); - const latestWorkerdVersion = ( - (await resp.json()) as { - "dist-tags": { latest: string }; - } - )["dist-tags"].latest; - - // The format of the workerd version is `major.yyyymmdd.patch`. - const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/); - - if (match) { - const [, year, month, date] = match; - const compatDate = `${year}-${month}-${date}`; - - return compatDate; - } - } catch { - /* empty */ - } -} - -/** - * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation. - * - * If the user refuses an error is thrown (since the file is mandatory). - * - * @param sourceDir The source directory for the project - * @return The path to the created source file - */ -export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise { - const openNextConfigPath = join(sourceDir, "open-next.config.ts"); - - if (!existsSync(openNextConfigPath)) { - const answer = await askConfirmation( - "Missing required `open-next.config.ts` file, do you want to create one?" - ); - - if (!answer) { - throw new Error("The `open-next.config.ts` file is required, aborting!"); - } - - cpSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), openNextConfigPath); - } - - return openNextConfigPath; -} diff --git a/packages/cloudflare/src/cli/build/utils/extract-project-env-vars.spec.ts b/packages/cloudflare/src/cli/build/utils/extract-project-env-vars.spec.ts deleted file mode 100644 index 0b65b743..00000000 --- a/packages/cloudflare/src/cli/build/utils/extract-project-env-vars.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { appendFileSync, writeFileSync } from "node:fs"; - -import { BuildOptions } from "@opennextjs/aws/build/helper.js"; -import mockFs from "mock-fs"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; - -import { extractProjectEnvVars } from "./extract-project-env-vars.js"; - -const options = { monorepoRoot: "", appPath: "" } as BuildOptions; - -describe("extractProjectEnvVars", () => { - beforeEach(() => { - mockFs({ - ".env": "ENV_VAR=value", - ".env.local": "ENV_LOCAL_VAR=value", - ".env.test": "ENV_TEST_VAR=value", - ".env.test.local": "ENV_TEST_LOCAL_VAR=value", - ".env.development": "ENV_DEV_VAR=value", - ".env.development.local": "ENV_DEV_LOCAL_VAR=value", - ".env.production": "ENV_PROD_VAR=value", - ".env.production.local": "ENV_PROD_LOCAL_VAR=value", - }); - }); - - afterEach(() => mockFs.restore()); - - it("should extract production env vars", () => { - const result = extractProjectEnvVars("production", options); - expect(result).toEqual({ - ENV_LOCAL_VAR: "value", - ENV_PROD_LOCAL_VAR: "value", - ENV_PROD_VAR: "value", - ENV_VAR: "value", - }); - }); - - it("should extract development env vars", () => { - writeFileSync(".dev.vars", 'NEXTJS_ENV = "development"'); - - const result = extractProjectEnvVars("development", options); - expect(result).toEqual({ - ENV_LOCAL_VAR: "value", - ENV_DEV_LOCAL_VAR: "value", - ENV_DEV_VAR: "value", - ENV_VAR: "value", - }); - }); - - it("should override env vars with those in a local file", () => { - writeFileSync(".env.production.local", "ENV_PROD_VAR=overridden"); - - const result = extractProjectEnvVars("production", options); - expect(result).toEqual({ - ENV_LOCAL_VAR: "value", - ENV_PROD_VAR: "overridden", - ENV_VAR: "value", - }); - }); - - it("should support referencing variables", () => { - appendFileSync(".env.production.local", "\nENV_PROD_LOCAL_VAR_REF=$ENV_PROD_LOCAL_VAR"); - - const result = extractProjectEnvVars("production", options); - expect(result).toEqual({ - ENV_LOCAL_VAR: "value", - ENV_PROD_LOCAL_VAR: "value", - ENV_PROD_LOCAL_VAR_REF: "value", - ENV_PROD_VAR: "value", - ENV_VAR: "value", - }); - }); - - it("should exclude .env.local files when extracting test env vars", () => { - const result = extractProjectEnvVars("test", options); - expect(result).toEqual({ - ENV_TEST_LOCAL_VAR: "value", - ENV_TEST_VAR: "value", - ENV_VAR: "value", - }); - }); -}); diff --git a/packages/cloudflare/src/cli/build/utils/index.ts b/packages/cloudflare/src/cli/build/utils/index.ts deleted file mode 100644 index 869bbe6b..00000000 --- a/packages/cloudflare/src/cli/build/utils/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./copy-package-cli-files.js"; -export * from "./create-config-files.js"; -export * from "./ensure-cf-config.js"; -export * from "./extract-project-env-vars.js"; -export * from "./needs-experimental-react.js"; -export * from "./normalize-path.js"; diff --git a/packages/cloudflare/src/cli/build/utils/needs-experimental-react.ts b/packages/cloudflare/src/cli/build/utils/needs-experimental-react.ts index 84406ed2..dad47860 100644 --- a/packages/cloudflare/src/cli/build/utils/needs-experimental-react.ts +++ b/packages/cloudflare/src/cli/build/utils/needs-experimental-react.ts @@ -1,8 +1,5 @@ import type { NextConfig } from "@opennextjs/aws/types/next-types.js"; -// Not sure if this should be upstreamed to aws -// Adding more stuff there make typing incorrect actually, these properties are never undefined as long as it is the right version of next -// Ideally we'd have different `NextConfig` types for different versions of next interface ExtendedNextConfig extends NextConfig { experimental: { ppr?: boolean; @@ -12,7 +9,6 @@ interface ExtendedNextConfig extends NextConfig { }; } -// Copied from https://github.com/vercel/next.js/blob/4518bc91641a0fd938664b781e12ae7c145f3396/packages/next/src/lib/needs-experimental-react.ts#L3-L6 export function needsExperimentalReact(nextConfig: ExtendedNextConfig) { const { ppr, taint, viewTransition } = nextConfig.experimental || {}; return Boolean(ppr || taint || viewTransition); diff --git a/packages/cloudflare/src/cli/commands/build.spec.ts b/packages/cloudflare/src/cli/commands/build.spec.ts new file mode 100644 index 00000000..1e1572b9 --- /dev/null +++ b/packages/cloudflare/src/cli/commands/build.spec.ts @@ -0,0 +1,100 @@ +import logger from "@opennextjs/aws/logger.js"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import { askConfirmation } from "../utils/ask-confirmation.js"; +import { createWranglerConfigFile } from "../utils/create-wrangler-config.js"; + +import { buildCommand } from "./build.js"; + +// Mock logger +vi.mock("@opennextjs/aws/logger.js", () => ({ + default: { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + setLevel: vi.fn(), + }, +})); + +// Mock node:module to prevent require.resolve from failing in tests +vi.mock("node:module", () => ({ + createRequire: vi.fn(() => ({ + resolve: vi.fn(() => "/mocked/adapter.js"), + })), +})); + +// Mock build implementation +vi.mock("../build/build.js", () => ({ + build: vi.fn(), +})); + +// Mock askConfirmation +vi.mock("../utils/ask-confirmation.js", () => ({ + askConfirmation: vi.fn(), +})); + +// Mock create-wrangler-config: findWranglerConfig returns undefined (no config found) +vi.mock("../utils/create-wrangler-config.js", () => ({ + findWranglerConfig: vi.fn(() => undefined), + createWranglerConfigFile: vi.fn(async () => ({ cachingEnabled: false })), +})); + +// Mock utils +vi.mock("./utils/utils.js", () => ({ + compileConfig: vi.fn(async () => ({ config: {}, buildDir: "" })), + getNormalizedOptions: vi.fn(() => ({})), + readWranglerConfig: vi.fn(async () => ({})), + printHeaders: vi.fn(), + nextAppDir: "/test", + withWranglerOptions: vi.fn(), + withWranglerPassthroughArgs: vi.fn(), +})); + +const defaultArgs = { + skipNextBuild: false, + noMinify: false, + skipWranglerConfigCheck: false, + openNextConfigPath: undefined, + dangerouslyUseUnsupportedNextVersion: false, + wranglerArgs: [], + wranglerConfigPath: undefined, + env: undefined, +}; + +describe("buildCommand", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should create wrangler config when user confirms", async () => { + vi.mocked(askConfirmation).mockResolvedValue(true); + + await buildCommand(defaultArgs); + + expect(askConfirmation).toHaveBeenCalledOnce(); + expect(vi.mocked(askConfirmation).mock.calls[0]).toMatchInlineSnapshot(` + [ + "No \`wrangler.(toml|json|jsonc)\` config file found, do you want to create one?", + ] + `); + expect(createWranglerConfigFile).toHaveBeenCalledOnce(); + expect(logger.warn).not.toHaveBeenCalled(); + }); + + it("should warn when user declines wrangler config creation", async () => { + vi.mocked(askConfirmation).mockResolvedValue(false); + + await buildCommand(defaultArgs); + + expect(askConfirmation).toHaveBeenCalledOnce(); + expect(createWranglerConfigFile).not.toHaveBeenCalled(); + expect(vi.mocked(logger.warn).mock.calls[0]).toMatchInlineSnapshot(` + [ + "No Wrangler config file created + + (to avoid this check use the \`--skipWranglerConfigCheck\` flag or set a \`SKIP_WRANGLER_CONFIG_CHECK\` environment variable to \`yes\`)", + ] + `); + }); +}); diff --git a/packages/cloudflare/src/cli/commands/build.ts b/packages/cloudflare/src/cli/commands/build.ts index 0c1ca544..79fee57b 100644 --- a/packages/cloudflare/src/cli/commands/build.ts +++ b/packages/cloudflare/src/cli/commands/build.ts @@ -1,11 +1,13 @@ import { createRequire } from "node:module"; +import logger from "@opennextjs/aws/logger.js"; import type yargs from "yargs"; import { build as buildImpl } from "../build/build.js"; -import { createWranglerConfigIfNotExistent } from "../build/utils/index.js"; +import { askConfirmation } from "../utils/ask-confirmation.js"; +import { createWranglerConfigFile, findWranglerConfig } from "../utils/create-wrangler-config.js"; -import type { WithWranglerArgs } from "./utils.js"; +import type { WithWranglerArgs } from "./utils/utils.js"; import { compileConfig, getNormalizedOptions, @@ -13,14 +15,14 @@ import { printHeaders, withWranglerOptions, withWranglerPassthroughArgs, -} from "./utils.js"; +} from "./utils/utils.js"; /** * Implementation of the `opennextjs-cloudflare build` command. * * @param args */ -async function buildCommand( +export async function buildCommand( args: WithWranglerArgs<{ skipNextBuild: boolean; noMinify: boolean; @@ -42,7 +44,16 @@ async function buildCommand( // Note: We don't ask when a custom config file is specified via `--config` // nor when `--skipWranglerConfigCheck` is used. if (!projectOpts.wranglerConfigPath && !args.skipWranglerConfigCheck) { - await createWranglerConfigIfNotExistent(projectOpts); + if (!findWranglerConfig(projectOpts.sourceDir)) { + const confirmCreate = "No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?"; + if (await askConfirmation(confirmCreate)) { + await createWranglerConfigFile(projectOpts.sourceDir); + } else { + logger.warn(`No Wrangler config file created + +(to avoid this check use the \`--skipWranglerConfigCheck\` flag or set a \`SKIP_WRANGLER_CONFIG_CHECK\` environment variable to \`yes\`)`); + } + } } await buildImpl(options, projectOpts); @@ -55,7 +66,7 @@ async function buildCommand( */ export function addBuildCommand(y: T) { return y.command( - "build", + "build [args..]", "Build an OpenNext Cloudflare worker", (c) => withWranglerOptions(c) diff --git a/packages/cloudflare/src/cli/commands/deploy.ts b/packages/cloudflare/src/cli/commands/deploy.ts index 851c9ba5..0ca121b6 100644 --- a/packages/cloudflare/src/cli/commands/deploy.ts +++ b/packages/cloudflare/src/cli/commands/deploy.ts @@ -1,19 +1,20 @@ +import logger from "@opennextjs/aws/logger.js"; import type yargs from "yargs"; import { DEPLOYMENT_MAPPING_ENV_NAME } from "../templates/skew-protection.js"; -import { runWrangler } from "../utils/run-wrangler.js"; -import { getEnvFromPlatformProxy, quoteShellMeta } from "./helpers.js"; import { populateCache, withPopulateCacheOptions } from "./populate-cache.js"; import { getDeploymentMapping } from "./skew-protection.js"; -import type { WithWranglerArgs } from "./utils.js"; +import { getEnvFromPlatformProxy, quoteShellMeta } from "./utils/helpers.js"; +import { runWrangler } from "./utils/run-wrangler.js"; +import type { WithWranglerArgs } from "./utils/utils.js"; import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerPassthroughArgs, -} from "./utils.js"; +} from "./utils/utils.js"; /** * Implementation of the `opennextjs-cloudflare deploy` command. @@ -46,7 +47,7 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu const deploymentMapping = await getDeploymentMapping(buildOpts, config, envVars); - runWrangler( + const result = runWrangler( buildOpts, [ "deploy", @@ -58,14 +59,15 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu { logging: "all", env: { - // If we are running the deploy command we set this OPEN_NEXT_DEPLOY environment variable - // to let `wrangler deploy` know that it is being run from open-next. We do this because - // otherwise `wrangler deploy` run in an open-next project would call - // `opennextjs-cloudflare deploy` (thus causing an unwanted recursion). OPEN_NEXT_DEPLOY: "true", }, } ); + + if (!result.success) { + logger.error(`Wrangler deploy command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } } /** @@ -75,7 +77,7 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu */ export function addDeployCommand(y: T) { return y.command( - "deploy", + "deploy [args..]", "Deploy a built OpenNext app to Cloudflare Workers", (c) => withPopulateCacheOptions(c), (args) => deployCommand(withWranglerPassthroughArgs(args)) diff --git a/packages/cloudflare/src/cli/commands/helpers.ts b/packages/cloudflare/src/cli/commands/helpers.ts deleted file mode 100644 index 85b6a0ec..00000000 --- a/packages/cloudflare/src/cli/commands/helpers.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { type BuildOptions } from "@opennextjs/aws/build/helper.js"; -import { getPlatformProxy, type GetPlatformProxyOptions } from "wrangler"; - -import { extractProjectEnvVars } from "../build/utils/extract-project-env-vars.js"; - -export type WorkerEnvVar = Record; - -/** - * Returns the env vars to use by the CLI. - * - * The environments variables are returned from a combination of `process.env`, wrangler config, and `.env*` files. - * - * Recommended usage on CI: - * - * - Add you secrets to `process.env` (i.e. `CF_ACCOUNT_ID`) - * - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`) - * - * Note: `.dev.vars*` and `.env*` should not be checked in. - * - * Recommended usage for local dev: - * - * - Add you secrets to either a `.dev.vars*` or `.env*` file (i.e. `CF_ACCOUNT_ID`) - * - Add public values to the wrangler config `wrangler.jsonc` (i.e. `R2_CACHE_PREFIX_ENV_NAME`) - * - * Note: `.env*` files are also used by `next dev` while `.dev.var*` files are only loaded by `wrangler`. - * - * Loading details: - * - * 1. The variables are first initialized from `process.env` - * 2. They are then augmented/replaced with variables from the wrangler config (`wrangler.jsonc` and `.dev.vars*`) - * 3. They are then augmented with variables from `.env*` files (existing values are not replaced). - * - * @param options Options to pass to `getPlatformProxy`, i.e. to set the environment - * @param buildOpts Open Next build options - * @returns the env vars - */ -export async function getEnvFromPlatformProxy(options: GetPlatformProxyOptions, buildOpts: BuildOptions) { - // 1. Start from `process.env` - const envVars = process.env; - - // 2. Apply vars from workers `env` - const proxy = await getPlatformProxy({ - ...options, - // Next.js uses a different mechanism to load `.env*` files from wrangler. - // We prevent wrangler for loading the files and handle that in `getEnvFromPlatformProxy`. - envFiles: [], - }); - - Object.entries(proxy.env).forEach(([key, value]) => { - if (typeof value === "string") { - // filter out bindings by only considering string values - envVars[key as keyof CloudflareEnv] = value; - } - }); - - await proxy.dispose(); - - // 3. Apply new vars from `.env*` files - let mode: "production" | "development" | "test" = "production"; - if (envVars.NEXTJS_ENV === "development") { - mode = "development"; - } else if (envVars.NEXTJS_ENV === "test") { - mode = "test"; - } - - const dotEnvVars = extractProjectEnvVars(mode, buildOpts); - - for (const varName in dotEnvVars) { - envVars[varName] ??= dotEnvVars[varName]; - } - - return envVars as unknown as WorkerEnvVar; -} - -/** - * Escape shell metacharacters. - * - * When `spawnSync` is invoked with `shell: true`, metacharacters need to be escaped. - * - * Based on https://github.com/ljharb/shell-quote/blob/main/quote.js - * - * @param arg - * @returns escaped arg - */ -export function quoteShellMeta(arg: string) { - if (process.platform === "win32") { - if (arg.length === 0) { - return '""'; - } - const needsEscaping = /[&|<>^()%!"]/; - const needsQuotes = /\s/.test(arg) || needsEscaping.test(arg); - let escaped = arg.replace(/"/g, '""'); - if (/[&|<>^()%!]/.test(arg)) { - escaped = escaped.replace(/[&|<>^()%!]/g, "^$&"); - } - return needsQuotes ? `"${escaped}"` : escaped; - } - if (/["\s]/.test(arg) && !/'/.test(arg)) { - return `'${arg.replace(/(['\\])/g, "\\$1")}'`; - } - if (/["'\s]/.test(arg)) { - return `"${arg.replace(/(["\\$`!])/g, "\\$1")}"`; - } - return arg.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); -} diff --git a/packages/cloudflare/src/cli/commands/migrate.ts b/packages/cloudflare/src/cli/commands/migrate.ts new file mode 100644 index 00000000..40e9110c --- /dev/null +++ b/packages/cloudflare/src/cli/commands/migrate.ts @@ -0,0 +1,343 @@ +import assert from "node:assert"; +import childProcess from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; + +import { + checkRunningInsideNextjsApp, + findNextConfig, + findPackagerAndRoot, + getNextVersion, +} from "@opennextjs/aws/build/helper.js"; +import logger from "@opennextjs/aws/logger.js"; +import type yargs from "yargs"; + +import { askConfirmation } from "../utils/ask-confirmation.js"; +import { createOpenNextConfigFile, findOpenNextConfig } from "../utils/create-open-next-config.js"; +import { createWranglerConfigFile, findWranglerConfig } from "../utils/create-wrangler-config.js"; +import { ensureNextjsVersionSupported } from "../utils/nextjs-support.js"; + +import { conditionalAppendFileSync } from "./utils/files.js"; +import { printHeaders } from "./utils/utils.js"; + +/** + * Implementation of the `opennextjs-cloudflare migrate` command. + * + * @param args + */ +async function migrateCommand(args: { forceInstall: boolean }): Promise { + printHeaders("migrate"); + + logger.info("🚀 Setting up the OpenNext Cloudflare adapter...\n"); + + const projectDir = process.cwd(); + + const nextConfigFileCreated = await maybeCreateNextConfigFileIfMissing(projectDir, args.forceInstall).catch( + (e) => { + logger.error(`${e instanceof Error ? e.message : e}\n`); + process.exit(1); + } + ); + + if (nextConfigFileCreated === false) { + logger.error("The next.config file is required, aborting!\n"); + process.exit(1); + } + + checkRunningInsideNextjsApp({ appPath: projectDir }); + + const wranglerConfigFilePath = findWranglerConfig(projectDir); + if (wranglerConfigFilePath) { + logger.error( + `The project already contains a Wrangler config file (at ${wranglerConfigFilePath}).\n` + + "This means that your application is either a static site or a next-on-pages project.\n" + + "If your project is a static site and you want to migrate to OpenNext, delete the Wrangler configuration file, convert the project to a full stack one and try again." + + " if your project is a next-on-pages one remove any next-on-pages configuration, any edge runtime usage and try again." + ); + process.exit(1); + } + + if (findOpenNextConfig(projectDir)) { + logger.info( + `Exiting since the project is already configured for OpenNext (an \`open-next.config.ts\` file already exists)\n` + ); + return; + } + + const { packager } = findPackagerAndRoot(projectDir); + const packageManager = packageManagers[packager]; + + printStepTitle("Installing dependencies"); + try { + const forceFlag = args.forceInstall ? " --force" : ""; + childProcess.execSync(`${packageManager.install}${forceFlag} @opennextjs/cloudflare@latest`, { + stdio: "inherit", + }); + childProcess.execSync(`${packageManager.installDev}${forceFlag} wrangler@latest`, { stdio: "inherit" }); + } catch (error) { + logger.error("Failed to install dependencies:", (error as Error).message); + process.exit(1); + } + + printStepTitle("Creating wrangler.jsonc"); + const { cachingEnabled } = await createWranglerConfigFile("./"); + + if (!cachingEnabled) { + logger.warn( + `Failed to set up cache for your project.\n` + + `After the migration completes, please manually setup cache in wrangler.jsonc and open-next.config.ts files (for more details see: https://opennext.js.org/cloudflare/caching).\n` + ); + } + + printStepTitle("Creating open-next.config.ts"); + createOpenNextConfigFile("./", { cache: cachingEnabled }); + + const devVarsExists = fs.existsSync(".dev.vars"); + printStepTitle(`${devVarsExists ? "Updating" : "Creating"} .dev.vars file`); + conditionalAppendFileSync(".dev.vars", "NEXTJS_ENV=development\n", { + appendIf: (content) => !/\bNEXTJS_ENV\b/.test(content), + appendPrefix: "\n", + }); + + printStepTitle(`${fs.existsSync("public/_headers") ? "Updating" : "Creating"} public/_headers file`); + conditionalAppendFileSync( + "public/_headers", + "# https://developers.cloudflare.com/workers/static-assets/headers\n" + + "# https://opennext.js.org/cloudflare/caching#static-assets-caching\n" + + "/_next/static/*\n" + + " Cache-Control: public,max-age=31536000,immutable\n", + { + appendIf: (content) => !/^\/_next\/static\/*\b/.test(content), + appendPrefix: "\n\n", + } + ); + + printStepTitle("Updating package.json scripts"); + const openNextScripts = { + preview: "opennextjs-cloudflare build && opennextjs-cloudflare preview", + deploy: "opennextjs-cloudflare build && opennextjs-cloudflare deploy", + upload: "opennextjs-cloudflare build && opennextjs-cloudflare upload", + ["cf-typegen"]: "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", + }; + try { + let packageJson: { scripts?: Record } = {}; + if (fs.existsSync("package.json")) { + packageJson = JSON.parse(fs.readFileSync("package.json", "utf8")) as { + scripts?: Record; + }; + } + + packageJson.scripts = { + build: "next build", + ...packageJson.scripts, + ...openNextScripts, + }; + + fs.writeFileSync("package.json", JSON.stringify(packageJson, null, 2)); + } catch (error) { + logger.error("Failed to update package.json", (error as Error).message); + logger.warn( + "\nPlease ensure that your package.json contains the following scripts:\n" + + console.log( + Object.entries(openNextScripts) + .map(([key, value]) => ` - ${key}: ${value}`) + .join("\n") + ) + + "\n" + ); + } + + const gitIgnoreExists = fs.existsSync(".gitignore"); + printStepTitle(`${gitIgnoreExists ? "Updating" : "Creating"} .gitignore file`); + conditionalAppendFileSync(".gitignore", "# OpenNext\n.open-next\n", { + appendIf: (content) => !content.includes(".open-next"), + appendPrefix: "\n", + }); + + const nextConfig = findNextConfig({ appPath: projectDir }); + + // At this point the next config file should exist (it either + // was part of the original project or we've created it) + assert(nextConfig, "Next config file unexpectedly missing"); + + printStepTitle("Updating Next.js config"); + conditionalAppendFileSync( + nextConfig.path, + "import('@opennextjs/cloudflare').then(m => m.initOpenNextCloudflareForDev());\n", + { + appendIf: (content) => !content.includes("initOpenNextCloudflareForDev"), + appendPrefix: "\n", + } + ); + + printStepTitle("Checking for edge runtime usage"); + try { + const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts"]; + const files = findFilesRecursive(projectDir, extensions); + let foundEdgeRuntime = false; + + for (const file of files) { + try { + const content = fs.readFileSync(file, "utf8"); + if (content.includes('export const runtime = "edge"')) { + logger.warn(`Found edge runtime in: ${file}`); + foundEdgeRuntime = true; + break; + } + } catch { + // Skip files that can't be read + } + } + + if (foundEdgeRuntime) { + logger.warn( + "Detected usage of the edge runtime.\n" + + "The edge runtime is not supported yet with @opennextjs/cloudflare.\n" + + 'Remove all the `export const runtime = "edge";` lines from your source files' + ); + } + } catch { + logger.warn( + "Failed to check for edge runtime usage.\n" + + "The edge runtime is not supported yet with @opennextjs/cloudflare.\n" + + 'If present, remove all the `export const runtime = "edge";` lines from your source files' + ); + } + + logger.info( + "🎉 OpenNext Cloudflare adapter complete!\n" + + "\nNext steps:\n" + + `- Run: "${packageManager.run} preview" to build and preview your Cloudflare application locally\n` + + `- Run: "${packageManager.run} deploy" to deploy your application to Cloudflare Workers\n` + + (cachingEnabled + ? "" + : `- ⚠️ Setup cache, see https://opennext.js.org/cloudflare/caching for more details\n`) + ); +} + +interface PackageManager { + name: string; + install: string; + installDev: string; + run: string; +} + +const packageManagers = { + pnpm: { name: "pnpm", install: "pnpm add", installDev: "pnpm add -D", run: "pnpm run" }, + npm: { name: "npm", install: "npm install", installDev: "npm install --save-dev", run: "npm run" }, + bun: { name: "bun", install: "bun add", installDev: "bun add -D", run: "bun" }, + yarn: { name: "yarn", install: "yarn add", installDev: "yarn add -D", run: "yarn" }, +} satisfies Record; + +/** + * Recursively searches a directory for files with specified extensions. + * + * Skips common build/cache directories: node_modules, .next, .open-next, .git, dist, build. + * + * @param dir - The directory path to start searching from + * @param extensions - Array of file extensions to match + * @param fileList - Accumulator array for found files (used internally for recursion) + * @returns Array of file paths matching the specified extensions + */ +function findFilesRecursive(dir: string, extensions: string[], fileList: string[] = []): string[] { + const files = fs.readdirSync(dir); + + files.forEach((file) => { + const filePath = path.join(dir, file); + const stat = fs.statSync(filePath); + + if (stat.isDirectory()) { + // Skip node_modules, .next, .open-next, and other common build/cache directories + if (!["node_modules", ".next", ".open-next", ".git", "dist", "build"].includes(file)) { + findFilesRecursive(filePath, extensions, fileList); + } + } else if (stat.isFile()) { + const ext = path.extname(file).toLowerCase(); + if (extensions.includes(ext)) { + fileList.push(filePath); + } + } + }); + + return fileList; +} + +function printStepTitle(title: string): void { + logger.info(`⚙️ ${title}...\n`); +} + +/** + * Creates a plain next.config.ts file + * + * @param appDir The directory where the config file should be created + */ +function createNextConfigFile(appDir: string): void { + const nextConfigPath = path.join(appDir, "next.config.ts"); + const content = `import type { NextConfig } from "next"; + +const nextConfig: NextConfig = {}; + +export default nextConfig; +`; + fs.writeFileSync(nextConfigPath, content); +} + +/** + * Creates a next.config.ts file, after asking for the user's confirmation, if missing in the project's directory. + * + * To be safe, this function also ensures that the "next" package is installed and its version is compatible with OpenNext. + * + * @param projectDir The project directory to check + * @param skipNextVersionCheck Whether to bypass the "next" version compatibility check + * @returns A boolean representing whether the user has accepter the creation of the config file, undefined if the file already existed + * @throws {Error} If "next" is not installed or the Next.js version is incompatible with open-next + */ +async function maybeCreateNextConfigFileIfMissing( + projectDir: string, + skipNextVersionCheck: boolean +): Promise { + if (findNextConfig({ appPath: projectDir })) { + return; + } + + let nextVersion: string; + try { + nextVersion = getNextVersion(projectDir); + } catch { + throw new Error( + "This does not appear to be a Next.js application. The 'next' package is not installed and no next.config file was found." + ); + } + + if (!skipNextVersionCheck) { + await ensureNextjsVersionSupported({ nextVersion }); + } + + const answer = await askConfirmation("Missing required next.config file. Do you want to create one?"); + + if (!answer) { + return false; + } + + createNextConfigFile(projectDir); + logger.info("Created next.config.ts\n"); + return true; +} + +/** + * Add the `migrate` command to yargs configuration. + */ +export function addMigrateCommand(y: T) { + return y.command( + "migrate", + "Set up the OpenNext Cloudflare adapter in an existing Next.js project", + (args) => + args.option("forceInstall", { + type: "boolean", + alias: "f", + desc: "Install the dependencies using the `--force` flag.", + default: false, + }), + (args) => migrateCommand(args) + ); +} diff --git a/packages/cloudflare/src/cli/commands/populate-cache.spec.ts b/packages/cloudflare/src/cli/commands/populate-cache.spec.ts index 55f72018..849f7eae 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.spec.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.spec.ts @@ -69,11 +69,11 @@ describe("getCacheAssets", () => { }); }); -vi.mock("../utils/run-wrangler.js", () => ({ - runWrangler: vi.fn(), +vi.mock("./utils/run-wrangler.js", () => ({ + runWrangler: vi.fn(() => ({ success: true, stdout: "", stderr: "" })), })); -vi.mock("./helpers.js", () => ({ +vi.mock("./utils/helpers.js", () => ({ getEnvFromPlatformProxy: vi.fn(async () => ({})), quoteShellMeta: vi.fn((s) => s), })); @@ -103,7 +103,7 @@ describe("populateCache", () => { }); test(target, async () => { - const { runWrangler } = await import("../utils/run-wrangler.js"); + const { runWrangler } = await import("./utils/run-wrangler.js"); setupMockFileSystem(); vi.mocked(runWrangler).mockClear(); @@ -139,7 +139,7 @@ describe("populateCache", () => { }); test(`${target} using jurisdiction`, async () => { - const { runWrangler } = await import("../utils/run-wrangler.js"); + const { runWrangler } = await import("./utils/run-wrangler.js"); setupMockFileSystem(); vi.mocked(runWrangler).mockClear(); diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts index 6555b6d7..496b6e5c 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.ts @@ -36,12 +36,12 @@ import { BINDING_NAME as D1_TAG_BINDING_NAME, NAME as D1_TAG_NAME, } from "../../api/overrides/tag-cache/d1-next-tag-cache.js"; -import { normalizePath } from "../build/utils/normalize-path.js"; -import type { WranglerTarget } from "../utils/run-wrangler.js"; -import { runWrangler } from "../utils/run-wrangler.js"; +import { normalizePath } from "../utils/normalize-path.js"; -import { getEnvFromPlatformProxy, quoteShellMeta, type WorkerEnvVar } from "./helpers.js"; -import type { WithWranglerArgs } from "./utils.js"; +import { getEnvFromPlatformProxy, quoteShellMeta, type WorkerEnvVar } from "./utils/helpers.js"; +import type { WranglerTarget } from "./utils/run-wrangler.js"; +import { runWrangler } from "./utils/run-wrangler.js"; +import type { WithWranglerArgs } from "./utils/utils.js"; import { getNormalizedOptions, printHeaders, @@ -49,7 +49,7 @@ import { retrieveCompiledConfig, withWranglerOptions, withWranglerPassthroughArgs, -} from "./utils.js"; +} from "./utils/utils.js"; /** * Implementation of the `opennextjs-cloudflare populateCache` command. @@ -248,7 +248,7 @@ async function populateR2IncrementalCache( const concurrency = Math.max(1, populateCacheOptions.cacheChunkSize ?? 50); const jurisdiction = binding.jurisdiction ? `--jurisdiction ${binding.jurisdiction}` : ""; - runWrangler( + const result = runWrangler( buildOpts, [ "r2 bulk put", @@ -260,8 +260,6 @@ async function populateR2IncrementalCache( { target: populateCacheOptions.target, configPath: populateCacheOptions.wranglerConfigPath, - // R2 does not support the environment flag and results in the following error: - // Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'. environment: undefined, logging: "error", } @@ -269,6 +267,11 @@ async function populateR2IncrementalCache( fs.rmSync(listFile, { force: true }); + if (!result.success) { + logger.error(`Wrangler r2 bulk put command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } + logger.info(`Successfully populated cache with ${assets.length} assets`); } @@ -314,7 +317,7 @@ async function populateKVIncrementalCache( fs.writeFileSync(chunkPath, JSON.stringify(kvMapping)); - runWrangler( + const result = runWrangler( buildOpts, [ "kv bulk put", @@ -331,6 +334,11 @@ async function populateKVIncrementalCache( ); fs.rmSync(chunkPath, { force: true }); + + if (!result.success) { + logger.error(`Wrangler kv bulk put command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } } logger.info(`Successfully populated cache with ${assets.length} assets`); @@ -350,7 +358,7 @@ function populateD1TagCache( throw new Error(`No D1 binding ${JSON.stringify(D1_TAG_BINDING_NAME)} found!`); } - runWrangler( + const result = runWrangler( buildOpts, [ "d1 execute", @@ -366,6 +374,11 @@ function populateD1TagCache( } ); + if (!result.success) { + logger.error(`Wrangler d1 execute command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } + logger.info("\nSuccessfully created D1 table"); } @@ -390,13 +403,13 @@ export function addPopulateCacheCommand(y: T) { return y.command("populateCache", "Populate the cache for a built Next.js app", (c) => c .command( - "local", + "local [args..]", "Local dev server cache", (c) => withPopulateCacheOptions(c), (args) => populateCacheCommand("local", withWranglerPassthroughArgs(args)) ) .command( - "remote", + "remote [args..]", "Remote Cloudflare Worker cache", (c) => withPopulateCacheOptions(c), (args) => populateCacheCommand("remote", withWranglerPassthroughArgs(args)) diff --git a/packages/cloudflare/src/cli/commands/preview.ts b/packages/cloudflare/src/cli/commands/preview.ts index 48c6a72c..82502310 100644 --- a/packages/cloudflare/src/cli/commands/preview.ts +++ b/packages/cloudflare/src/cli/commands/preview.ts @@ -1,17 +1,17 @@ +import logger from "@opennextjs/aws/logger.js"; import type yargs from "yargs"; -import { runWrangler } from "../utils/run-wrangler.js"; - -import { getEnvFromPlatformProxy } from "./helpers.js"; import { populateCache, withPopulateCacheOptions } from "./populate-cache.js"; -import type { WithWranglerArgs } from "./utils.js"; +import { getEnvFromPlatformProxy } from "./utils/helpers.js"; +import { runWrangler } from "./utils/run-wrangler.js"; +import type { WithWranglerArgs } from "./utils/utils.js"; import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerPassthroughArgs, -} from "./utils.js"; +} from "./utils/utils.js"; /** * Implementation of the `opennextjs-cloudflare preview` command. @@ -43,7 +43,12 @@ export async function previewCommand( envVars ); - runWrangler(buildOpts, ["dev", ...args.wranglerArgs], { logging: "all" }); + const result = runWrangler(buildOpts, ["dev", ...args.wranglerArgs], { logging: "all" }); + + if (!result.success) { + logger.error(`Wrangler dev command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } } /** @@ -53,7 +58,7 @@ export async function previewCommand( */ export function addPreviewCommand(y: T) { return y.command( - "preview", + "preview [args..]", "Preview a built OpenNext app with a Wrangler dev server", (c) => withPopulateCacheOptions(c).option("remote", { diff --git a/packages/cloudflare/src/cli/commands/skew-protection.ts b/packages/cloudflare/src/cli/commands/skew-protection.ts index a671d0f8..914fcc66 100644 --- a/packages/cloudflare/src/cli/commands/skew-protection.ts +++ b/packages/cloudflare/src/cli/commands/skew-protection.ts @@ -33,7 +33,7 @@ import type { VersionGetResponse } from "cloudflare/resources/workers/scripts/ve import type { OpenNextConfig } from "../../api/index.js"; import { CURRENT_VERSION_ID, DEPLOYMENT_MAPPING_ENV_NAME } from "../templates/skew-protection.js"; -import type { WorkerEnvVar } from "./helpers.js"; +import type { WorkerEnvVar } from "./utils/helpers.js"; /** Maximum number of versions to list */ const MAX_NUMBER_OF_VERSIONS = 20; diff --git a/packages/cloudflare/src/cli/commands/upload.ts b/packages/cloudflare/src/cli/commands/upload.ts index b0111e55..b94d2616 100644 --- a/packages/cloudflare/src/cli/commands/upload.ts +++ b/packages/cloudflare/src/cli/commands/upload.ts @@ -1,19 +1,20 @@ +import logger from "@opennextjs/aws/logger.js"; import type yargs from "yargs"; import { DEPLOYMENT_MAPPING_ENV_NAME } from "../templates/skew-protection.js"; -import { runWrangler } from "../utils/run-wrangler.js"; -import { getEnvFromPlatformProxy, quoteShellMeta } from "./helpers.js"; import { populateCache, withPopulateCacheOptions } from "./populate-cache.js"; import { getDeploymentMapping } from "./skew-protection.js"; -import type { WithWranglerArgs } from "./utils.js"; +import { getEnvFromPlatformProxy, quoteShellMeta } from "./utils/helpers.js"; +import { runWrangler } from "./utils/run-wrangler.js"; +import type { WithWranglerArgs } from "./utils/utils.js"; import { getNormalizedOptions, printHeaders, readWranglerConfig, retrieveCompiledConfig, withWranglerPassthroughArgs, -} from "./utils.js"; +} from "./utils/utils.js"; /** * Implementation of the `opennextjs-cloudflare upload` command. @@ -52,7 +53,7 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu envVars ); - runWrangler( + const result = runWrangler( buildOpts, [ "versions upload", @@ -63,6 +64,11 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu ], { logging: "all" } ); + + if (!result.success) { + logger.error(`Wrangler versions upload command failed${result.stderr ? `:\n${result.stderr}` : ""}`); + process.exit(1); + } } /** @@ -72,7 +78,7 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu */ export function addUploadCommand(y: T) { return y.command( - "upload", + "upload [args..]", "Upload a built OpenNext app to Cloudflare Workers", (c) => withPopulateCacheOptions(c), (args) => uploadCommand(withWranglerPassthroughArgs(args)) diff --git a/packages/cloudflare/src/cli/commands/utils/files.ts b/packages/cloudflare/src/cli/commands/utils/files.ts new file mode 100644 index 00000000..2ca92a0f --- /dev/null +++ b/packages/cloudflare/src/cli/commands/utils/files.ts @@ -0,0 +1,26 @@ +import fs from "node:fs"; +import path from "node:path"; + +export function conditionalAppendFileSync( + filepath: string, + text: string, + { + appendIf = () => true, + appendPrefix = "", + }: { + appendIf?: (fileContent: string) => boolean; + appendPrefix?: string; + } = {} +): void { + const fileExists = fs.existsSync(filepath); + const maybeFileContent = fileExists ? fs.readFileSync(filepath, "utf8") : ""; + + if (!fileExists) { + const dir = path.dirname(filepath); + fs.mkdirSync(dir, { recursive: true }); + } + + if (!fileExists || appendIf(maybeFileContent)) { + fs.appendFileSync(filepath, `${maybeFileContent.length > 0 ? appendPrefix : ""}${text}`); + } +} diff --git a/packages/cloudflare/src/cli/commands/utils/helpers.ts b/packages/cloudflare/src/cli/commands/utils/helpers.ts new file mode 100644 index 00000000..09e35a78 --- /dev/null +++ b/packages/cloudflare/src/cli/commands/utils/helpers.ts @@ -0,0 +1,60 @@ +import { type BuildOptions } from "@opennextjs/aws/build/helper.js"; +import { getPlatformProxy, type GetPlatformProxyOptions } from "wrangler"; + +import { extractProjectEnvVars } from "../../utils/extract-project-env-vars.js"; + +export type WorkerEnvVar = Record; + +export async function getEnvFromPlatformProxy(options: GetPlatformProxyOptions, buildOpts: BuildOptions) { + const envVars = process.env; + + const proxy = await getPlatformProxy({ + ...options, + envFiles: [], + }); + + Object.entries(proxy.env).forEach(([key, value]) => { + if (typeof value === "string") { + envVars[key as keyof CloudflareEnv] = value; + } + }); + + await proxy.dispose(); + + let mode: "production" | "development" | "test" = "production"; + if (envVars.NEXTJS_ENV === "development") { + mode = "development"; + } else if (envVars.NEXTJS_ENV === "test") { + mode = "test"; + } + + const dotEnvVars = extractProjectEnvVars(mode, buildOpts); + + for (const varName in dotEnvVars) { + envVars[varName] ??= dotEnvVars[varName]; + } + + return envVars as unknown as WorkerEnvVar; +} + +export function quoteShellMeta(arg: string) { + if (process.platform === "win32") { + if (arg.length === 0) { + return '""'; + } + const needsEscaping = /[&|<>^()%!"]/; + const needsQuotes = /\s/.test(arg) || needsEscaping.test(arg); + let escaped = arg.replace(/"/g, '""'); + if (/[&|<>^()%!]/.test(arg)) { + escaped = escaped.replace(/[&|<>^()%!]/g, "^$&"); + } + return needsQuotes ? `"${escaped}"` : escaped; + } + if (/["\s]/.test(arg) && !/'/.test(arg)) { + return `'${arg.replace(/(['\\])/g, "\\$1")}'`; + } + if (/["'\s]/.test(arg)) { + return `"${arg.replace(/(["\\$`!])/g, "\\$1")}"`; + } + return arg.replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, "$1\\$2"); +} diff --git a/packages/cloudflare/src/cli/utils/run-wrangler.ts b/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts similarity index 52% rename from packages/cloudflare/src/cli/utils/run-wrangler.ts rename to packages/cloudflare/src/cli/commands/utils/run-wrangler.ts index f6c07cdb..f062abf0 100644 --- a/packages/cloudflare/src/cli/utils/run-wrangler.ts +++ b/packages/cloudflare/src/cli/commands/utils/run-wrangler.ts @@ -2,29 +2,32 @@ import { spawnSync } from "node:child_process"; import { readFileSync } from "node:fs"; import path from "node:path"; -import type { BuildOptions } from "@opennextjs/aws/build/helper.js"; import { compareSemver } from "@opennextjs/aws/build/helper.js"; -import logger from "@opennextjs/aws/logger.js"; + +export type PackagerDetails = { + packager: "npm" | "pnpm" | "yarn" | "bun"; + monorepoRoot: string; +}; export type WranglerTarget = "local" | "remote"; +export type WranglerCommandResult = { + success: boolean; + stdout: string; + stderr: string; +}; + type WranglerOptions = { target?: WranglerTarget; environment?: string; configPath?: string; - logging?: "all" | "error"; + logging?: "all" | "error" | "none"; env?: Record; }; -/** - * Checks the package.json `packageManager` field to determine whether yarn modern is used. - * - * @param options Build options. - * @returns Whether yarn modern is used. - */ -function isYarnModern(options: BuildOptions) { +function isYarnModern(monorepoRoot: string) { const packageJson: { packageManager?: string } = JSON.parse( - readFileSync(path.join(options.monorepoRoot, "package.json"), "utf-8") + readFileSync(path.join(monorepoRoot, "package.json"), "utf8") ); if (!packageJson.packageManager?.startsWith("yarn")) return false; @@ -33,18 +36,8 @@ function isYarnModern(options: BuildOptions) { return version ? compareSemver(version, ">=", "4.0.0") : false; } -/** - * Prepends CLI flags with `--` so that certain package managers can pass args through to wrangler - * properly. - * - * npm and yarn classic require `--` to be used, while pnpm and bun require that it is not used. - * - * @param options Build options. - * @param args CLI args. - * @returns Arguments with a passthrough flag injected when needed. - */ -function injectPassthroughFlagForArgs(options: BuildOptions, args: string[]) { - if (options.packager !== "npm" && (options.packager !== "yarn" || isYarnModern(options))) { +function injectPassthroughFlagForArgs(options: PackagerDetails, args: string[]) { + if (options.packager !== "npm" && (options.packager !== "yarn" || isYarnModern(options.monorepoRoot))) { return args; } @@ -56,7 +49,12 @@ function injectPassthroughFlagForArgs(options: BuildOptions, args: string[]) { return args; } -export function runWrangler(options: BuildOptions, args: string[], wranglerOpts: WranglerOptions = {}) { +export function runWrangler( + options: PackagerDetails, + args: string[], + wranglerOpts: WranglerOptions = {} +): WranglerCommandResult { + const noLogs = wranglerOpts.logging === "none"; const shouldPipeLogs = wranglerOpts.logging === "error"; const result = spawnSync( @@ -77,27 +75,31 @@ export function runWrangler(options: BuildOptions, args: string[], wranglerOpts: ], { shell: true, - stdio: shouldPipeLogs ? ["ignore", "pipe", "pipe"] : "inherit", + stdio: shouldPipeLogs || noLogs ? ["ignore", "pipe", "pipe"] : ["inherit", "inherit", "pipe"], env: { ...process.env, ...wranglerOpts.env, - // `.env` files are handled by the adapter. - // Wrangler would load `.env.` while we should load `.env.` - // See https://opennext.js.org/cloudflare/howtos/env-vars CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV: "false", }, } ); - if (result.status !== 0) { - if (shouldPipeLogs) { - process.stdout.write(result.stdout.toString()); - process.stderr.write(result.stderr.toString()); + const success = result.status === 0; + const stdout = result.stdout?.toString() ?? ""; + const stderr = result.stderr?.toString() ?? ""; + + if (!noLogs) { + if (!shouldPipeLogs && stderr) { + process.stderr.write(stderr); } - logger.error("Wrangler command failed"); - process.exit(1); + if (!success && shouldPipeLogs) { + process.stdout.write(stdout); + process.stderr.write(stderr); + } } + + return { success, stdout, stderr }; } export function isWranglerTarget(v: string | undefined): v is WranglerTarget { diff --git a/packages/cloudflare/src/cli/commands/utils/utils.spec.ts b/packages/cloudflare/src/cli/commands/utils/utils.spec.ts new file mode 100644 index 00000000..3781ee82 --- /dev/null +++ b/packages/cloudflare/src/cli/commands/utils/utils.spec.ts @@ -0,0 +1,126 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +import { askConfirmation } from "../../utils/ask-confirmation.js"; +import { createOpenNextConfigFile, findOpenNextConfig } from "../../utils/create-open-next-config.js"; + +import { compileConfig } from "./utils.js"; + +const { mockExistsSync } = vi.hoisted(() => ({ + mockExistsSync: vi.fn(), +})); + +// Mock node:fs — only override existsSync +vi.mock("node:fs", async (importOriginal) => { + const mod = await importOriginal(); + return { ...mod, existsSync: mockExistsSync }; +}); + +// Mock logger +vi.mock("@opennextjs/aws/logger.js", () => ({ + default: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), setLevel: vi.fn() }, +})); + +// Mock compileOpenNextConfig +const mockCompileOpenNextConfig = vi.fn(async () => ({ + config: { default: {} }, + buildDir: "/build", +})); +vi.mock("@opennextjs/aws/build/compileConfig.js", () => ({ + compileOpenNextConfig: (...args: unknown[]) => mockCompileOpenNextConfig(...args), +})); + +// Mock ensureCloudflareConfig +vi.mock("../../build/utils/ensure-cf-config.js", () => ({ + ensureCloudflareConfig: vi.fn(), +})); + +// Mock askConfirmation +vi.mock("../../utils/ask-confirmation.js", () => ({ + askConfirmation: vi.fn(), +})); + +// Mock create-open-next-config +vi.mock("../../utils/create-open-next-config.js", () => ({ + findOpenNextConfig: vi.fn(), + createOpenNextConfigFile: vi.fn(() => "/test/open-next.config.ts"), +})); + +// Mock wrangler +vi.mock("wrangler", () => ({ + unstable_readConfig: vi.fn(), +})); + +// Mock build utils +vi.mock("@opennextjs/aws/build/utils.js", () => ({ + printHeader: vi.fn(), + showWarningOnWindows: vi.fn(), +})); + +// Mock build helper +vi.mock("@opennextjs/aws/build/helper.js", () => ({ + normalizeOptions: vi.fn(() => ({})), +})); + +describe("compileConfig", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should compile config when configPath is provided and file exists", async () => { + mockExistsSync.mockReturnValue(true); + + const result = await compileConfig("/app/open-next.config.ts"); + + expect(mockCompileOpenNextConfig).toHaveBeenCalledWith("/app/open-next.config.ts", { compileEdge: true }); + expect(result).toEqual({ config: { default: {} }, buildDir: "/build" }); + }); + + it("should throw when configPath is provided but file does not exist", async () => { + mockExistsSync.mockReturnValue(false); + + await expect(compileConfig("/app/missing-config.ts")).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: Custom config file not found at /app/missing-config.ts]` + ); + }); + + it("should compile config when no configPath is provided but one is found", async () => { + vi.mocked(findOpenNextConfig).mockReturnValue("/app/open-next.config.ts"); + + const result = await compileConfig(undefined); + + expect(findOpenNextConfig).toHaveBeenCalledOnce(); + expect(mockCompileOpenNextConfig).toHaveBeenCalledWith("/app/open-next.config.ts", { compileEdge: true }); + expect(result).toEqual({ config: { default: {} }, buildDir: "/build" }); + }); + + it("should create config when no configPath found and user confirms", async () => { + vi.mocked(findOpenNextConfig).mockReturnValue(undefined); + vi.mocked(askConfirmation).mockResolvedValue(true); + + const result = await compileConfig(undefined); + + expect(askConfirmation).toHaveBeenCalledOnce(); + expect(vi.mocked(askConfirmation).mock.calls[0]).toMatchInlineSnapshot(` + [ + "Missing required \`open-next.config.ts\` file, do you want to create one?", + ] + `); + expect(createOpenNextConfigFile).toHaveBeenCalledOnce(); + expect(mockCompileOpenNextConfig).toHaveBeenCalledWith("/test/open-next.config.ts", { + compileEdge: true, + }); + expect(result).toEqual({ config: { default: {} }, buildDir: "/build" }); + }); + + it("should throw when no configPath found and user declines", async () => { + vi.mocked(findOpenNextConfig).mockReturnValue(undefined); + vi.mocked(askConfirmation).mockResolvedValue(false); + + await expect(compileConfig(undefined)).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: The \`open-next.config.ts\` file is required, aborting!]` + ); + + expect(askConfirmation).toHaveBeenCalledOnce(); + expect(createOpenNextConfigFile).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils/utils.ts similarity index 66% rename from packages/cloudflare/src/cli/commands/utils.ts rename to packages/cloudflare/src/cli/commands/utils/utils.ts index 15b40198..0bee952d 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils/utils.ts @@ -10,11 +10,12 @@ import logger from "@opennextjs/aws/logger.js"; import { unstable_readConfig } from "wrangler"; import type yargs from "yargs"; -import type { OpenNextConfig } from "../../api/config.js"; -import { createOpenNextConfigIfNotExistent, ensureCloudflareConfig } from "../build/utils/index.js"; +import type { OpenNextConfig } from "../../../api/config.js"; +import { ensureCloudflareConfig } from "../../build/utils/ensure-cf-config.js"; +import { askConfirmation } from "../../utils/ask-confirmation.js"; +import { createOpenNextConfigFile, findOpenNextConfig } from "../../utils/create-open-next-config.js"; export type WithWranglerArgs = T & { - // Array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. wranglerArgs: string[]; wranglerConfigPath: string | undefined; env: string | undefined; @@ -22,11 +23,6 @@ export type WithWranglerArgs = T & { export const nextAppDir = process.cwd(); -/** - * Print headers and warnings for the CLI. - * - * @param command - */ export function printHeaders(command: string) { printHeader(`Cloudflare ${command}`); @@ -34,23 +30,34 @@ export function printHeaders(command: string) { } /** - * Compile the OpenNext config. - * - * When users do not specify a custom config file (using `--openNextConfigPath`), - * the CLI will offer to create one. + * Compile the OpenNext config file. * * When users specify a custom config file but it doesn't exist, we throw an Error. * + * @throws If a custom config path is provided but the file does not exist. + * @throws If no config file is found and the user declines to create one. + * * @param configPath Optional path to the config file. Absolute or relative to cwd. - * @returns OpenNext config. + * @returns The compiled OpenNext config and the build directory. + * */ export async function compileConfig(configPath: string | undefined) { if (configPath && !existsSync(configPath)) { throw new Error(`Custom config file not found at ${configPath}`); } + configPath ??= findOpenNextConfig(nextAppDir); + if (!configPath) { - configPath = await createOpenNextConfigIfNotExistent(nextAppDir); + const answer = await askConfirmation( + "Missing required `open-next.config.ts` file, do you want to create one?" + ); + + if (!answer) { + throw new Error("The `open-next.config.ts` file is required, aborting!"); + } + + configPath = createOpenNextConfigFile(nextAppDir, { cache: false }); } const { config, buildDir } = await compileOpenNextConfig(configPath, { compileEdge: true }); @@ -59,11 +66,6 @@ export async function compileConfig(configPath: string | undefined) { return { config, buildDir }; } -/** - * Retrieve a compiled OpenNext config, and ensure it is for Cloudflare. - * - * @returns OpenNext config. - */ export async function retrieveCompiledConfig() { const configPath = path.join(nextAppDir, ".open-next/.build/open-next.config.edge.mjs"); @@ -78,13 +80,6 @@ export async function retrieveCompiledConfig() { return { config }; } -/** - * Normalize the OpenNext options and set the logging level. - * - * @param config - * @param buildDir Directory to use when building the application - * @returns Normalized options. - */ export function getNormalizedOptions(config: OpenNextConfig, buildDir = nextAppDir) { const require = createRequire(import.meta.url); const openNextDistDir = path.dirname(require.resolve("@opennextjs/aws/index.js")); @@ -95,22 +90,10 @@ export function getNormalizedOptions(config: OpenNextConfig, buildDir = nextAppD return options; } -/** - * Read the Wrangler config. - * - * @param args Wrangler environment and config path. - * @returns Wrangler config. - */ export async function readWranglerConfig(args: WithWranglerArgs) { - // Note: `unstable_readConfig` is sync as of wrangler 4.60.0 - // But it will eventually become async. - // See https://github.com/cloudflare/workers-sdk/pull/12031 return await unstable_readConfig({ env: args.env, config: args.wranglerConfigPath }); } -/** - * Adds flags for the wrangler config path and environment to the yargs configuration. - */ export function withWranglerOptions(args: T) { return args .option("config", { @@ -137,12 +120,12 @@ type WranglerInputArgs = { remote?: boolean | undefined; }; -/** - * - * @param args - * @returns An array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. - */ -function getWranglerArgs(args: WranglerInputArgs & { _: (string | number)[] }): string[] { +function getWranglerArgs( + args: WranglerInputArgs & { + _: (string | number)[]; + args?: (string | number)[]; + } +): string[] { if (args.configPath) { logger.warn("The `--configPath` flag is deprecated, please use `--config` instead."); @@ -159,16 +142,10 @@ function getWranglerArgs(args: WranglerInputArgs & { _: (string | number)[] }): ...(args.config ? ["--config", args.config] : []), ...(args.env ? ["--env", args.env] : []), ...(args.remote ? ["--remote"] : []), - // Note: the first args in `_` will be the commands. - ...args._.slice(args._[0] === "populateCache" ? 2 : 1).map((a) => `${a}`), + ...(args.args?.map((a) => `${a}`) ?? []), ]; } -/** - * - * @param args - * @returns The inputted args, and an array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. - */ export function withWranglerPassthroughArgs>( args: T ): WithWranglerArgs { diff --git a/packages/cloudflare/src/cli/index.ts b/packages/cloudflare/src/cli/index.ts index 6f982c67..3646f316 100644 --- a/packages/cloudflare/src/cli/index.ts +++ b/packages/cloudflare/src/cli/index.ts @@ -1,9 +1,12 @@ #!/usr/bin/env node +import logger from "@opennextjs/aws/logger.js"; import yargs from "yargs"; +import { getVersion } from "./build/utils/version.js"; import { addBuildCommand } from "./commands/build.js"; import { addDeployCommand } from "./commands/deploy.js"; +import { addMigrateCommand } from "./commands/migrate.js"; import { addPopulateCacheCommand } from "./commands/populate-cache.js"; import { addPreviewCommand } from "./commands/preview.js"; import { addUploadCommand } from "./commands/upload.js"; @@ -11,13 +14,29 @@ import { addUploadCommand } from "./commands/upload.js"; export function runCommand() { const y = yargs(process.argv.slice(2).filter((arg) => arg !== "--")) .scriptName("opennextjs-cloudflare") - .parserConfiguration({ "unknown-options-as-args": true }); + .parserConfiguration({ "unknown-options-as-args": true }) + .strictCommands() + .help() + .alias("h", "help") + .version(getVersion().cloudflare) + .alias("v", "version") + .fail((msg, err, yargs) => { + if (msg) { + logger.error(`${msg}\n`); + } + if (err) { + throw err; + } + yargs.showHelp(); + process.exit(1); + }); addBuildCommand(y); addPreviewCommand(y); addDeployCommand(y); addUploadCommand(y); addPopulateCacheCommand(y); + addMigrateCommand(y); return y.demandCommand(1, 1).parse(); } diff --git a/packages/cloudflare/src/cli/project-options.ts b/packages/cloudflare/src/cli/project-options.ts index fd8946ab..42f86dfa 100644 --- a/packages/cloudflare/src/cli/project-options.ts +++ b/packages/cloudflare/src/cli/project-options.ts @@ -1,4 +1,4 @@ -import type { WranglerTarget } from "./utils/run-wrangler.js"; +import type { WranglerTarget } from "./commands/utils/run-wrangler.js"; export type ProjectOptions = { // Next app root folder diff --git a/packages/cloudflare/src/cli/templates/images.spec.ts b/packages/cloudflare/src/cli/templates/images.spec.ts index 0489997e..14b441f8 100644 --- a/packages/cloudflare/src/cli/templates/images.spec.ts +++ b/packages/cloudflare/src/cli/templates/images.spec.ts @@ -2,7 +2,12 @@ import pm from "picomatch"; import { describe, expect, it } from "vitest"; import type { LocalPattern } from "./images.js"; -import { matchLocalPattern, matchRemotePattern as mRP } from "./images.js"; +import { + detectImageContentType, + matchLocalPattern, + matchRemotePattern as mRP, + parseCdnCgiImageRequest, +} from "./images.js"; /** * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-remote-pattern.test.ts @@ -426,3 +431,128 @@ describe("matchLocalPattern", () => { expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(true); }); }); + +describe("parseCdnCgiImageRequest", () => { + it("should parse a valid local image request", () => { + const result = parseCdnCgiImageRequest( + "/cdn-cgi/image/width=640,quality=75,format=auto/_next/static/media/photo.png" + ); + expect(result).toEqual({ ok: true, url: "/_next/static/media/photo.png", static: false }); + }); + + it("should parse a valid remote image request", () => { + const result = parseCdnCgiImageRequest( + "/cdn-cgi/image/width=1080,quality=75,format=auto/https://example.com/photo.jpg" + ); + expect(result).toEqual({ ok: true, url: "https://example.com/photo.jpg", static: false }); + }); + + it("should reject when pathname does not match /cdn-cgi/image/ format", () => { + const result = parseCdnCgiImageRequest("/cdn-cgi/image/"); + expect(result).toEqual({ ok: false, message: "Invalid /cdn-cgi/image/ URL format" }); + }); + + it("should reject when options segment has no trailing image URL", () => { + const result = parseCdnCgiImageRequest("/cdn-cgi/image/width=640"); + expect(result).toEqual({ ok: false, message: "Invalid /cdn-cgi/image/ URL format" }); + }); + + it("should reject protocol-relative URLs", () => { + const result = parseCdnCgiImageRequest( + "/cdn-cgi/image/width=640,quality=75,format=auto//evil.com/photo.jpg" + ); + expect(result).toEqual({ + ok: false, + message: '"url" parameter cannot be a protocol-relative URL (//)', + }); + }); + + it("should add leading slash to relative image URLs", () => { + const result = parseCdnCgiImageRequest( + "/cdn-cgi/image/width=640,quality=75,format=auto/path/to/image.png" + ); + expect(result).toMatchObject({ ok: true, url: "/path/to/image.png" }); + }); +}); + +describe("detectImageContentType", () => { + it("should detect JPEG", () => { + const buffer = new Uint8Array([0xff, 0xd8, 0xff]); + expect(detectImageContentType(buffer)).toBe("image/jpeg"); + }); + + it("should detect PNG", () => { + const buffer = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + expect(detectImageContentType(buffer)).toBe("image/png"); + }); + + it("should detect GIF", () => { + const buffer = new Uint8Array([0x47, 0x49, 0x46, 0x38]); + expect(detectImageContentType(buffer)).toBe("image/gif"); + }); + + it("should detect WebP", () => { + const buffer = new Uint8Array([0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50]); + expect(detectImageContentType(buffer)).toBe("image/webp"); + }); + + it("should detect SVG ( { + const buffer = new Uint8Array([0x3c, 0x3f, 0x78, 0x6d, 0x6c]); + expect(detectImageContentType(buffer)).toBe("image/svg+xml"); + }); + + it("should detect SVG ( { + const buffer = new Uint8Array([0x3c, 0x73, 0x76, 0x67]); + expect(detectImageContentType(buffer)).toBe("image/svg+xml"); + }); + + it("should detect AVIF", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66]); + expect(detectImageContentType(buffer)).toBe("image/avif"); + }); + + it("should detect ICO", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x01, 0x00]); + expect(detectImageContentType(buffer)).toBe("image/x-icon"); + }); + + it("should detect ICNS", () => { + const buffer = new Uint8Array([0x69, 0x63, 0x6e, 0x73]); + expect(detectImageContentType(buffer)).toBe("image/x-icns"); + }); + + it("should detect TIFF", () => { + const buffer = new Uint8Array([0x49, 0x49, 0x2a, 0x00]); + expect(detectImageContentType(buffer)).toBe("image/tiff"); + }); + + it("should detect BMP", () => { + const buffer = new Uint8Array([0x42, 0x4d]); + expect(detectImageContentType(buffer)).toBe("image/bmp"); + }); + + it("should detect JXL (short signature)", () => { + const buffer = new Uint8Array([0xff, 0x0a]); + expect(detectImageContentType(buffer)).toBe("image/jxl"); + }); + + it("should detect JXL (long signature)", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x00, 0x0c, 0x4a, 0x58, 0x4c, 0x20, 0x0d, 0x0a, 0x87, 0x0a]); + expect(detectImageContentType(buffer)).toBe("image/jxl"); + }); + + it("should detect HEIC", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63]); + expect(detectImageContentType(buffer)).toBe("image/heic"); + }); + + it("should detect JP2", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a]); + expect(detectImageContentType(buffer)).toBe("image/jp2"); + }); + + it("should return null for unknown content", () => { + const buffer = new Uint8Array([0x00, 0x00, 0x00, 0x00]); + expect(detectImageContentType(buffer)).toBeNull(); + }); +}); diff --git a/packages/cloudflare/src/cli/templates/images.ts b/packages/cloudflare/src/cli/templates/images.ts index dbe6ef44..1dae02f2 100644 --- a/packages/cloudflare/src/cli/templates/images.ts +++ b/packages/cloudflare/src/cli/templates/images.ts @@ -89,20 +89,12 @@ export async function handleImageRequest( } } - const [contentTypeImageStream, imageStream] = imageResponse.body.tee(); - const imageHeaderBytes = new Uint8Array(32); - const contentTypeImageReader = contentTypeImageStream.getReader({ - mode: "byob", - }); - const readImageHeaderBytesResult = await contentTypeImageReader.readAtLeast(32, imageHeaderBytes); - if (readImageHeaderBytesResult.value === undefined) { - await imageResponse.body.cancel(); - - return new Response('"url" parameter is valid but upstream response is invalid', { - status: 400, - }); + const readHeaderResult = await readImageHeader(imageResponse); + if (readHeaderResult instanceof Response) { + return readHeaderResult; } - const contentType = detectImageContentType(readImageHeaderBytesResult.value); + + const { contentType, imageStream } = readHeaderResult; if (contentType === null) { warn(`Failed to detect content type of "${parseResult.url}"`); return new Response('"url" parameter is valid but image type is not allowed', { @@ -349,6 +341,94 @@ type ErrorResult = { message: string; }; +export function parseCdnCgiImageRequest( + pathname: string +): { ok: true; url: string; static: boolean } | ErrorResult { + const match = pathname.match(/^\/cdn-cgi\/image\/(?[^/]+)\/(?.+)$/); + if (match === null || !match.groups?.options || !match.groups?.url) { + return { ok: false, message: "Invalid /cdn-cgi/image/ URL format" }; + } + + const imageUrl = match.groups.url; + if (imageUrl.startsWith("/")) { + return { ok: false, message: '"url" parameter cannot be a protocol-relative URL (//)' }; + } + + let resolvedUrl: string; + if (imageUrl.match(/^https?:\/\//)) { + resolvedUrl = imageUrl; + } else { + resolvedUrl = `/${imageUrl}`; + } + + return { + ok: true, + url: resolvedUrl, + static: false, + }; +} + +export async function handleCdnCgiImageRequest(requestURL: URL, env: CloudflareEnv): Promise { + const parseResult = parseCdnCgiImageRequest(requestURL.pathname); + if (!parseResult.ok) { + return new Response(parseResult.message, { status: 400 }); + } + + let imageResponse: Response; + if (parseResult.url.startsWith("/")) { + if (env.ASSETS === undefined) { + return new Response("env.ASSETS binding is not defined", { status: 404 }); + } + const absoluteURL = new URL(parseResult.url, requestURL); + imageResponse = await env.ASSETS.fetch(absoluteURL); + } else { + imageResponse = await fetch(parseResult.url); + } + + if (!imageResponse.ok || imageResponse.body === null) { + return new Response('"url" parameter is valid but upstream response is invalid', { + status: imageResponse.status, + }); + } + + const readHeaderResult = await readImageHeader(imageResponse); + if (readHeaderResult instanceof Response) { + return readHeaderResult; + } + + const { contentType, imageStream } = readHeaderResult; + if (contentType === null || !SUPPORTED_CDN_CGI_INPUT_TYPES.has(contentType)) { + return new Response('"url" parameter is valid but image type is not allowed', { status: 400 }); + } + + if (contentType === SVG && !__IMAGES_ALLOW_SVG__) { + return new Response('"url" parameter is valid but image type is not allowed', { status: 400 }); + } + + return new Response(imageStream, { + headers: { "Content-Type": contentType }, + }); +} + +async function readImageHeader( + imageResponse: Response +): Promise<{ contentType: ImageContentType | null; imageStream: ReadableStream } | Response> { + const [contentTypeStream, imageStream] = imageResponse.body!.tee(); + const headerBytes = new Uint8Array(32); + const reader = contentTypeStream.getReader({ mode: "byob" }); + const readResult = await reader.readAtLeast(32, headerBytes); + + if (readResult.value === undefined) { + await imageResponse.body!.cancel(); + return new Response('"url" parameter is valid but upstream response is invalid', { + status: 400, + }); + } + + const contentType = detectImageContentType(readResult.value); + return { contentType, imageStream }; +} + /** * Validates that there is exactly one "url" query parameter. * @@ -631,6 +711,8 @@ const ICNS = "image/x-icns"; const TIFF = "image/tiff"; const BMP = "image/bmp"; +const SUPPORTED_CDN_CGI_INPUT_TYPES: ReadonlySet = new Set([JPEG, PNG, GIF, WEBP, SVG, HEIC]); + type ImageContentType = | "image/avif" | "image/webp" diff --git a/packages/cloudflare/src/cli/templates/worker.ts b/packages/cloudflare/src/cli/templates/worker.ts index a8568a23..b304c8b5 100644 --- a/packages/cloudflare/src/cli/templates/worker.ts +++ b/packages/cloudflare/src/cli/templates/worker.ts @@ -1,7 +1,7 @@ import type { InternalResult } from "@opennextjs/aws/types/open-next.js"; //@ts-expect-error: Will be resolved by wrangler build -import { handleImageRequest } from "./cloudflare/images.js"; +import { handleCdnCgiImageRequest, handleImageRequest } from "./cloudflare/images.js"; //@ts-expect-error: Will be resolved by wrangler build import { runWithCloudflareRequestContext } from "./cloudflare/init.js"; //@ts-expect-error: Will be resolved by wrangler build @@ -29,14 +29,7 @@ export default { // Serve images in development. // Note: "/cdn-cgi/image/..." requests do not reach production workers. if (url.pathname.startsWith("/cdn-cgi/image/")) { - const m = url.pathname.match(/\/cdn-cgi\/image\/.+?\/(?.+)$/); - if (m === null) { - return new Response("Not Found!", { status: 404 }); - } - const imageUrl = m.groups!.url!; - return imageUrl.match(/^https?:\/\//) - ? fetch(imageUrl, { cf: { cacheEverything: true } }) - : env.ASSETS?.fetch(new URL(`/${imageUrl}`, url)); + return handleCdnCgiImageRequest(url, env); } // Fallback for the Next default image loader. diff --git a/packages/cloudflare/src/cli/utils/ask-account-selection.ts b/packages/cloudflare/src/cli/utils/ask-account-selection.ts new file mode 100644 index 00000000..1f4a9b19 --- /dev/null +++ b/packages/cloudflare/src/cli/utils/ask-account-selection.ts @@ -0,0 +1,27 @@ +import { randomUUID } from "node:crypto"; + +import Enquirer from "enquirer"; + +interface Account { + id: string; + name: string; +} + +export async function askAccountSelection(accounts: Account[]): Promise { + const questionName = randomUUID(); + + const enquirerAnswersObject = await Enquirer.prompt>({ + name: questionName, + message: "Select which Cloudflare account to use", + type: "select", + choices: accounts.map((account) => ({ + name: account.id, + message: account.name, + })), + format: (accountId) => `${accounts.find(({ id }) => id === accountId)?.name ?? ""}`, + }); + + console.log(""); + + return enquirerAnswersObject[questionName]; +} diff --git a/packages/cloudflare/src/cli/utils/create-open-next-config.ts b/packages/cloudflare/src/cli/utils/create-open-next-config.ts new file mode 100644 index 00000000..03d576ec --- /dev/null +++ b/packages/cloudflare/src/cli/utils/create-open-next-config.ts @@ -0,0 +1,49 @@ +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js"; + +import { getPackageTemplatesDirPath } from "../../utils/get-package-templates-dir-path.js"; + +export function findOpenNextConfig(appDir: string): string | undefined { + const openNextConfigPath = join(appDir, "open-next.config.ts"); + + if (existsSync(openNextConfigPath)) { + return openNextConfigPath; + } + + return undefined; +} + +export function createOpenNextConfigFile(appDir: string, options: { cache: boolean }): string { + const openNextConfigPath = join(appDir, "open-next.config.ts"); + + let content = readFileSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), "utf8"); + + if (!options.cache) { + content = patchCode(content, commentOutR2ImportRule); + content = patchCode(content, commentOutIncrementalCacheRule); + } + + writeFileSync(openNextConfigPath, content); + + return openNextConfigPath; +} + +const commentOutR2ImportRule = ` +rule: + pattern: import $ID from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +fix: |- + // import $ID from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +`; + +const commentOutIncrementalCacheRule = ` +rule: + pattern: '{ incrementalCache: $ID }' +fix: |- + { + // For best results consider enabling R2 caching + // See https://opennext.js.org/cloudflare/caching for more details + // incrementalCache: $ID + } +`; diff --git a/packages/cloudflare/src/cli/utils/create-wrangler-config.ts b/packages/cloudflare/src/cli/utils/create-wrangler-config.ts new file mode 100644 index 00000000..0dae0398 --- /dev/null +++ b/packages/cloudflare/src/cli/utils/create-wrangler-config.ts @@ -0,0 +1,262 @@ +import assert from "node:assert"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; + +import { findPackagerAndRoot } from "@opennextjs/aws/build/helper.js"; +import Cloudflare from "cloudflare"; +import { type CommentObject, parse, stringify } from "comment-json"; + +import { getPackageTemplatesDirPath } from "../../utils/get-package-templates-dir-path.js"; + +import { askAccountSelection } from "./ask-account-selection.js"; + +export type PackagerDetails = { + packager: "npm" | "pnpm" | "yarn" | "bun"; + monorepoRoot: string; +}; + +export function findWranglerConfig(appDir: string): string | undefined { + const possibleExts = ["toml", "json", "jsonc"]; + + for (const ext of possibleExts) { + const path = join(appDir, `wrangler.${ext}`); + if (existsSync(path)) { + return path; + } + } + + return undefined; +} + +export async function createWranglerConfigFile( + projectDir: string, + defaultCompatDate = "2026-02-01" +): Promise<{ cachingEnabled: boolean }> { + const workerName = getWorkerName(projectDir); + const compatibilityDate = (await getLatestCompatDate()) ?? defaultCompatDate; + + const wranglerConfigStr = readFileSync(join(getPackageTemplatesDirPath(), "wrangler.jsonc"), "utf8") + .replaceAll("", workerName) + .replaceAll("", compatibilityDate); + + const wranglerConfig = parse(wranglerConfigStr) as CommentObject; + + assert(Array.isArray(wranglerConfig.r2_buckets)); + assert(wranglerConfig.r2_buckets[0] != null && typeof wranglerConfig.r2_buckets[0] === "object"); + assert( + "bucket_name" in wranglerConfig.r2_buckets[0] && + typeof wranglerConfig.r2_buckets[0].bucket_name === "string" + ); + + const bucketName = wranglerConfig.r2_buckets[0].bucket_name.replaceAll( + "", + `${workerName}-opennext-cache` + ); + wranglerConfig.r2_buckets[0].bucket_name = bucketName; + + const { success: cachingEnabled } = await maybeCreateR2Bucket(projectDir, bucketName); + + if (!cachingEnabled) { + delete wranglerConfig.r2_buckets; + } + + writeFileSync(join(projectDir, "wrangler.jsonc"), stringify(wranglerConfig, null, "\t")); + + return { cachingEnabled }; +} + +function getWorkerName(projectDir: string): string { + const appName = getNameFromPackageJson(projectDir) ?? "app-name"; + + return appName + .toLowerCase() + .replace(/^@[^/]+\//, "") + .replaceAll("_", "-") + .replace(/[^a-z0-9-]/g, ""); +} + +function getNameFromPackageJson(sourceDir: string): string | undefined { + try { + const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8"); + const packageJson: Record = JSON.parse(packageJsonStr); + if (typeof packageJson.name === "string") return packageJson.name; + } catch { + /* empty */ + } +} + +async function getLatestCompatDate(): Promise { + try { + const resp = await fetch(`https://registry.npmjs.org/workerd`); + const latestWorkerdVersion = ( + (await resp.json()) as { + "dist-tags": { latest: string }; + } + )["dist-tags"].latest; + + const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/); + + if (match) { + const [, year, month, day] = match; + const compatDate = `${year}-${month}-${day}`; + + const currentDate = new Date().toISOString().slice(0, 10); + + return compatDate < currentDate ? compatDate : currentDate; + } + } catch { + /* empty */ + } +} + +async function maybeCreateR2Bucket(projectDir: string, bucketName: string): Promise<{ success: boolean }> { + const { packager, root: monorepoRoot } = findPackagerAndRoot(projectDir); + const packagerDetails: PackagerDetails = { packager, monorepoRoot }; + + const authResult = runWrangler(packagerDetails, ["whoami", "--json"], { logging: "none" }); + + if (!authResult.success) { + return { success: false }; + } + + let whoami: { + api_token: string; + auth_status: string; + email: string; + accounts: { id: string; name: string }[]; + }; + + try { + whoami = JSON.parse(authResult.stdout); + } catch { + return { success: false }; + } + + if (whoami.auth_status !== "active") { + return { success: false }; + } + + const accountId = process.env.CLOUDFLARE_ACCOUNT_ID ?? (await selectAccount(whoami.accounts)); + + if (!accountId) { + return { success: false }; + } + + const cf = new Cloudflare({ apiToken: whoami.api_token }); + + try { + await cf.r2.buckets.get(bucketName, { account_id: accountId }); + } catch (error: unknown) { + if (error instanceof Error && "code" in error && error.code === "MISSING_ARG_REGION") { + try { + await cf.r2.buckets.create({ account_id: accountId, name: bucketName }); + } catch { + return { success: false }; + } + } else if (error instanceof Error && error.message.includes("NotFound")) { + try { + await cf.r2.buckets.create({ account_id: accountId, name: bucketName }); + } catch { + return { success: false }; + } + } else { + return { success: false }; + } + } + + return { success: true }; +} + +async function selectAccount(accounts: { id: string; name: string }[]): Promise { + if (accounts.length === 0) { + return undefined; + } + + if (accounts.length === 1) { + return accounts[0]!.id; + } + + return askAccountSelection(accounts); +} + +type WranglerCommandResult = { + success: boolean; + stdout: string; + stderr: string; +}; + +type WranglerOptions = { + target?: "local" | "remote"; + environment?: string; + configPath?: string; + logging?: "all" | "error" | "none"; + env?: Record; +}; + +function runWrangler( + options: PackagerDetails, + args: string[], + wranglerOpts: WranglerOptions = {} +): WranglerCommandResult { + const { spawnSync } = require("node:child_process"); + + const noLogs = wranglerOpts.logging === "none"; + const shouldPipeLogs = wranglerOpts.logging === "error"; + + const result = spawnSync( + options.packager, + [ + options.packager === "bun" ? "x" : "exec", + "wrangler", + ...injectPassthroughFlagForArgs( + options, + [ + ...args, + wranglerOpts.environment && `--env ${wranglerOpts.environment}`, + wranglerOpts.configPath && `--config ${wranglerOpts.configPath}`, + wranglerOpts.target === "remote" && "--remote", + wranglerOpts.target === "local" && "--local", + ].filter((v): v is string => !!v) + ), + ], + { + shell: true, + stdio: shouldPipeLogs || noLogs ? ["ignore", "pipe", "pipe"] : ["inherit", "inherit", "pipe"], + env: { + ...process.env, + ...wranglerOpts.env, + CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV: "false", + }, + } + ); + + const success = result.status === 0; + const stdout = result.stdout?.toString() ?? ""; + const stderr = result.stderr?.toString() ?? ""; + + if (!noLogs) { + if (!shouldPipeLogs && stderr) { + process.stderr.write(stderr); + } + + if (!success && shouldPipeLogs) { + process.stdout.write(stdout); + process.stderr.write(stderr); + } + } + + return { success, stdout, stderr }; +} + +function injectPassthroughFlagForArgs(options: PackagerDetails, args: string[]): string[] { + if (options.packager !== "npm" && options.packager !== "yarn") { + return args; + } + + const flagInArgsIndex = args.findIndex((v) => v.startsWith("--")); + if (flagInArgsIndex !== -1) { + args.splice(flagInArgsIndex, 0, "--"); + } + + return args; +} diff --git a/packages/cloudflare/src/cli/build/utils/extract-project-env-vars.ts b/packages/cloudflare/src/cli/utils/extract-project-env-vars.ts similarity index 56% rename from packages/cloudflare/src/cli/build/utils/extract-project-env-vars.ts rename to packages/cloudflare/src/cli/utils/extract-project-env-vars.ts index 2ec85395..cc98c07a 100644 --- a/packages/cloudflare/src/cli/build/utils/extract-project-env-vars.ts +++ b/packages/cloudflare/src/cli/utils/extract-project-env-vars.ts @@ -10,22 +10,6 @@ function readEnvFile(filePath: string) { } } -/** - * Extracts the environment variables defined in various .env files for a project. - * - * The `NEXTJS_ENV` environment variable in `.dev.vars` determines the mode. - * - * Merged variables respect the following priority order. - * 1. `.env.{mode}.local` - * 2. `.env.local` (when mode is not equal to `test`) - * 3. `.env.{mode}` - * 4. `.env` - * - * https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#environment-variable-load-order - * - * In a monorepo, the env files in an app's directory will take precedence over - * the env files at the root of the monorepo. - */ export function extractProjectEnvVars(mode: string, { monorepoRoot, appPath }: BuildOptions) { return [".env", `.env.${mode}`, ...(mode !== "test" ? [".env.local"] : []), `.env.${mode}.local`] .flatMap((fileName) => [ diff --git a/packages/cloudflare/src/cli/utils/needs-experimental-react.ts b/packages/cloudflare/src/cli/utils/needs-experimental-react.ts new file mode 100644 index 00000000..dad47860 --- /dev/null +++ b/packages/cloudflare/src/cli/utils/needs-experimental-react.ts @@ -0,0 +1,15 @@ +import type { NextConfig } from "@opennextjs/aws/types/next-types.js"; + +interface ExtendedNextConfig extends NextConfig { + experimental: { + ppr?: boolean; + taint?: boolean; + viewTransition?: boolean; + serverActions?: boolean; + }; +} + +export function needsExperimentalReact(nextConfig: ExtendedNextConfig) { + const { ppr, taint, viewTransition } = nextConfig.experimental || {}; + return Boolean(ppr || taint || viewTransition); +} diff --git a/packages/cloudflare/src/cli/utils/nextjs-support.ts b/packages/cloudflare/src/cli/utils/nextjs-support.ts new file mode 100644 index 00000000..ba845fa8 --- /dev/null +++ b/packages/cloudflare/src/cli/utils/nextjs-support.ts @@ -0,0 +1,16 @@ +import { compareSemver } from "@opennextjs/aws/build/helper.js"; +import logger from "@opennextjs/aws/logger.js"; + +export async function ensureNextjsVersionSupported({ nextVersion }: { nextVersion: string }) { + if (compareSemver(nextVersion, "<", "14.2.0")) { + throw new Error("Next.js version unsupported, please upgrade to version 14.2 or greater."); + } + + const { + default: { version: wranglerVersion }, + } = await import("wrangler/package.json", { with: { type: "json" } }); + + if (compareSemver(nextVersion, ">=", "16.1.0") && compareSemver(wranglerVersion, "<", "4.59.2")) { + logger.warn(`Next.js 16.1+ requires wrangler 4.59.2 or greater (${wranglerVersion} detected).`); + } +} diff --git a/packages/cloudflare/src/cli/build/utils/normalize-path.ts b/packages/cloudflare/src/cli/utils/normalize-path.ts similarity index 100% rename from packages/cloudflare/src/cli/build/utils/normalize-path.ts rename to packages/cloudflare/src/cli/utils/normalize-path.ts diff --git a/packages/cloudflare/templates/wrangler.jsonc b/packages/cloudflare/templates/wrangler.jsonc index 9fb58e9a..7cf1ce87 100644 --- a/packages/cloudflare/templates/wrangler.jsonc +++ b/packages/cloudflare/templates/wrangler.jsonc @@ -2,7 +2,7 @@ "$schema": "node_modules/wrangler/config-schema.json", "main": ".open-next/worker.js", "name": "", - "compatibility_date": "2025-12-01", + "compatibility_date": "", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], "assets": { "directory": ".open-next/assets", @@ -24,7 +24,7 @@ // Create the bucket before deploying // You can change the bucket name if you want // See https://developers.cloudflare.com/workers/wrangler/commands/#r2-bucket-create - "bucket_name": "cache" + "bucket_name": "" } ], "images": { diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index da2575b5..159495b9 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -337,8 +337,7 @@ export function copyEnvFile(appPath: string, packagePath: string, outputPath: st /** * Check we are in a Nextjs app by looking for the Nextjs config file. */ -export function checkRunningInsideNextjsApp(options: BuildOptions) { - const { appPath } = options; +export function checkRunningInsideNextjsApp({ appPath }: { appPath: string }) { const extension = ["js", "cjs", "mjs", "ts"].find((ext) => fs.existsSync(path.join(appPath, `next.config.${ext}`)) ); diff --git a/packages/tests-unit/tests/adapters/cache.test.ts b/packages/tests-unit/tests/adapters/cache.test.ts index 03bc2d80..fa739380 100644 --- a/packages/tests-unit/tests/adapters/cache.test.ts +++ b/packages/tests-unit/tests/adapters/cache.test.ts @@ -392,6 +392,46 @@ describe("CacheHandler", () => { }); }); + it("Should return value when cache data type is app with segmentData and postponed (Next 15+)", async () => { + globalThis.isNextAfter15 = true; + incrementalCache.get.mockResolvedValueOnce({ + value: { + type: "app", + html: "", + rsc: "rsc-data", + segmentData: { + segment1: "data1", + segment2: "data2", + }, + meta: { + status: 200, + headers: { "x-custom": "value" }, + postponed: "postponed-data", + }, + }, + lastModified: Date.now(), + }); + + const result = await cache.get("key", { kindHint: "app" }); + + expect(getIncrementalCache).toHaveBeenCalled(); + expect(result).toEqual({ + value: { + kind: "APP_PAGE", + html: "", + rscData: Buffer.from("rsc-data"), + status: 200, + headers: { "x-custom": "value" }, + postponed: "postponed-data", + segmentData: new Map([ + ["segment1", Buffer.from("data1")], + ["segment2", Buffer.from("data2")], + ]), + }, + lastModified: Date.now(), + }); + }); + it("Should return value when cache data type is redirect", async () => { incrementalCache.get.mockResolvedValueOnce({ value: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e7bb3c7..191fab7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -161,8 +161,8 @@ importers: create-cloudflare/next: dependencies: '@opennextjs/cloudflare': - specifier: ^1.15.1 - version: 1.15.1(aws-crt@1.23.0)(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(wrangler@4.60.0(@cloudflare/workers-types@4.20260123.0)) + specifier: ^1.17.1 + version: 1.18.0(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(wrangler@4.60.0(@cloudflare/workers-types@4.20260123.0)) next: specifier: 16.1.4 version: 16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -1275,6 +1275,9 @@ importers: cloudflare: specifier: ^4.4.1 version: 4.5.0 + comment-json: + specifier: ^4.5.1 + version: 4.6.2 enquirer: specifier: ^2.4.1 version: 2.4.1 @@ -1297,6 +1300,9 @@ importers: '@tsconfig/strictest': specifier: 'catalog:' version: 2.0.8 + '@types/comment-json': + specifier: ^2.4.5 + version: 2.4.5 '@types/mock-fs': specifier: 'catalog:' version: 4.13.4 @@ -1488,37 +1494,18 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@ast-grep/napi-darwin-arm64@0.40.0': - resolution: {integrity: sha512-ZMjl5yLhKjxdwbqEEdMizgQdWH2NrWsM6Px+JuGErgCDe6Aedq9yurEPV7veybGdLVJQhOah6htlSflXxjHnYA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - '@ast-grep/napi-darwin-arm64@0.40.5': resolution: {integrity: sha512-2F072fGN0WTq7KI3okuEnkGJVEHLbi56Bw1H6NAMf7j2mJJeQWsRyGOMcyNnUXZDeNdvoMH0OB2a5wwUegY/nQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@ast-grep/napi-darwin-x64@0.40.0': - resolution: {integrity: sha512-f9Ol5oQKNRMBkvDtzBK1WiNn2/3eejF2Pn9xwTj7PhXuSFseedOspPYllxQo0gbwUlw/DJqGFTce/jarhR/rBw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - '@ast-grep/napi-darwin-x64@0.40.5': resolution: {integrity: sha512-dJMidHZhhxuLBYNi6/FKI812jQ7wcFPSKkVPwviez2D+KvYagapUMAV/4dJ7FCORfguVk8Y0jpPAlYmWRT5nvA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@ast-grep/napi-linux-arm64-gnu@0.40.0': - resolution: {integrity: sha512-+tO+VW5GDhT9jGkKOK+3b8+ohKjC98WTzn7wSskd/myyhK3oYL1WTKqCm07WSYBZOJvb3z+WaX+wOUrc4bvtyQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@ast-grep/napi-linux-arm64-gnu@0.40.5': resolution: {integrity: sha512-nBRCbyoS87uqkaw4Oyfe5VO+SRm2B+0g0T8ME69Qry9ShMf41a2bTdpcQx9e8scZPogq+CTwDHo3THyBV71l9w==} engines: {node: '>= 10'} @@ -1526,13 +1513,6 @@ packages: os: [linux] libc: [glibc] - '@ast-grep/napi-linux-arm64-musl@0.40.0': - resolution: {integrity: sha512-MS9qalLRjUnF2PCzuTKTvCMVSORYHxxe3Qa0+SSaVULsXRBmuy5C/b1FeWwMFnwNnC0uie3VDet31Zujwi8q6A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - '@ast-grep/napi-linux-arm64-musl@0.40.5': resolution: {integrity: sha512-/qKsmds5FMoaEj6FdNzepbmLMtlFuBLdrAn9GIWCqOIcVcYvM1Nka8+mncfeXB/MFZKOrzQsQdPTWqrrQzXLrA==} engines: {node: '>= 10'} @@ -1540,13 +1520,6 @@ packages: os: [linux] libc: [musl] - '@ast-grep/napi-linux-x64-gnu@0.40.0': - resolution: {integrity: sha512-BeHZVMNXhM3WV3XE2yghO0fRxhMOt8BTN972p5piYEQUvKeSHmS8oeGcs6Ahgx5znBclqqqq37ZfioYANiTqJA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - '@ast-grep/napi-linux-x64-gnu@0.40.5': resolution: {integrity: sha512-DP4oDbq7f/1A2hRTFLhJfDFR6aI5mRWdEfKfHzRItmlKsR9WlcEl1qDJs/zX9R2EEtIDsSKRzuJNfJllY3/W8Q==} engines: {node: '>= 10'} @@ -1554,13 +1527,6 @@ packages: os: [linux] libc: [glibc] - '@ast-grep/napi-linux-x64-musl@0.40.0': - resolution: {integrity: sha512-rG1YujF7O+lszX8fd5u6qkFTuv4FwHXjWvt1CCvCxXwQLSY96LaCW88oVKg7WoEYQh54y++Fk57F+Wh9Gv9nVQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - '@ast-grep/napi-linux-x64-musl@0.40.5': resolution: {integrity: sha512-BRZUvVBPUNpWPo6Ns8chXVzxHPY+k9gpsubGTHy92Q26ecZULd/dTkWWdnvfhRqttsSQ9Pe/XQdi5+hDQ6RYcg==} engines: {node: '>= 10'} @@ -1568,46 +1534,24 @@ packages: os: [linux] libc: [musl] - '@ast-grep/napi-win32-arm64-msvc@0.40.0': - resolution: {integrity: sha512-9SqmnQqd4zTEUk6yx0TuW2ycZZs2+e569O/R0QnhSiQNpgwiJCYOe/yPS0BC9HkiaozQm6jjAcasWpFtz/dp+w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - '@ast-grep/napi-win32-arm64-msvc@0.40.5': resolution: {integrity: sha512-y95zSEwc7vhxmcrcH0GnK4ZHEBQrmrszRBNQovzaciF9GUqEcCACNLoBesn4V47IaOp4fYgD2/EhGRTIBFb2Ug==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@ast-grep/napi-win32-ia32-msvc@0.40.0': - resolution: {integrity: sha512-0JkdBZi5l9vZhGEO38A1way0LmLRDU5Vos6MXrLIOVkymmzDTDlCdY394J1LMmmsfwWcyJg6J7Yv2dw41MCxDQ==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - '@ast-grep/napi-win32-ia32-msvc@0.40.5': resolution: {integrity: sha512-K/u8De62iUnFCzVUs7FBdTZ2Jrgc5/DLHqjpup66KxZ7GIM9/HGME/O8aSoPkpcAeCD4TiTZ11C1i5p5H98hTg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@ast-grep/napi-win32-x64-msvc@0.40.0': - resolution: {integrity: sha512-Hk2IwfPqMFGZt5SRxsoWmGLxBXxprow4LRp1eG6V8EEiJCNHxZ9ZiEaIc5bNvMDBjHVSnqZAXT22dROhrcSKQg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - '@ast-grep/napi-win32-x64-msvc@0.40.5': resolution: {integrity: sha512-dqm5zg/o4Nh4VOQPEpMS23ot8HVd22gG0eg01t4CFcZeuzyuSgBlOL3N7xLbz3iH2sVkk7keuBwAzOIpTqziNQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@ast-grep/napi@0.40.0': - resolution: {integrity: sha512-tq6nO/8KwUF/mHuk1ECaAOSOlz2OB/PmygnvprJzyAHGRVzdcffblaOOWe90M9sGz5MAasXoF+PTcayQj9TKKA==} - engines: {node: '>= 10'} - '@ast-grep/napi@0.40.5': resolution: {integrity: sha512-hJA62OeBKUQT68DD2gDyhOqJxZxycqg8wLxbqjgqSzYttCMSDL9tiAQ9abgekBYNHudbJosm9sWOEbmCDfpX2A==} engines: {node: '>= 10'} @@ -1662,34 +1606,19 @@ packages: '@aws-crypto/crc32c@5.2.0': resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - '@aws-crypto/ie11-detection@3.0.0': - resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} - '@aws-crypto/sha1-browser@5.2.0': resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - '@aws-crypto/sha256-browser@3.0.0': - resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} - '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - '@aws-crypto/sha256-js@3.0.0': - resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} - '@aws-crypto/sha256-js@5.2.0': resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} engines: {node: '>=16.0.0'} - '@aws-crypto/supports-web-crypto@3.0.0': - resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} - '@aws-crypto/supports-web-crypto@5.2.0': resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - '@aws-crypto/util@3.0.0': - resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} - '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} @@ -1697,10 +1626,6 @@ packages: resolution: {integrity: sha512-IpFZAuOMO6rm4y5Dt+QUYfeGrbv5KBHH8fzrKfEO6akCn3ryWMgqMPH/5psmDqf3N2e2LX+lF7+5UdBIhbPBcQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-cloudfront@3.398.0': - resolution: {integrity: sha512-kISKhqN1k48TaMPbLgq9jj7mO2jvbJdhirvfu4JW3jhFhENnkY0oCwTPvR4Q6Ne2as6GFAMo2XZDZq4rxC7YDw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/client-cloudfront@3.984.0': resolution: {integrity: sha512-couDuDLpJtoeWne/nYyJ+I+5ntBVdNgBVRTCoDaXuVV7OC3u/wz5Ps0+GogspEwMLEFoOJ8t691h3YXQtnpQTw==} engines: {node: '>=20.0.0'} @@ -1767,10 +1692,6 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.678.0 - '@aws-sdk/client-sso@3.398.0': - resolution: {integrity: sha512-CygL0jhfibw4kmWXG/3sfZMFNjcXo66XUuPC4BqZBk8Rj5vFoxp1vZeMkDLzTIk97Nvo5J5Bh+QnXKhub6AckQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/client-sso@3.678.0': resolution: {integrity: sha512-5Fg2BkR1En8iBbiZ18STvLDGPK9Re5MyCmX+hfIhQzPsEf1FRkAkOluEXX79aBva8iWn2oCD/xKBUku4x3eusw==} engines: {node: '>=16.0.0'} @@ -1779,10 +1700,6 @@ packages: resolution: {integrity: sha512-ci+GiM0c4ULo4D79UMcY06LcOLcfvUfiyt8PzNY0vbt5O8BfCPYf4QomwVgkNcLLCYmroO4ge2Yy1EsLUlcD6g==} engines: {node: '>=20.0.0'} - '@aws-sdk/client-sts@3.398.0': - resolution: {integrity: sha512-/3Pa9wLMvBZipKraq3AtbmTfXW6q9kyvhwOno64f1Fz7kFb8ijQFMGoATS70B2pGEZTlxkUqJFWDiisT6Q6dFg==} - engines: {node: '>=14.0.0'} - '@aws-sdk/client-sts@3.678.0': resolution: {integrity: sha512-oRtDnbqIuTbBq0xd7XlaugDA41EqRFzWLpPNr4uwkH8L7xwtIByfJG/qXx2OtOiFFasAhMWJLu/DDqWZyp819A==} engines: {node: '>=16.0.0'} @@ -1820,10 +1737,6 @@ packages: resolution: {integrity: sha512-t9bgu2Kc0H8FdQsSrkIJ42vis0CaVxUlA0wmmNyh268ZZyT9lKXUmf91QIhWbZ1zHx8Ek2u301xusoIaj4mLHA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.398.0': - resolution: {integrity: sha512-Z8Yj5z7FroAsR6UVML+XUdlpoqEe9Dnle8c2h8/xWwIC2feTfIBhjLhRVxfbpbM1pLgBSNEcZ7U8fwq5l7ESVQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-env@3.678.0': resolution: {integrity: sha512-29uhXAB7uJqHtvJ2U3pi1YkMfv0WefW9EmSMoFAunjudXXBVktwTlWg0lyCM+KHrGKLkQyfs5UF/A9IelS8tdQ==} engines: {node: '>=16.0.0'} @@ -1848,10 +1761,6 @@ packages: resolution: {integrity: sha512-qPymamdPcLp6ugoVocG1y5r69ScNiRzb0hogX25/ij+Wz7c7WnsgjLTaz7+eB5BfRxeyUwuw5hgULMuwOGOpcw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.398.0': - resolution: {integrity: sha512-AsK1lStK3nB9Cn6S6ODb1ktGh7SRejsNVQVKX3t5d3tgOaX+aX1Iwy8FzM/ZEN8uCloeRifUGIY9uQFygg5mSw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-ini@3.678.0': resolution: {integrity: sha512-8kHy7V5rRO73EpBCUclykP9T/QIBVi0SkQsc88ZRxpdh59/JY2N6DT5khMTzrz9+Vvlw3FDMJN4AI/qWjJHhdw==} engines: {node: '>=16.0.0'} @@ -1874,10 +1783,6 @@ packages: resolution: {integrity: sha512-EFcM8RM3TUxnZOfMJo++3PnyxFu1fL/huzmn3Vh+8IWRgqZawUD3cRwwOr+/4bE9DpyHaLOWFAjY0lfK5X9ZkQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.398.0': - resolution: {integrity: sha512-odmI/DSKfuWUYeDnGTCEHBbC8/MwnF6yEq874zl6+owoVv0ZsYP8qBHfiJkYqrwg7wQ7Pi40sSAPC1rhesGwzg==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-node@3.678.0': resolution: {integrity: sha512-KGRBVD/oNr/aD+Wy5zc5AjfeSv5b4ahAu5eAUbOz+eGjGpGgrMtjY+R2rDY/3i3wFj9/DvOIfFGeZQMwtDzIuA==} engines: {node: '>=16.0.0'} @@ -1890,10 +1795,6 @@ packages: resolution: {integrity: sha512-jXpxSolfFnPVj6GCTtx3xIdWNoDR7hYC/0SbetGZxOC9UnNmipHeX1k6spVstf7eWJrMhXNQEgXC0pD1r5tXIg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.398.0': - resolution: {integrity: sha512-WrkBL1W7TXN508PA9wRXPFtzmGpVSW98gDaHEaa8GolAPHMPa5t2QcC/z/cFpglzrcVv8SA277zu9Z8tELdZhg==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-process@3.678.0': resolution: {integrity: sha512-5TpzzHKwPOvUJig0bvTt+brtXfLPaSVLwea9re+XGrS5T6Hz65IaX2RL6uY1GQ0UVOqgwQ5nAti1WOfBoSJ5BA==} engines: {node: '>=16.0.0'} @@ -1906,10 +1807,6 @@ packages: resolution: {integrity: sha512-IL/TFW59++b7MpHserjUblGrdP5UXy5Ekqqx1XQkERXBFJcZr74I7VaSrQT5dxdRMU16xGK4L0RQ5fQG1pMgnA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.398.0': - resolution: {integrity: sha512-2Dl35587xbnzR/GGZqA2MnFs8+kS4wbHQO9BioU0okA+8NRueohNMdrdQmQDdSNK4BfIpFspiZmFkXFNyEAfgw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-sso@3.678.0': resolution: {integrity: sha512-PXydLUsLYd1rkhZ7zwf0613u5sofxIEhh7C1QGP1MSY3L1jt8bu7pZIcMzubfvmaGZI5k84aHhhjQEiAJUxIMg==} engines: {node: '>=16.0.0'} @@ -1922,10 +1819,6 @@ packages: resolution: {integrity: sha512-c6ghvRb6gTlMznWhGxn/bpVCcp0HRaz4DobGVD9kI4vwHq186nU2xN/S7QGkm0lo0H2jQU8+dgpUFLxfTcwCOg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.398.0': - resolution: {integrity: sha512-iG3905Alv9pINbQ8/MIsshgqYMbWx+NDQWpxbIW3W0MkSH3iAqdVpSCteYidYX9G/jv2Um1nW3y360ib20bvNg==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-web-identity@3.678.0': resolution: {integrity: sha512-fcYZjTTFcef99l+BhcEAhHS4tEK1kE6Xj5Zz5lT4tFA07BkQt3d6kUKRVVfJnsbcHH4RDBUCnLhU8HPfc/kvjA==} engines: {node: '>=16.0.0'} @@ -1980,10 +1873,6 @@ packages: resolution: {integrity: sha512-SPSvF0G1t8m8CcB0L+ClNFszzQOvXaxmRj25oRWDf6aU+TuN2PXPFAJ9A6lt1IvX4oGAqqbTdMPTYs/SSHUYYQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.398.0': - resolution: {integrity: sha512-m+5laWdBaxIZK2ko0OwcCHJZJ5V1MgEIt8QVQ3k4/kOkN9ICjevOYmba751pHoTnbOYB7zQd6D2OT3EYEEsUcA==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-host-header@3.667.0': resolution: {integrity: sha512-Z7fIAMQnPegs7JjAQvlOeWXwpMRfegh5eCoIP6VLJIeR6DLfYKbP35JBtt98R6DXslrN2RsbTogjbxPEDQfw1w==} engines: {node: '>=16.0.0'} @@ -2004,10 +1893,6 @@ packages: resolution: {integrity: sha512-KaUoFuoFPziIa98DSQsTPeke1gvGXlc5ZGMhy+b+nLxZ4A7jmJgLzjEF95l8aOQN2T/qlPP3MrAyELm8ExXucw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.398.0': - resolution: {integrity: sha512-CiJjW+FL12elS6Pn7/UVjVK8HWHhXMfvHZvOwx/Qkpy340sIhkuzOO6fZEruECDTZhl2Wqn81XdJ1ZQ4pRKpCg==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-logger@3.667.0': resolution: {integrity: sha512-PtTRNpNm/5c746jRgZCNg4X9xEJIwggkGJrF0GP9AB1ANg4pc/sF2Fvn1NtqPe9wtQ2stunJprnm5WkCHN7QiA==} engines: {node: '>=16.0.0'} @@ -2020,10 +1905,6 @@ packages: resolution: {integrity: sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.398.0': - resolution: {integrity: sha512-7QpOqPQAZNXDXv6vsRex4R8dLniL0E/80OPK4PPFsrCh9btEyhN9Begh4i1T+5lL28hmYkztLOkTQ2N5J3hgRQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-recursion-detection@3.667.0': resolution: {integrity: sha512-U5glWD3ehFohzpUpopLtmqAlDurGWo2wRGPNgi4SwhWU7UDt6LS7E/UvJjqC0CUrjlzOw+my2A+Ncf+fisMhxQ==} engines: {node: '>=16.0.0'} @@ -2061,14 +1942,6 @@ packages: resolution: {integrity: sha512-LnzPRRoDXGtlFV2G1p2rsY6fRKrbf6Pvvc21KliSLw3+NmQca2+Aa1QIMRbpQvZYedsSqkGYwxe+qvXwQ2uxDw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-sts@3.398.0': - resolution: {integrity: sha512-+JH76XHEgfVihkY+GurohOQ5Z83zVN1nYcQzwCFnCDTh4dG4KwhnZKG+WPw6XJECocY0R+H0ivofeALHvVWJtQ==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-signing@3.398.0': - resolution: {integrity: sha512-O0KqXAix1TcvZBFt1qoFkHMUNJOSgjJTYS7lFTRKSwgsD27bdW2TM2r9R8DAccWFt5Amjkdt+eOwQMIXPGTm8w==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-signing@3.664.0': resolution: {integrity: sha512-lKOrS5ZzR43+1XYyeFXcwl2TrteUpuXt3La+qFaLnqtSui8fqacJ+h5Ndx4xC6eNPLl/Sy9Ew6m3MgXm7hPbNg==} engines: {node: '>=16.0.0'} @@ -2081,10 +1954,6 @@ packages: resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.398.0': - resolution: {integrity: sha512-nF1jg0L+18b5HvTcYzwyFgfZQQMELJINFqI0mi4yRKaX7T5a3aGp5RVLGGju/6tAGTuFbfBoEhkhU3kkxexPYQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-user-agent@3.678.0': resolution: {integrity: sha512-tg9cC5COgGP0cznD2ys9kxPtVeKUygPZshDWXLAfA/cH/4m2ZUBvoEVv1SxkIbvOjnPwa976rdPLQUwRZvsL0g==} engines: {node: '>=16.0.0'} @@ -2146,10 +2015,6 @@ packages: resolution: {integrity: sha512-OF+2RfRmUKyjzrRWlDcyju3RBsuqcrYDQ8TwrJg8efcOotMzuZN4U9mpVTIdATpmEc4lWNZBMSjPzrGm6JPnAQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.398.0': - resolution: {integrity: sha512-nrYgjzavGCKJL/48Vt0EL+OlIc5UZLfNGpgyUW9cv3XZwl+kXV0QB+HH0rHZZLfpbBgZ2RBIJR9uD5ieu/6hpQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/token-providers@3.667.0': resolution: {integrity: sha512-ZecJlG8p6D4UTYlBHwOWX6nknVtw/OBJ3yPXTSajBjhUlj9lE2xvejI8gl4rqkyLXk7z3bki+KR4tATbMaM9yg==} engines: {node: '>=16.0.0'} @@ -2160,10 +2025,6 @@ packages: resolution: {integrity: sha512-cBykL0LiccKIgNhGWvQRTPvsBLPZxnmJU3pYxG538jpFX8lQtrCy1L7mmIHNEdxIdIGEPgAEHF8/JQxgBToqUQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.398.0': - resolution: {integrity: sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/types@3.664.0': resolution: {integrity: sha512-+GtXktvVgpreM2b+NJL9OqZGsOzHwlCUrO8jgQUvH/yA6Kd8QO2YFhQCp0C9sSzTteZJVqGBu8E0CQurxJHPbw==} engines: {node: '>=16.0.0'} @@ -2200,10 +2061,6 @@ packages: resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.398.0': - resolution: {integrity: sha512-Fy0gLYAei/Rd6BrXG4baspCnWTUSd0NdokU1pZh4KlfEAEN1i8SPPgfiO5hLk7+2inqtCmqxVJlfqbMVe9k4bw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/util-endpoints@3.667.0': resolution: {integrity: sha512-X22SYDAuQJWnkF1/q17pkX3nGw5XMD9YEUbmt87vUnRq7iyJ3JOpl6UKOBeUBaL838wA5yzdbinmCITJ/VZ1QA==} engines: {node: '>=16.0.0'} @@ -2228,9 +2085,6 @@ packages: resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-user-agent-browser@3.398.0': - resolution: {integrity: sha512-A3Tzx1tkDHlBT+IgxmsMCHbV8LM7SwwCozq2ZjJRx0nqw3MCrrcxQFXldHeX/gdUMO+0Oocb7HGSnVODTq+0EA==} - '@aws-sdk/util-user-agent-browser@3.675.0': resolution: {integrity: sha512-HW4vGfRiX54RLcsYjLuAhcBBJ6lRVEZd7njfGpAwBB9s7BH8t48vrpYbyA5XbbqbTvXfYBnugQCUw9HWjEa1ww==} @@ -2240,15 +2094,6 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.398.0': - resolution: {integrity: sha512-RTVQofdj961ej4//fEkppFf4KXqKGMTCqJYghx3G0C/MYXbg7MGl7LjfNGtJcboRE8pfHHQ/TUWBDA7RIAPPlQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - '@aws-sdk/util-user-agent-node@3.678.0': resolution: {integrity: sha512-bKRemCdHMPAlEYE9KuQiMQG9/b4n8C+9DlJAL/X00Q7Zvm9Gv6h0+i5EZ+Xx8sbHq5oUv9a4W4tb+nkUZ0ltpw==} engines: {node: '>=16.0.0'} @@ -2279,10 +2124,6 @@ packages: '@aws-sdk/util-utf8-browser@3.259.0': resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - '@aws-sdk/xml-builder@3.310.0': - resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/xml-builder@3.972.0': resolution: {integrity: sha512-POaGMcXnozzqBUyJM3HLUZ9GR6OKJWPGJEmhtTnxZXt8B6JcJ/6K3xRJ5H/j8oovVLz8Wg6vFxAHv8lvuASxMg==} engines: {node: '>=20.0.0'} @@ -4571,18 +4412,18 @@ packages: '@octokit/types@13.6.1': resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==} - '@opennextjs/aws@3.9.12': - resolution: {integrity: sha512-lA6+ZWr3Qc/AuEJALjdC2sGmPZOKsE+exQ6ihAJBnNGX0X3VsmGRcsk3uKv8lKmZzY78H8JiDZqFp/wXb8XopA==} + '@opennextjs/aws@3.9.16': + resolution: {integrity: sha512-jQQStCysIllNCPqz5W2KSguXpr+ETlOcD8SyNu+h9zwpRVYk4uEPQge+ErG3avI5xsT8vKA7EGLYG59dhj/B6Q==} hasBin: true peerDependencies: - next: ^14.2.35 || ~15.0.7 || ~15.1.11 || ~15.2.8 || ~15.3.8 || ~15.4.10 || ~15.5.9 || ^16.0.10 + next: ~15.0.8 || ~15.1.12 || ~15.2.9 || ~15.3.9 || ~15.4.11 || ~15.5.10 || ~16.0.11 || ^16.1.5 - '@opennextjs/cloudflare@1.15.1': - resolution: {integrity: sha512-fR37Bt/ymoNCU5fX0dZd1P/OdXc0d8QnROUy+Az4Rj+rAbeCI0+sazYnP1NNfhcbHM9dJ2M6HUJBnXzab3Z5Jw==} + '@opennextjs/cloudflare@1.18.0': + resolution: {integrity: sha512-JM236YHnKzroFAZqst1t28ZGOShvnkVUDtjrp7TJ/W2P3RLo4b6npJ8VEXOn6frs6lsUfR5rvsKYLYb7h1GIJQ==} hasBin: true peerDependencies: - next: ^14.2.35 || ~15.0.7 || ~15.1.11 || ~15.2.8 || ~15.3.8 || ~15.4.10 || ~15.5.9 || ^16.0.10 - wrangler: ^4.59.2 + next: ~15.0.8 || ~15.1.12 || ~15.2.9 || ~15.3.9 || ~15.4.11 || ~15.5.10 || ~16.0.11 || ^16.1.5 + wrangler: ^4.65.0 '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} @@ -4934,10 +4775,6 @@ packages: resolution: {integrity: sha512-5imgGUlZL4dW4YWdMYAKLmal9ny/tlenM81QZY7xYyb76z9Z/QOg7oM5Ak9HQl8QfFTlGVWwcMXl+54jroRgEQ==} engines: {node: '>=14.0.0'} - '@smithy/abort-controller@2.2.0': - resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} - engines: {node: '>=14.0.0'} - '@smithy/abort-controller@3.1.6': resolution: {integrity: sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==} engines: {node: '>=16.0.0'} @@ -4962,10 +4799,6 @@ packages: resolution: {integrity: sha512-7WD9eZHp46BxAjNGHJLmxhhyeiNWkBdVStd7SUJPUZqQGeIO/REtIrcIfKUfdiHTQ9jyu2SYoqvzqqaFc6987w==} engines: {node: '>=14.0.0'} - '@smithy/config-resolver@2.2.0': - resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} - engines: {node: '>=14.0.0'} - '@smithy/config-resolver@3.0.10': resolution: {integrity: sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==} engines: {node: '>=16.0.0'} @@ -4990,10 +4823,6 @@ packages: resolution: {integrity: sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@2.3.0': - resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} - engines: {node: '>=14.0.0'} - '@smithy/credential-provider-imds@3.2.5': resolution: {integrity: sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==} engines: {node: '>=16.0.0'} @@ -5048,9 +4877,6 @@ packages: '@smithy/fetch-http-handler@1.1.0': resolution: {integrity: sha512-N22C9R44u5WGlcY+Wuv8EXmCAq62wWwriRAuoczMEwAIjPbvHSthyPSLqI4S7kAST1j6niWg8kwpeJ3ReAv3xg==} - '@smithy/fetch-http-handler@2.5.0': - resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} - '@smithy/fetch-http-handler@3.2.9': resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} @@ -5069,10 +4895,6 @@ packages: resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@2.2.0': - resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} - engines: {node: '>=14.0.0'} - '@smithy/hash-node@3.0.8': resolution: {integrity: sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==} engines: {node: '>=16.0.0'} @@ -5089,9 +4911,6 @@ packages: resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@2.2.0': - resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} - '@smithy/invalid-dependency@3.0.8': resolution: {integrity: sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==} @@ -5127,10 +4946,6 @@ packages: resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@2.2.0': - resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} - engines: {node: '>=14.0.0'} - '@smithy/middleware-content-length@3.0.10': resolution: {integrity: sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==} engines: {node: '>=16.0.0'} @@ -5143,10 +4958,6 @@ packages: resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@2.5.1': - resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} - engines: {node: '>=14.0.0'} - '@smithy/middleware-endpoint@3.2.1': resolution: {integrity: sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==} engines: {node: '>=16.0.0'} @@ -5163,10 +4974,6 @@ packages: resolution: {integrity: sha512-lINKYxIvT+W20YFOtHBKeGm7npuJg0/YCoShttU7fVpsmU+a2rdb9zrJn1MHqWfUL6DhTAWGa0tH2O7l4XrDcw==} engines: {node: '>=14.0.0'} - '@smithy/middleware-retry@2.3.1': - resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} - engines: {node: '>=14.0.0'} - '@smithy/middleware-retry@3.0.25': resolution: {integrity: sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==} engines: {node: '>=16.0.0'} @@ -5179,10 +4986,6 @@ packages: resolution: {integrity: sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@2.3.0': - resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} - engines: {node: '>=14.0.0'} - '@smithy/middleware-serde@3.0.8': resolution: {integrity: sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==} engines: {node: '>=16.0.0'} @@ -5199,10 +5002,6 @@ packages: resolution: {integrity: sha512-XynYiIvXNea2BbLcppvpNK0zu8o2woJqgnmxqYTn4FWagH/Hr2QIk8LOsUz7BIJ4tooFhmx8urHKCdlPbbPDCA==} engines: {node: '>=14.0.0'} - '@smithy/middleware-stack@2.2.0': - resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} - engines: {node: '>=14.0.0'} - '@smithy/middleware-stack@3.0.8': resolution: {integrity: sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==} engines: {node: '>=16.0.0'} @@ -5215,10 +5014,6 @@ packages: resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@2.3.0': - resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} - engines: {node: '>=14.0.0'} - '@smithy/node-config-provider@3.1.9': resolution: {integrity: sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==} engines: {node: '>=16.0.0'} @@ -5235,10 +5030,6 @@ packages: resolution: {integrity: sha512-d3kRriEgaIiGXLziAM8bjnaLn1fthCJeTLZIwEIpzQqe6yPX0a+yQoLCTyjb2fvdLwkMoG4p7THIIB5cj5lkbg==} engines: {node: '>=14.0.0'} - '@smithy/node-http-handler@2.5.0': - resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} - engines: {node: '>=14.0.0'} - '@smithy/node-http-handler@3.2.5': resolution: {integrity: sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==} engines: {node: '>=16.0.0'} @@ -5251,10 +5042,6 @@ packages: resolution: {integrity: sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@2.2.0': - resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} - engines: {node: '>=14.0.0'} - '@smithy/property-provider@3.1.8': resolution: {integrity: sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==} engines: {node: '>=16.0.0'} @@ -5271,14 +5058,6 @@ packages: resolution: {integrity: sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==} engines: {node: '>=14.0.0'} - '@smithy/protocol-http@2.0.5': - resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} - engines: {node: '>=14.0.0'} - - '@smithy/protocol-http@3.3.0': - resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} - engines: {node: '>=14.0.0'} - '@smithy/protocol-http@4.1.5': resolution: {integrity: sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==} engines: {node: '>=16.0.0'} @@ -5295,10 +5074,6 @@ packages: resolution: {integrity: sha512-gDEi4LxIGLbdfjrjiY45QNbuDmpkwh9DX4xzrR2AzjjXpxwGyfSpbJaYhXARw9p17VH0h9UewnNQXNwaQyYMDA==} engines: {node: '>=14.0.0'} - '@smithy/querystring-builder@2.2.0': - resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} - engines: {node: '>=14.0.0'} - '@smithy/querystring-builder@3.0.8': resolution: {integrity: sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==} engines: {node: '>=16.0.0'} @@ -5311,10 +5086,6 @@ packages: resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@2.2.0': - resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} - engines: {node: '>=14.0.0'} - '@smithy/querystring-parser@3.0.8': resolution: {integrity: sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==} engines: {node: '>=16.0.0'} @@ -5331,10 +5102,6 @@ packages: resolution: {integrity: sha512-OCTEeJ1igatd5kFrS2VDlYbainNNpf7Lj1siFOxnRWqYOP9oNvC5HOJBd3t+Z8MbrmehBtuDJ2QqeBsfeiNkww==} engines: {node: '>=14.0.0'} - '@smithy/service-error-classification@2.1.5': - resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} - engines: {node: '>=14.0.0'} - '@smithy/service-error-classification@3.0.8': resolution: {integrity: sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==} engines: {node: '>=16.0.0'} @@ -5347,10 +5114,6 @@ packages: resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@2.4.0': - resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} - engines: {node: '>=14.0.0'} - '@smithy/shared-ini-file-loader@3.1.9': resolution: {integrity: sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==} engines: {node: '>=16.0.0'} @@ -5383,10 +5146,6 @@ packages: resolution: {integrity: sha512-j32SGgVhv2G9nBTmel9u3OXux8KG20ssxuFakJrEeDug3kqbl1qrGzVLCe+Eib402UDtA0Sp1a4NZ2SEXDBxag==} engines: {node: '>=14.0.0'} - '@smithy/smithy-client@2.5.1': - resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} - engines: {node: '>=14.0.0'} - '@smithy/smithy-client@3.4.2': resolution: {integrity: sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==} engines: {node: '>=16.0.0'} @@ -5419,9 +5178,6 @@ packages: resolution: {integrity: sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@2.2.0': - resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} - '@smithy/url-parser@3.0.8': resolution: {integrity: sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==} @@ -5437,10 +5193,6 @@ packages: resolution: {integrity: sha512-FpYmDmVbOXAxqvoVCwqehUN0zXS+lN8V7VS9O7I8MKeVHdSTsZzlwiMEvGoyTNOXWn8luF4CTDYgNHnZViR30g==} engines: {node: '>=14.0.0'} - '@smithy/util-base64@2.3.0': - resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} - engines: {node: '>=14.0.0'} - '@smithy/util-base64@3.0.0': resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} engines: {node: '>=16.0.0'} @@ -5453,9 +5205,6 @@ packages: resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@2.2.0': - resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} - '@smithy/util-body-length-browser@3.0.0': resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} @@ -5467,10 +5216,6 @@ packages: resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@2.3.0': - resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} - engines: {node: '>=14.0.0'} - '@smithy/util-body-length-node@3.0.0': resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} engines: {node: '>=16.0.0'} @@ -5507,10 +5252,6 @@ packages: resolution: {integrity: sha512-rQ47YpNmF6Is4I9GiE3T3+0xQ+r7RKRKbmHYyGSbyep/0cSf9kteKcI0ssJTvveJ1K4QvwrxXj1tEFp/G2UqxQ==} engines: {node: '>=14.0.0'} - '@smithy/util-config-provider@2.3.0': - resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} - engines: {node: '>=14.0.0'} - '@smithy/util-config-provider@3.0.0': resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} engines: {node: '>=16.0.0'} @@ -5523,10 +5264,6 @@ packages: resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@2.2.1': - resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} - engines: {node: '>= 10.0.0'} - '@smithy/util-defaults-mode-browser@3.0.25': resolution: {integrity: sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==} engines: {node: '>= 10.0.0'} @@ -5539,10 +5276,6 @@ packages: resolution: {integrity: sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@2.3.1': - resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} - engines: {node: '>= 10.0.0'} - '@smithy/util-defaults-mode-node@3.0.25': resolution: {integrity: sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==} engines: {node: '>= 10.0.0'} @@ -5611,10 +5344,6 @@ packages: resolution: {integrity: sha512-ygQW5HBqYXpR3ua09UciS0sL7UGJzGiktrKkOuEJwARoUuzz40yaEGU6xd9Gs7KBmAaFC8gMfnghHtwZ2nyBCQ==} engines: {node: '>= 14.0.0'} - '@smithy/util-retry@2.2.0': - resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} - engines: {node: '>= 14.0.0'} - '@smithy/util-retry@3.0.8': resolution: {integrity: sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==} engines: {node: '>=16.0.0'} @@ -5631,10 +5360,6 @@ packages: resolution: {integrity: sha512-w3lsdGsntaLQIrwDWJkIFKrFscgZXwU/oxsse09aSTNv5TckPhDeYea3LhsDrU5MGAG3vprhVZAKr33S45coVA==} engines: {node: '>=14.0.0'} - '@smithy/util-stream@2.2.0': - resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} - engines: {node: '>=14.0.0'} - '@smithy/util-stream@3.2.1': resolution: {integrity: sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==} engines: {node: '>=16.0.0'} @@ -5687,10 +5412,6 @@ packages: resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@2.2.0': - resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} - engines: {node: '>=14.0.0'} - '@smithy/util-waiter@3.1.7': resolution: {integrity: sha512-d5yGlQtmN/z5eoTtIYgkvOw27US2Ous4VycnXatyoImIF9tzlcpnKqQ/V7qhvJmb2p6xZne1NopCLakdTnkBBQ==} engines: {node: '>=16.0.0'} @@ -5896,6 +5617,10 @@ packages: '@types/caseless@0.12.5': resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} + '@types/comment-json@2.4.5': + resolution: {integrity: sha512-Zc70LYxV8bri2tcIVVAYQ33dsGOvcZ3Hx7MdMKEJ+cWIt5BqpjAi5Kxcxe3rplp+g6qIenPoxx44k9nGqUstmg==} + deprecated: This is a stub types definition. comment-json provides its own type definitions, so you do not need this installed. + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -6290,6 +6015,9 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -6738,6 +6466,10 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} + comment-json@4.6.2: + resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} + engines: {node: '>= 6'} + commist@1.1.0: resolution: {integrity: sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==} @@ -7562,10 +7294,6 @@ packages: fast-xml-builder@1.1.4: resolution: {integrity: sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==} - fast-xml-parser@4.2.5: - resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} - hasBin: true - fast-xml-parser@4.4.1: resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} hasBin: true @@ -10388,9 +10116,6 @@ packages: typescript: optional: true - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} @@ -11014,72 +10739,33 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@ast-grep/napi-darwin-arm64@0.40.0': - optional: true - '@ast-grep/napi-darwin-arm64@0.40.5': optional: true - '@ast-grep/napi-darwin-x64@0.40.0': - optional: true - '@ast-grep/napi-darwin-x64@0.40.5': optional: true - '@ast-grep/napi-linux-arm64-gnu@0.40.0': - optional: true - '@ast-grep/napi-linux-arm64-gnu@0.40.5': optional: true - '@ast-grep/napi-linux-arm64-musl@0.40.0': - optional: true - '@ast-grep/napi-linux-arm64-musl@0.40.5': optional: true - '@ast-grep/napi-linux-x64-gnu@0.40.0': - optional: true - '@ast-grep/napi-linux-x64-gnu@0.40.5': optional: true - '@ast-grep/napi-linux-x64-musl@0.40.0': - optional: true - '@ast-grep/napi-linux-x64-musl@0.40.5': optional: true - '@ast-grep/napi-win32-arm64-msvc@0.40.0': - optional: true - '@ast-grep/napi-win32-arm64-msvc@0.40.5': optional: true - '@ast-grep/napi-win32-ia32-msvc@0.40.0': - optional: true - '@ast-grep/napi-win32-ia32-msvc@0.40.5': optional: true - '@ast-grep/napi-win32-x64-msvc@0.40.0': - optional: true - '@ast-grep/napi-win32-x64-msvc@0.40.5': optional: true - '@ast-grep/napi@0.40.0': - optionalDependencies: - '@ast-grep/napi-darwin-arm64': 0.40.0 - '@ast-grep/napi-darwin-x64': 0.40.0 - '@ast-grep/napi-linux-arm64-gnu': 0.40.0 - '@ast-grep/napi-linux-arm64-musl': 0.40.0 - '@ast-grep/napi-linux-x64-gnu': 0.40.0 - '@ast-grep/napi-linux-x64-musl': 0.40.0 - '@ast-grep/napi-win32-arm64-msvc': 0.40.0 - '@ast-grep/napi-win32-ia32-msvc': 0.40.0 - '@ast-grep/napi-win32-x64-msvc': 0.40.0 - '@ast-grep/napi@0.40.5': optionalDependencies: '@ast-grep/napi-darwin-arm64': 0.40.5 @@ -11144,10 +10830,6 @@ snapshots: '@aws-sdk/types': 3.973.0 tslib: 2.8.0 - '@aws-crypto/ie11-detection@3.0.0': - dependencies: - tslib: 1.14.1 - '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 @@ -11157,17 +10839,6 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.0 - '@aws-crypto/sha256-browser@3.0.0': - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.973.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 @@ -11178,32 +10849,16 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.0 - '@aws-crypto/sha256-js@3.0.0': - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.973.0 - tslib: 1.14.1 - '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 '@aws-sdk/types': 3.973.0 tslib: 2.8.0 - '@aws-crypto/supports-web-crypto@3.0.0': - dependencies: - tslib: 1.14.1 - '@aws-crypto/supports-web-crypto@5.2.0': dependencies: tslib: 2.8.0 - '@aws-crypto/util@3.0.0': - dependencies: - '@aws-sdk/types': 3.973.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - '@aws-crypto/util@5.2.0': dependencies: '@aws-sdk/types': 3.973.0 @@ -11259,51 +10914,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-cloudfront@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/credential-provider-node': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/middleware-host-header': 3.398.0 - '@aws-sdk/middleware-logger': 3.398.0 - '@aws-sdk/middleware-recursion-detection': 3.398.0 - '@aws-sdk/middleware-signing': 3.398.0 - '@aws-sdk/middleware-user-agent': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@aws-sdk/util-endpoints': 3.398.0 - '@aws-sdk/util-user-agent-browser': 3.398.0 - '@aws-sdk/util-user-agent-node': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/xml-builder': 3.310.0 - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-stream': 2.2.0 - '@smithy/util-utf8': 2.3.0 - '@smithy/util-waiter': 2.2.0 - fast-xml-parser: 4.2.5 - tslib: 2.8.0 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-cloudfront@3.984.0(aws-crt@1.23.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -12087,44 +11697,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/middleware-host-header': 3.398.0 - '@aws-sdk/middleware-logger': 3.398.0 - '@aws-sdk/middleware-recursion-detection': 3.398.0 - '@aws-sdk/middleware-user-agent': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@aws-sdk/util-endpoints': 3.398.0 - '@aws-sdk/util-user-agent-browser': 3.398.0 - '@aws-sdk/util-user-agent-node': 3.398.0(aws-crt@1.23.0) - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-sso@3.678.0(aws-crt@1.23.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -12211,48 +11783,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/credential-provider-node': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/middleware-host-header': 3.398.0 - '@aws-sdk/middleware-logger': 3.398.0 - '@aws-sdk/middleware-recursion-detection': 3.398.0 - '@aws-sdk/middleware-sdk-sts': 3.398.0 - '@aws-sdk/middleware-signing': 3.398.0 - '@aws-sdk/middleware-user-agent': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@aws-sdk/util-endpoints': 3.398.0 - '@aws-sdk/util-user-agent-browser': 3.398.0 - '@aws-sdk/util-user-agent-node': 3.398.0(aws-crt@1.23.0) - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-utf8': 2.3.0 - fast-xml-parser: 4.2.5 - tslib: 2.8.0 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -12385,13 +11915,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-env@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.678.0': dependencies: '@aws-sdk/core': 3.678.0 @@ -12455,21 +11978,6 @@ snapshots: '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-sdk/credential-provider-env': 3.398.0 - '@aws-sdk/credential-provider-process': 3.398.0 - '@aws-sdk/credential-provider-sso': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/credential-provider-web-identity': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-ini@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0))(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0)': dependencies: '@aws-sdk/client-sts': 3.678.0(aws-crt@1.23.0) @@ -12553,22 +12061,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-sdk/credential-provider-env': 3.398.0 - '@aws-sdk/credential-provider-ini': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/credential-provider-process': 3.398.0 - '@aws-sdk/credential-provider-sso': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/credential-provider-web-identity': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-node@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0))(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0)': dependencies: '@aws-sdk/credential-provider-env': 3.678.0 @@ -12622,14 +12114,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@aws-sdk/credential-provider-process@3.678.0': dependencies: '@aws-sdk/core': 3.678.0 @@ -12657,18 +12141,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-sdk/client-sso': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/token-providers': 3.398.0(aws-crt@1.23.0) - '@aws-sdk/types': 3.398.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-sso@3.678.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0))(aws-crt@1.23.0)': dependencies: '@aws-sdk/client-sso': 3.678.0(aws-crt@1.23.0) @@ -12709,13 +12181,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@aws-sdk/credential-provider-web-identity@3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))': dependencies: '@aws-sdk/client-sts': 3.678.0(aws-crt@1.23.0) @@ -12863,13 +12328,6 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@aws-sdk/middleware-host-header@3.667.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -12903,12 +12361,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@aws-sdk/middleware-logger@3.667.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -12927,13 +12379,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@aws-sdk/middleware-recursion-detection@3.667.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -13040,23 +12485,6 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-sts@3.398.0': - dependencies: - '@aws-sdk/middleware-signing': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@smithy/types': 2.12.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-signing@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/property-provider': 2.2.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/signature-v4': 2.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-middleware': 2.2.0 - tslib: 2.8.0 - '@aws-sdk/middleware-signing@3.664.0': dependencies: '@aws-sdk/types': 3.664.0 @@ -13079,14 +12507,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@aws-sdk/util-endpoints': 3.398.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@aws-sdk/middleware-user-agent@3.678.0': dependencies: '@aws-sdk/core': 3.678.0 @@ -13301,46 +12721,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/token-providers@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/middleware-host-header': 3.398.0 - '@aws-sdk/middleware-logger': 3.398.0 - '@aws-sdk/middleware-recursion-detection': 3.398.0 - '@aws-sdk/middleware-user-agent': 3.398.0 - '@aws-sdk/types': 3.398.0 - '@aws-sdk/util-endpoints': 3.398.0 - '@aws-sdk/util-user-agent-browser': 3.398.0 - '@aws-sdk/util-user-agent-node': 3.398.0(aws-crt@1.23.0) - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/property-provider': 2.2.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/token-providers@3.667.0(@aws-sdk/client-sso-oidc@3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.678.0(@aws-sdk/client-sts@3.678.0(aws-crt@1.23.0))(aws-crt@1.23.0) @@ -13362,11 +12742,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.398.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@aws-sdk/types@3.664.0': dependencies: '@smithy/types': 3.6.0 @@ -13408,11 +12783,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - tslib: 2.8.0 - '@aws-sdk/util-endpoints@3.667.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -13455,13 +12825,6 @@ snapshots: dependencies: tslib: 2.8.0 - '@aws-sdk/util-user-agent-browser@3.398.0': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/types': 2.12.0 - bowser: 2.11.0 - tslib: 2.8.0 - '@aws-sdk/util-user-agent-browser@3.675.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -13483,15 +12846,6 @@ snapshots: bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.398.0(aws-crt@1.23.0)': - dependencies: - '@aws-sdk/types': 3.398.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - optionalDependencies: - aws-crt: 1.23.0 - '@aws-sdk/util-user-agent-node@3.678.0(aws-crt@1.23.0)': dependencies: '@aws-sdk/middleware-user-agent': 3.678.0 @@ -13527,10 +12881,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/xml-builder@3.310.0': - dependencies: - tslib: 2.8.0 - '@aws-sdk/xml-builder@3.972.0': dependencies: '@smithy/types': 4.12.0 @@ -15507,10 +14857,10 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 - '@opennextjs/aws@3.9.12(aws-crt@1.23.0)(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))': + '@opennextjs/aws@3.9.16(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))': dependencies: '@ast-grep/napi': 0.40.5 - '@aws-sdk/client-cloudfront': 3.398.0(aws-crt@1.23.0) + '@aws-sdk/client-cloudfront': 3.984.0(aws-crt@1.23.0) '@aws-sdk/client-dynamodb': 3.984.0(aws-crt@1.23.0) '@aws-sdk/client-lambda': 3.984.0(aws-crt@1.23.0) '@aws-sdk/client-s3': 3.984.0(aws-crt@1.23.0) @@ -15531,12 +14881,13 @@ snapshots: - aws-crt - supports-color - '@opennextjs/cloudflare@1.15.1(aws-crt@1.23.0)(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(wrangler@4.60.0(@cloudflare/workers-types@4.20260123.0))': + '@opennextjs/cloudflare@1.18.0(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(wrangler@4.60.0(@cloudflare/workers-types@4.20260123.0))': dependencies: - '@ast-grep/napi': 0.40.0 + '@ast-grep/napi': 0.40.5 '@dotenvx/dotenvx': 1.31.0 - '@opennextjs/aws': 3.9.12(aws-crt@1.23.0)(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + '@opennextjs/aws': 3.9.16(next@16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) cloudflare: 4.5.0 + comment-json: 4.6.2 enquirer: 2.4.1 glob: 12.0.0 next: 16.1.4(@opentelemetry/api@1.9.0)(@playwright/test@1.58.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -15827,11 +15178,6 @@ snapshots: '@smithy/types': 1.2.0 tslib: 2.8.1 - '@smithy/abort-controller@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@smithy/abort-controller@3.1.6': dependencies: '@smithy/types': 3.6.0 @@ -15863,14 +15209,6 @@ snapshots: '@smithy/util-middleware': 1.1.0 tslib: 2.8.1 - '@smithy/config-resolver@2.2.0': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-config-provider': 2.3.0 - '@smithy/util-middleware': 2.2.0 - tslib: 2.8.0 - '@smithy/config-resolver@3.0.10': dependencies: '@smithy/node-config-provider': 3.1.9 @@ -15934,14 +15272,6 @@ snapshots: '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/credential-provider-imds@2.3.0': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - tslib: 2.8.1 - '@smithy/credential-provider-imds@3.2.5': dependencies: '@smithy/node-config-provider': 3.1.9 @@ -16034,14 +15364,6 @@ snapshots: '@smithy/util-base64': 1.1.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@2.5.0': - dependencies: - '@smithy/protocol-http': 3.3.0 - '@smithy/querystring-builder': 2.2.0 - '@smithy/types': 2.12.0 - '@smithy/util-base64': 2.3.0 - tslib: 2.8.0 - '@smithy/fetch-http-handler@3.2.9': dependencies: '@smithy/protocol-http': 4.1.5 @@ -16081,13 +15403,6 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.0 - '@smithy/hash-node@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.0 - '@smithy/hash-node@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16115,11 +15430,6 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.0 - '@smithy/invalid-dependency@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/invalid-dependency@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16161,12 +15471,6 @@ snapshots: '@smithy/util-utf8': 4.2.0 tslib: 2.8.0 - '@smithy/middleware-content-length@2.2.0': - dependencies: - '@smithy/protocol-http': 3.3.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/middleware-content-length@3.0.10': dependencies: '@smithy/protocol-http': 4.1.5 @@ -16185,16 +15489,6 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.0 - '@smithy/middleware-endpoint@2.5.1': - dependencies: - '@smithy/middleware-serde': 2.3.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-middleware': 2.2.0 - tslib: 2.8.0 - '@smithy/middleware-endpoint@3.2.1': dependencies: '@smithy/core': 2.5.1 @@ -16238,18 +15532,6 @@ snapshots: tslib: 2.8.1 uuid: 8.3.2 - '@smithy/middleware-retry@2.3.1': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/service-error-classification': 2.1.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/util-middleware': 2.2.0 - '@smithy/util-retry': 2.2.0 - tslib: 2.8.0 - uuid: 9.0.1 - '@smithy/middleware-retry@3.0.25': dependencies: '@smithy/node-config-provider': 3.1.9 @@ -16286,11 +15568,6 @@ snapshots: '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@2.3.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/middleware-serde@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16313,11 +15590,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/middleware-stack@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/middleware-stack@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16333,13 +15605,6 @@ snapshots: '@smithy/types': 4.12.0 tslib: 2.8.0 - '@smithy/node-config-provider@2.3.0': - dependencies: - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/node-config-provider@3.1.9': dependencies: '@smithy/property-provider': 3.1.8 @@ -16369,14 +15634,6 @@ snapshots: '@smithy/types': 1.2.0 tslib: 2.8.1 - '@smithy/node-http-handler@2.5.0': - dependencies: - '@smithy/abort-controller': 2.2.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/querystring-builder': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/node-http-handler@3.2.5': dependencies: '@smithy/abort-controller': 3.1.6 @@ -16401,11 +15658,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/property-provider@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@smithy/property-provider@3.1.8': dependencies: '@smithy/types': 3.6.0 @@ -16426,16 +15678,6 @@ snapshots: '@smithy/types': 1.2.0 tslib: 2.8.1 - '@smithy/protocol-http@2.0.5': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.0 - - '@smithy/protocol-http@3.3.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@smithy/protocol-http@4.1.5': dependencies: '@smithy/types': 3.6.0 @@ -16457,12 +15699,6 @@ snapshots: '@smithy/util-uri-escape': 1.1.0 tslib: 2.8.1 - '@smithy/querystring-builder@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-uri-escape': 2.2.0 - tslib: 2.8.1 - '@smithy/querystring-builder@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16481,11 +15717,6 @@ snapshots: '@smithy/util-uri-escape': 4.2.0 tslib: 2.8.0 - '@smithy/querystring-parser@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@smithy/querystring-parser@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16503,10 +15734,6 @@ snapshots: '@smithy/service-error-classification@1.1.0': {} - '@smithy/service-error-classification@2.1.5': - dependencies: - '@smithy/types': 2.12.0 - '@smithy/service-error-classification@3.0.8': dependencies: '@smithy/types': 3.6.0 @@ -16519,11 +15746,6 @@ snapshots: dependencies: '@smithy/types': 4.12.0 - '@smithy/shared-ini-file-loader@2.4.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.8.1 - '@smithy/shared-ini-file-loader@3.1.9': dependencies: '@smithy/types': 3.6.0 @@ -16589,15 +15811,6 @@ snapshots: '@smithy/util-stream': 1.1.0 tslib: 2.8.1 - '@smithy/smithy-client@2.5.1': - dependencies: - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-stack': 2.2.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-stream': 2.2.0 - tslib: 2.8.0 - '@smithy/smithy-client@3.4.2': dependencies: '@smithy/core': 2.5.1 @@ -16648,12 +15861,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/url-parser@2.2.0': - dependencies: - '@smithy/querystring-parser': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/url-parser@3.0.8': dependencies: '@smithy/querystring-parser': 3.0.8 @@ -16677,12 +15884,6 @@ snapshots: '@smithy/util-buffer-from': 1.1.0 tslib: 2.8.1 - '@smithy/util-base64@2.3.0': - dependencies: - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.0 - '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 @@ -16701,10 +15902,6 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-body-length-browser@2.2.0': - dependencies: - tslib: 2.8.0 - '@smithy/util-body-length-browser@3.0.0': dependencies: tslib: 2.8.0 @@ -16717,10 +15914,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@2.3.0': - dependencies: - tslib: 2.8.0 - '@smithy/util-body-length-node@3.0.0': dependencies: tslib: 2.8.0 @@ -16762,10 +15955,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-config-provider@2.3.0': - dependencies: - tslib: 2.8.1 - '@smithy/util-config-provider@3.0.0': dependencies: tslib: 2.8.1 @@ -16778,14 +15967,6 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@2.2.1': - dependencies: - '@smithy/property-provider': 2.2.0 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - bowser: 2.11.0 - tslib: 2.8.0 - '@smithy/util-defaults-mode-browser@3.0.25': dependencies: '@smithy/property-provider': 3.1.8 @@ -16808,16 +15989,6 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@2.3.1': - dependencies: - '@smithy/config-resolver': 2.2.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/util-defaults-mode-node@3.0.25': dependencies: '@smithy/config-resolver': 3.0.10 @@ -16915,12 +16086,6 @@ snapshots: '@smithy/service-error-classification': 1.1.0 tslib: 2.8.1 - '@smithy/util-retry@2.2.0': - dependencies: - '@smithy/service-error-classification': 2.1.5 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/util-retry@3.0.8': dependencies: '@smithy/service-error-classification': 3.0.8 @@ -16950,17 +16115,6 @@ snapshots: '@smithy/util-utf8': 1.1.0 tslib: 2.8.1 - '@smithy/util-stream@2.2.0': - dependencies: - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/types': 2.12.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-hex-encoding': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.0 - '@smithy/util-stream@3.2.1': dependencies: '@smithy/fetch-http-handler': 4.0.0 @@ -17039,12 +16193,6 @@ snapshots: '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/util-waiter@2.2.0': - dependencies: - '@smithy/abort-controller': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.8.0 - '@smithy/util-waiter@3.1.7': dependencies: '@smithy/abort-controller': 3.1.6 @@ -17224,6 +16372,10 @@ snapshots: '@types/caseless@0.12.5': optional: true + '@types/comment-json@2.4.5': + dependencies: + comment-json: 4.6.2 + '@types/connect@3.4.38': dependencies: '@types/node': 22.19.7 @@ -17778,6 +16930,8 @@ snapshots: array-flatten@1.1.1: {} + array-timsort@1.0.3: {} + array-union@2.1.0: {} arrify@2.0.1: @@ -18295,6 +17449,11 @@ snapshots: commander@9.5.0: {} + comment-json@4.6.2: + dependencies: + array-timsort: 1.0.3 + esprima: 4.0.1 + commist@1.1.0: dependencies: leven: 2.1.0 @@ -19153,10 +18312,6 @@ snapshots: dependencies: path-expression-matcher: 1.2.0 - fast-xml-parser@4.2.5: - dependencies: - strnum: 1.0.5 - fast-xml-parser@4.4.1: dependencies: strnum: 1.0.5 @@ -22708,8 +21863,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - tslib@1.14.1: {} - tslib@2.8.0: {} tslib@2.8.1: {}