diff --git a/.changeset/friendly-experts-eat.md b/.changeset/friendly-experts-eat.md new file mode 100644 index 00000000..0fcd1ed8 --- /dev/null +++ b/.changeset/friendly-experts-eat.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +fix deduplication for memory queue and add some log diff --git a/examples/common/apps.ts b/examples/common/apps.ts index c61bdf34..46325bc9 100644 --- a/examples/common/apps.ts +++ b/examples/common/apps.ts @@ -14,6 +14,7 @@ const apps = [ "pages-router", // overrides "d1-tag-next", + "memory-queue", // bugs "gh-119", "gh-219", diff --git a/examples/overrides/memory-queue/.gitignore b/examples/overrides/memory-queue/.gitignore new file mode 100644 index 00000000..3f753f29 --- /dev/null +++ b/examples/overrides/memory-queue/.gitignore @@ -0,0 +1,47 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/examples/overrides/memory-queue/app/favicon.ico b/examples/overrides/memory-queue/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/examples/overrides/memory-queue/app/favicon.ico differ diff --git a/examples/overrides/memory-queue/app/globals.css b/examples/overrides/memory-queue/app/globals.css new file mode 100644 index 00000000..6e6f12f3 --- /dev/null +++ b/examples/overrides/memory-queue/app/globals.css @@ -0,0 +1,14 @@ +html, +body { + max-width: 100vw; + overflow-x: hidden; + height: 100vh; + display: flex; + flex-direction: column; +} + +footer { + padding: 1rem; + display: flex; + justify-content: end; +} diff --git a/examples/overrides/memory-queue/app/layout.tsx b/examples/overrides/memory-queue/app/layout.tsx new file mode 100644 index 00000000..e878f82a --- /dev/null +++ b/examples/overrides/memory-queue/app/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export const metadata: Metadata = { + title: "SSG App", + description: "An app in which all the routes are SSG'd", +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + const cloudflareContext = await getCloudflareContext({ + async: true, + }); + + return ( + + {children} + + ); +} diff --git a/examples/overrides/memory-queue/app/page.module.css b/examples/overrides/memory-queue/app/page.module.css new file mode 100644 index 00000000..1217984e --- /dev/null +++ b/examples/overrides/memory-queue/app/page.module.css @@ -0,0 +1,17 @@ +.page { + display: grid; + grid-template-rows: 20px 1fr 20px; + align-items: center; + justify-items: center; + flex: 1; + border: 3px solid gray; + margin: 1rem; + margin-block-end: 0; +} + +.main { + display: flex; + flex-direction: column; + gap: 32px; + grid-row-start: 2; +} diff --git a/examples/overrides/memory-queue/app/page.tsx b/examples/overrides/memory-queue/app/page.tsx new file mode 100644 index 00000000..de3385c2 --- /dev/null +++ b/examples/overrides/memory-queue/app/page.tsx @@ -0,0 +1,16 @@ +import styles from "./page.module.css"; + +export const revalidate = 5; + +export default async function Home() { + // We purposefully wait for 2 seconds to allow deduplication to occur + await new Promise((resolve) => setTimeout(resolve, 2000)); + return ( +
+
+

Hello from a Statically generated page

+

{Date.now()}

+
+
+ ); +} diff --git a/examples/overrides/memory-queue/e2e/base.spec.ts b/examples/overrides/memory-queue/e2e/base.spec.ts new file mode 100644 index 00000000..6336893c --- /dev/null +++ b/examples/overrides/memory-queue/e2e/base.spec.ts @@ -0,0 +1,36 @@ +import { test, expect } from "@playwright/test"; + +test.describe("memory-queue", () => { + test("the index page should work", async ({ page }) => { + await page.goto("/"); + await expect(page.getByText("Hello from a Statically generated page")).toBeVisible(); + }); + + test("the index page should revalidate", async ({ page, request }) => { + // We need to make sure the page is loaded and is a HIT + // If it is STALE, the next hit may have an updated date and thus fail the test + let cacheHeaders = ""; + do { + const req = await request.get("/"); + cacheHeaders = req.headers()["x-nextjs-cache"]; + await page.waitForTimeout(500); + } while (cacheHeaders !== "HIT"); + + await page.goto("/"); + const firstDate = await page.getByTestId("date-local").textContent(); + + await page.reload(); + let newDate = await page.getByTestId("date-local").textContent(); + expect(newDate).toBe(firstDate); + + await page.waitForTimeout(5000); + + do { + await page.reload(); + newDate = await page.getByTestId("date-local").textContent(); + await page.waitForTimeout(1000); + } while (newDate === firstDate); + + expect(newDate).not.toBe(firstDate); + }); +}); diff --git a/examples/overrides/memory-queue/e2e/playwright.config.ts b/examples/overrides/memory-queue/e2e/playwright.config.ts new file mode 100644 index 00000000..6a99f627 --- /dev/null +++ b/examples/overrides/memory-queue/e2e/playwright.config.ts @@ -0,0 +1,8 @@ +import { configurePlaywright } from "../../../common/config-e2e"; + +// Here we don't want to run the tests in parallel +export default configurePlaywright("memory-queue", { + isCI: !!process.env.CI, + parallel: false, + multipleBrowsers: false, +}); diff --git a/examples/overrides/memory-queue/next.config.ts b/examples/overrides/memory-queue/next.config.ts new file mode 100644 index 00000000..4b075e44 --- /dev/null +++ b/examples/overrides/memory-queue/next.config.ts @@ -0,0 +1,11 @@ +import type { NextConfig } from "next"; +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; + +initOpenNextCloudflareForDev(); + +const nextConfig: NextConfig = { + typescript: { ignoreBuildErrors: true }, + eslint: { ignoreDuringBuilds: true }, +}; + +export default nextConfig; diff --git a/examples/overrides/memory-queue/open-next.config.ts b/examples/overrides/memory-queue/open-next.config.ts new file mode 100644 index 00000000..992d7fc8 --- /dev/null +++ b/examples/overrides/memory-queue/open-next.config.ts @@ -0,0 +1,8 @@ +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; +import memoryQueue from "@opennextjs/cloudflare/memory-queue"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + queue: memoryQueue, +}); diff --git a/examples/overrides/memory-queue/package.json b/examples/overrides/memory-queue/package.json new file mode 100644 index 00000000..0ed10a10 --- /dev/null +++ b/examples/overrides/memory-queue/package.json @@ -0,0 +1,28 @@ +{ + "name": "memory-queue", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "build:worker": "opennextjs-cloudflare", + "preview": "pnpm build:worker && pnpm wrangler dev", + "e2e": "playwright test -c e2e/playwright.config.ts" + }, + "dependencies": { + "react": "catalog:e2e", + "react-dom": "catalog:e2e", + "next": "catalog:e2e" + }, + "devDependencies": { + "@opennextjs/cloudflare": "workspace:*", + "@playwright/test": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:e2e", + "@types/react-dom": "catalog:e2e", + "typescript": "catalog:", + "wrangler": "catalog:" + } +} diff --git a/examples/overrides/memory-queue/tsconfig.json b/examples/overrides/memory-queue/tsconfig.json new file mode 100644 index 00000000..d8b93235 --- /dev/null +++ b/examples/overrides/memory-queue/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/overrides/memory-queue/wrangler.jsonc b/examples/overrides/memory-queue/wrangler.jsonc new file mode 100644 index 00000000..2970e70a --- /dev/null +++ b/examples/overrides/memory-queue/wrangler.jsonc @@ -0,0 +1,23 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "memory-queue", + "compatibility_date": "2025-02-04", + "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS" + }, + "kv_namespaces": [ + { + "binding": "NEXT_CACHE_WORKERS_KV", + "id": "" + } + ], + "services": [ + { + "binding": "NEXT_CACHE_REVALIDATION_WORKER", + "service": "memory-queue" + } + ] +} diff --git a/packages/cloudflare/src/api/memory-queue.spec.ts b/packages/cloudflare/src/api/memory-queue.spec.ts index 27c5e3d1..4a9c673e 100644 --- a/packages/cloudflare/src/api/memory-queue.spec.ts +++ b/packages/cloudflare/src/api/memory-queue.spec.ts @@ -31,7 +31,7 @@ describe("MemoryQueue", () => { const firstRequest = cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/test"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/test", }); vi.advanceTimersByTime(DEFAULT_REVALIDATION_TIMEOUT_MS); await firstRequest; @@ -40,7 +40,7 @@ describe("MemoryQueue", () => { const secondRequest = cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/test"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/test", }); vi.advanceTimersByTime(1); await secondRequest; @@ -51,7 +51,7 @@ describe("MemoryQueue", () => { const firstRequest = cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/test"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/test", }); vi.advanceTimersByTime(1); await firstRequest; @@ -60,7 +60,7 @@ describe("MemoryQueue", () => { const secondRequest = cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/other"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/other", }); vi.advanceTimersByTime(1); await secondRequest; @@ -72,12 +72,12 @@ describe("MemoryQueue", () => { cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/test"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/test", }), cache.send({ MessageBody: generateMessageBody({ host: "test.local", url: "/test" }), MessageGroupId: generateMessageGroupId("/test"), - MessageDeduplicationId: "", + MessageDeduplicationId: "/test", }), ]; vi.advanceTimersByTime(1); diff --git a/packages/cloudflare/src/api/memory-queue.ts b/packages/cloudflare/src/api/memory-queue.ts index 4ebd0e38..338b4353 100644 --- a/packages/cloudflare/src/api/memory-queue.ts +++ b/packages/cloudflare/src/api/memory-queue.ts @@ -1,4 +1,4 @@ -import logger from "@opennextjs/aws/logger.js"; +import { debug, error } from "@opennextjs/aws/adapters/logger.js"; import type { Queue, QueueMessage } from "@opennextjs/aws/types/overrides.js"; import { IgnorableError } from "@opennextjs/aws/utils/error.js"; @@ -16,21 +16,17 @@ export const DEFAULT_REVALIDATION_TIMEOUT_MS = 10_000; export class MemoryQueue implements Queue { readonly name = "memory-queue"; - revalidatedPaths = new Map>(); + revalidatedPaths = new Set(); constructor(private opts = { revalidationTimeoutMs: DEFAULT_REVALIDATION_TIMEOUT_MS }) {} - async send({ MessageBody: { host, url }, MessageGroupId }: QueueMessage): Promise { + async send({ MessageBody: { host, url }, MessageDeduplicationId }: QueueMessage): Promise { const service = getCloudflareContext().env.NEXT_CACHE_REVALIDATION_WORKER; if (!service) throw new IgnorableError("No service binding for cache revalidation worker"); - if (this.revalidatedPaths.has(MessageGroupId)) return; + if (this.revalidatedPaths.has(MessageDeduplicationId)) return; - this.revalidatedPaths.set( - MessageGroupId, - // force remove to allow new revalidations incase something went wrong - setTimeout(() => this.revalidatedPaths.delete(MessageGroupId), this.opts.revalidationTimeoutMs) - ); + this.revalidatedPaths.add(MessageDeduplicationId); try { const protocol = host.includes("localhost") ? "http" : "https"; @@ -38,18 +34,25 @@ export class MemoryQueue implements Queue { // TODO: Drop the import - https://github.com/opennextjs/opennextjs-cloudflare/issues/361 // @ts-ignore const manifest = await import("./.next/prerender-manifest.json"); - await service.fetch(`${protocol}://${host}${url}`, { + const response = await service.fetch(`${protocol}://${host}${url}`, { method: "HEAD", headers: { "x-prerender-revalidate": manifest.preview.previewModeId, "x-isr": "1", }, + // We want to timeout the revalidation to avoid hanging the queue + signal: AbortSignal.timeout(this.opts.revalidationTimeoutMs), }); + + // Here we want at least to log when the revalidation was not successful + if (response.status !== 200 || response.headers.get("x-nextjs-cache") !== "REVALIDATED") { + error(`Revalidation failed for ${url} with status ${response.status}`); + } + debug(`Revalidation successful for ${url}`); } catch (e) { - logger.error(e); + error(e); } finally { - clearTimeout(this.revalidatedPaths.get(MessageGroupId)); - this.revalidatedPaths.delete(MessageGroupId); + this.revalidatedPaths.delete(MessageDeduplicationId); } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77b00c6b..5b14f927 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -717,6 +717,40 @@ importers: specifier: 'catalog:' version: 3.114.1(@cloudflare/workers-types@4.20250224.0) + examples/overrides/memory-queue: + dependencies: + next: + specifier: catalog:e2e + version: 15.2.2(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: + specifier: catalog:e2e + version: 19.0.0 + react-dom: + specifier: catalog:e2e + version: 19.0.0(react@19.0.0) + devDependencies: + '@opennextjs/cloudflare': + specifier: workspace:* + version: link:../../../packages/cloudflare + '@playwright/test': + specifier: 'catalog:' + version: 1.51.1 + '@types/node': + specifier: 'catalog:' + version: 22.2.0 + '@types/react': + specifier: catalog:e2e + version: 19.0.0 + '@types/react-dom': + specifier: catalog:e2e + version: 19.0.0 + typescript: + specifier: 'catalog:' + version: 5.7.3 + wrangler: + specifier: 'catalog:' + version: 3.114.1(@cloudflare/workers-types@4.20250224.0) + examples/playground14: dependencies: next: @@ -14347,7 +14381,7 @@ snapshots: '@types/react-dom@19.0.0': dependencies: - '@types/react': 19.0.0 + '@types/react': 19.0.8 '@types/react-dom@19.0.3(@types/react@19.0.8)': dependencies: @@ -16180,7 +16214,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 @@ -16199,7 +16233,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 @@ -16218,7 +16252,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 9.11.1(jiti@1.21.6) - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.11.1(jiti@1.21.6)) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6)) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 @@ -16237,7 +16271,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 9.19.0(jiti@1.21.6) - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0(jiti@1.21.6)) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0(jiti@1.21.6)))(eslint@9.19.0(jiti@1.21.6)) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 @@ -16250,7 +16284,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.7.0(eslint@8.57.1)(typescript@5.7.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -16261,7 +16306,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.11.1(jiti@1.21.6)): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16272,7 +16317,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0(jiti@1.21.6)): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0(jiti@1.21.6)))(eslint@9.19.0(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16283,7 +16328,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: @@ -16294,7 +16339,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.11.1(jiti@1.21.6)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16305,7 +16350,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0(jiti@1.21.6)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0(jiti@1.21.6)))(eslint@9.19.0(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: @@ -16327,7 +16372,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -16355,7 +16400,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@8.57.1)(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -16384,7 +16429,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.11.1(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.11.1(jiti@1.21.6)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.11.1(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.11.1(jiti@1.21.6)))(eslint@9.11.1(jiti@1.21.6)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -16413,7 +16458,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.19.0(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@9.19.0(jiti@1.21.6)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.7.0(eslint@9.19.0(jiti@1.21.6))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@9.19.0(jiti@1.21.6)))(eslint@9.19.0(jiti@1.21.6)) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3