From aa70f29e5b677900007f932ea4f2249d6fc0f527 Mon Sep 17 00:00:00 2001 From: Eliott Gandiolle Date: Mon, 21 Sep 2026 17:20:48 +0200 Subject: [PATCH 1/3] feat: support TanStack Query v5 The detector was already v5-aware: `getPendingQueries()` matches both the v5 `pending` and the v4 `loading` status, and every core API it uses (`matchQuery`, `notifyManager.batchCalls`, `QueryCache.subscribe`, `queryCache.findAll({ predicate })`) is unchanged in v5. Only the peerDependency range `~4` blocked v5 apps from installing the package. Widen it to `>=4 <6` and note both supported majors in the README. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 ++ package.json | 2 +- yarn.lock | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f7dfc35..20f6427 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,5 @@ yarn add @bam.tech/tanstack-query-detect-waterfall --dev # or bun add @bam.tech/tanstack-query-detect-waterfall --dev ``` + +Works with both TanStack Query v4 and v5: the detector handles the `loading` (v4) and `pending` (v5) query statuses. diff --git a/package.json b/package.json index c6c9399..e2f5852 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,6 @@ "typescript": "^5.4.5" }, "peerDependencies": { - "@tanstack/query-core": "~4" + "@tanstack/query-core": ">=4 <6" } } diff --git a/yarn.lock b/yarn.lock index 42202c7..2179aa1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,7 +15,7 @@ __metadata: tsup: "npm:^8.0.2" typescript: "npm:^5.4.5" peerDependencies: - "@tanstack/query-core": ~4 + "@tanstack/query-core": ">=4 <6" languageName: unknown linkType: soft From 815eabf01610e30e73ad83d6778930885d80b6f2 Mon Sep 17 00:00:00 2001 From: Eliott Gandiolle Date: Mon, 21 Sep 2026 17:37:09 +0200 Subject: [PATCH 2/3] test: prove v4/v5 support with a real QueryClient suite and CI Add vitest (natural fit with the existing tsup/esbuild stack, no extra transform config) and run the same behavioural suite against both majors via two vitest projects: `query-core-v5` uses the installed v5, and `query-core-v4` aliases `@tanstack/query-core` to a `npm:@tanstack/query-core@^4` dev dependency. The tests drive an actual QueryClient (no mocks of the query cache) and assert on the warning payload: sequential queries, parallel queries, whitelisting, a three-query chain, and the unsubscribe handle. Add a GitHub Actions workflow running typecheck, build and the suite, matrixed over query-core 4 and 5. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 38 + package.json | 8 +- src/__tests__/detectQueryWaterfalls.test.ts | 144 +++ vitest.config.mts | 29 + yarn.lock | 1054 ++++++++++++++++++- 5 files changed, 1268 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 src/__tests__/detectQueryWaterfalls.test.ts create mode 100644 vitest.config.mts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6dda9d2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + name: query-core v${{ matrix.query-core }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + query-core: [4, 5] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Enable corepack + run: corepack enable + + - name: Install + run: yarn install --immutable + + - name: Typecheck + run: yarn typecheck + + - name: Build + run: yarn build + + # Each matrix leg runs the same suite against one major: the `query-core-v4` + # vitest project resolves `@tanstack/query-core` to the v4 alias dependency. + - name: Test + run: yarn vitest run --project query-core-v${{ matrix.query-core }} diff --git a/package.json b/package.json index e2f5852..d575de1 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "url": "git@github.com:bamlab/tanstack-query-detect-waterfall.git" }, "scripts": { - "build": "tsup" + "build": "tsup", + "test": "vitest run", + "typecheck": "tsc --noEmit" }, "main": "dist/index.js", "module": "dist/index.mjs", @@ -26,9 +28,11 @@ }, "devDependencies": { "@tanstack/query-core": "~5", + "@tanstack/query-core-v4": "npm:@tanstack/query-core@^4", "@types/lodash": "~4", "tsup": "^8.0.2", - "typescript": "^5.4.5" + "typescript": "^5.4.5", + "vitest": "^3" }, "peerDependencies": { "@tanstack/query-core": ">=4 <6" diff --git a/src/__tests__/detectQueryWaterfalls.test.ts b/src/__tests__/detectQueryWaterfalls.test.ts new file mode 100644 index 0000000..6ca94fd --- /dev/null +++ b/src/__tests__/detectQueryWaterfalls.test.ts @@ -0,0 +1,144 @@ +import { QueryClient } from "@tanstack/query-core"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { detectQueryWaterfalls } from "../index"; + +/** + * These tests drive a real `QueryClient` from `@tanstack/query-core` (no mocks of + * the query cache) and assert on the payload the detector actually hands to + * `console.warn`. The same file runs twice, once per major, via the two vitest + * projects declared in `vitest.config.ts`. + */ + +type Deferred = { promise: Promise; resolve: (value: T) => void }; + +const deferred = (): Deferred => { + let resolve!: (value: T) => void; + const promise = new Promise((res) => { + resolve = res; + }); + return { promise, resolve }; +}; + +/** Let the notifyManager's scheduled batches (setTimeout 0) drain. */ +const flush = async () => { + for (let i = 0; i < 5; i++) { + await new Promise((resolve) => setTimeout(resolve, 0)); + } +}; + +let queryClient: QueryClient; +let warn: ReturnType; + +/** The `from --> to` lines of every warning emitted so far, in order. */ +const detectedWaterfalls = (): string[] => + warn.mock.calls.flatMap(([message]) => + String(message) + .split("\n") + .filter((line) => line.includes(" --> ")) + .map((line) => line.trim()) + ); + +beforeEach(() => { + queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + warn = vi.spyOn(console, "warn").mockImplementation(() => undefined); +}); + +afterEach(() => { + warn.mockRestore(); + queryClient.clear(); +}); + +/** + * Runs `queryKeys` strictly one after the other: each query only starts once the + * previous one has settled, which is exactly the dependent-query waterfall shape. + */ +const runSequentially = async (queryKeys: string[]) => { + for (const key of queryKeys) { + const gate = deferred(); + const query = queryClient.fetchQuery({ queryKey: [key], queryFn: () => gate.promise }); + await flush(); + gate.resolve(key); + await query; + } + await flush(); +}; + +describe("detectQueryWaterfalls", () => { + it("reports exactly one waterfall, with the right from/to keys, for two sequential queries", async () => { + detectQueryWaterfalls(queryClient); + + await runSequentially(["first", "second"]); + + expect(detectedWaterfalls()).toEqual(['["first"] --> ["second"]']); + expect(warn).toHaveBeenCalledTimes(1); + expect(String(warn.mock.calls[0][0])).toContain("Detected query waterfalls"); + }); + + it("reports nothing for two parallel queries", async () => { + detectQueryWaterfalls(queryClient); + + const first = deferred(); + const second = deferred(); + const queries = Promise.all([ + queryClient.fetchQuery({ queryKey: ["first"], queryFn: () => first.promise }), + queryClient.fetchQuery({ queryKey: ["second"], queryFn: () => second.promise }), + ]); + await flush(); + first.resolve("first"); + second.resolve("second"); + await queries; + await flush(); + + expect(detectedWaterfalls()).toEqual([]); + expect(warn).not.toHaveBeenCalled(); + }); + + it("reports nothing when the sequential pair is whitelisted", async () => { + detectQueryWaterfalls(queryClient, { whitelist: [{ from: ["first"], to: ["second"] }] }); + + await runSequentially(["first", "second"]); + + expect(detectedWaterfalls()).toEqual([]); + expect(warn).not.toHaveBeenCalled(); + }); + + it("only whitelists the exact pair, not the whole chain", async () => { + detectQueryWaterfalls(queryClient, { whitelist: [{ from: ["first"], to: ["second"] }] }); + + await runSequentially(["first", "second", "third"]); + + expect(detectedWaterfalls()).toEqual(['["second"] --> ["third"]']); + }); + + it("reports each link of a three-query chain separately", async () => { + detectQueryWaterfalls(queryClient); + + await runSequentially(["first", "second", "third"]); + + expect(detectedWaterfalls()).toEqual(['["first"] --> ["second"]', '["second"] --> ["third"]']); + expect(warn).toHaveBeenCalledTimes(2); + }); + + it("stops detecting once the returned unsubscribe function is called", async () => { + const unsubscribe = detectQueryWaterfalls(queryClient); + expect(typeof unsubscribe).toBe("function"); + + unsubscribe(); + await runSequentially(["first", "second"]); + + expect(detectedWaterfalls()).toEqual([]); + expect(warn).not.toHaveBeenCalled(); + }); + + it("keeps detecting waterfalls that start after an unrelated query has settled", async () => { + detectQueryWaterfalls(queryClient); + + // A first, isolated query: it settles with nothing else in flight, so it is + // not the source of a waterfall. + await runSequentially(["standalone"]); + expect(detectedWaterfalls()).toEqual([]); + + await runSequentially(["first", "second"]); + expect(detectedWaterfalls()).toEqual(['["first"] --> ["second"]']); + }); +}); diff --git a/vitest.config.mts b/vitest.config.mts new file mode 100644 index 0000000..ef6cd05 --- /dev/null +++ b/vitest.config.mts @@ -0,0 +1,29 @@ +import { defineConfig } from "vitest/config"; + +// The library imports `@tanstack/query-core` directly, so the only honest way to +// prove it works on both majors is to run the very same suite twice with that +// specifier resolved to each major. `@tanstack/query-core-v4` is an npm alias +// devDependency (`npm:@tanstack/query-core@^4`). +export default defineConfig({ + test: { + projects: [ + { + test: { + name: "query-core-v5", + include: ["src/**/*.test.ts"], + environment: "node", + }, + }, + { + resolve: { + alias: [{ find: /^@tanstack\/query-core$/, replacement: "@tanstack/query-core-v4" }], + }, + test: { + name: "query-core-v4", + include: ["src/**/*.test.ts"], + environment: "node", + }, + }, + ], + }, +}); diff --git a/yarn.lock b/yarn.lock index 2179aa1..00fce0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,10 +10,12 @@ __metadata: resolution: "@bam.tech/tanstack-query-detect-waterfall@workspace:." dependencies: "@tanstack/query-core": "npm:~5" + "@tanstack/query-core-v4": "npm:@tanstack/query-core@^4" "@types/lodash": "npm:~4" lodash: "npm:~4" tsup: "npm:^8.0.2" typescript: "npm:^5.4.5" + vitest: "npm:^3" peerDependencies: "@tanstack/query-core": ">=4 <6" languageName: unknown @@ -26,6 +28,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/aix-ppc64@npm:0.28.2" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/android-arm64@npm:0.19.12" @@ -33,6 +42,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-arm64@npm:0.28.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/android-arm@npm:0.19.12" @@ -40,6 +56,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-arm@npm:0.28.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/android-x64@npm:0.19.12" @@ -47,6 +70,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/android-x64@npm:0.28.2" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/darwin-arm64@npm:0.19.12" @@ -54,6 +84,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/darwin-arm64@npm:0.28.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/darwin-x64@npm:0.19.12" @@ -61,6 +98,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/darwin-x64@npm:0.28.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/freebsd-arm64@npm:0.19.12" @@ -68,6 +112,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/freebsd-arm64@npm:0.28.2" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/freebsd-x64@npm:0.19.12" @@ -75,6 +126,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/freebsd-x64@npm:0.28.2" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-arm64@npm:0.19.12" @@ -82,6 +140,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-arm64@npm:0.28.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-arm@npm:0.19.12" @@ -89,6 +154,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-arm@npm:0.28.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-ia32@npm:0.19.12" @@ -96,6 +168,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-ia32@npm:0.28.2" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-loong64@npm:0.19.12" @@ -103,6 +182,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-loong64@npm:0.28.2" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-mips64el@npm:0.19.12" @@ -110,6 +196,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-mips64el@npm:0.28.2" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-ppc64@npm:0.19.12" @@ -117,6 +210,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-ppc64@npm:0.28.2" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-riscv64@npm:0.19.12" @@ -124,6 +224,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-riscv64@npm:0.28.2" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-s390x@npm:0.19.12" @@ -131,6 +238,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-s390x@npm:0.28.2" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/linux-x64@npm:0.19.12" @@ -138,6 +252,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/linux-x64@npm:0.28.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/netbsd-arm64@npm:0.28.2" + conditions: os=netbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/netbsd-x64@npm:0.19.12" @@ -145,6 +273,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/netbsd-x64@npm:0.28.2" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openbsd-arm64@npm:0.28.2" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/openbsd-x64@npm:0.19.12" @@ -152,6 +294,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openbsd-x64@npm:0.28.2" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openharmony-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/openharmony-arm64@npm:0.28.2" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/sunos-x64@npm:0.19.12" @@ -159,6 +315,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/sunos-x64@npm:0.28.2" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/win32-arm64@npm:0.19.12" @@ -166,6 +329,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-arm64@npm:0.28.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/win32-ia32@npm:0.19.12" @@ -173,6 +343,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-ia32@npm:0.28.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/win32-x64@npm:0.19.12" @@ -180,6 +357,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.28.2": + version: 0.28.2 + resolution: "@esbuild/win32-x64@npm:0.28.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -226,6 +410,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.5.5": + version: 1.6.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.6.0" + checksum: 10c0/b5be700e45a775f218589c3466c4ffea630582b4988657652da464e5ab8a7d18bf928ee4fd2363fb346ede4bea9c6ff0bae05a538358c4565ece6199531b5f72 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.24": version: 0.3.25 resolution: "@jridgewell/trace-mapping@npm:0.3.25" @@ -236,6 +427,13 @@ __metadata: languageName: node linkType: hard +"@napi-rs/lzma-linux-x64-gnu@npm:1.5.1": + version: 1.5.1 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.5.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -299,6 +497,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.63.4" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-android-arm64@npm:4.17.2" @@ -306,6 +511,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-android-arm64@npm:4.63.4" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-darwin-arm64@npm:4.17.2" @@ -313,6 +525,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-darwin-arm64@npm:4.63.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-darwin-x64@npm:4.17.2" @@ -320,6 +539,27 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-darwin-x64@npm:4.63.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.63.4" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@rollup/rollup-freebsd-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-freebsd-x64@npm:4.63.4" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.17.2" @@ -327,6 +567,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.63.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-musleabihf@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.17.2" @@ -334,6 +581,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.63.4" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.17.2" @@ -341,6 +595,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.63.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.17.2" @@ -348,6 +609,27 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.63.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-loong64-gnu@npm:4.63.4" + conditions: os=linux & cpu=loong64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-loong64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-loong64-musl@npm:4.63.4" + conditions: os=linux & cpu=loong64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.17.2" @@ -355,6 +637,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-ppc64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-ppc64-gnu@npm:4.63.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-ppc64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-ppc64-musl@npm:4.63.4" + conditions: os=linux & cpu=ppc64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.17.2" @@ -362,6 +658,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.63.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.63.4" + conditions: os=linux & cpu=riscv64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-s390x-gnu@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.17.2" @@ -369,6 +679,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.63.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.17.2" @@ -376,6 +693,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.63.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-linux-x64-musl@npm:4.17.2" @@ -383,6 +707,27 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.63.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-openbsd-x64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-openbsd-x64@npm:4.63.4" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@rollup/rollup-openharmony-arm64@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-openharmony-arm64@npm:4.63.4" + conditions: os=openharmony & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.17.2" @@ -390,6 +735,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.63.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.17.2" @@ -397,6 +749,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.63.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@rollup/rollup-win32-x64-gnu@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-x64-gnu@npm:4.63.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.17.2": version: 4.17.2 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.17.2" @@ -404,6 +770,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.63.4": + version: 4.63.4 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.63.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tanstack/query-core-v4@npm:@tanstack/query-core@^4": + version: 4.44.0 + resolution: "@tanstack/query-core@npm:4.44.0" + checksum: 10c0/6b8967cbe756d105a43f1ce6e11cf3ba52697c8a53d5780fd5cd234cda6dbe22b6e0a8902af8c891fd3c9daadd7f238a9325f5bbd1bcab4abc4e58a80320882e + languageName: node + linkType: hard + "@tanstack/query-core@npm:~5": version: 5.36.0 resolution: "@tanstack/query-core@npm:5.36.0" @@ -411,6 +791,23 @@ __metadata: languageName: node linkType: hard +"@types/chai@npm:^5.2.2": + version: 5.2.3 + resolution: "@types/chai@npm:5.2.3" + dependencies: + "@types/deep-eql": "npm:*" + assertion-error: "npm:^2.0.1" + checksum: 10c0/e0ef1de3b6f8045a5e473e867c8565788c444271409d155588504840ad1a53611011f85072188c2833941189400228c1745d78323dac13fcede9c2b28bacfb2f + languageName: node + linkType: hard + +"@types/deep-eql@npm:*": + version: 4.0.2 + resolution: "@types/deep-eql@npm:4.0.2" + checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844 + languageName: node + linkType: hard + "@types/estree@npm:1.0.5": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" @@ -418,6 +815,13 @@ __metadata: languageName: node linkType: hard +"@types/estree@npm:1.0.9, @types/estree@npm:^1.0.0": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 + languageName: node + linkType: hard + "@types/lodash@npm:~4": version: 4.17.1 resolution: "@types/lodash@npm:4.17.1" @@ -425,6 +829,89 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/expect@npm:3.2.7" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/spy": "npm:3.2.7" + "@vitest/utils": "npm:3.2.7" + chai: "npm:^5.2.0" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/5a13fee261d1020d47a3517f4d9a2cb209d7475399c815f6ebe22be116ddb0c6c401592f07d24f9a8b1c192155d11f651397ee28061f886ea61f1f9181a8fd48 + languageName: node + linkType: hard + +"@vitest/mocker@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/mocker@npm:3.2.7" + dependencies: + "@vitest/spy": "npm:3.2.7" + estree-walker: "npm:^3.0.3" + magic-string: "npm:^0.30.17" + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + checksum: 10c0/e8d9a155723e77b3e14c5a1eba35d8966b94420ebc733ffd7119b7bede22006580c468eec2aec6612fb77852cbcdfa94c6f49218f3ca1427aa9363d545c624fd + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:3.2.7, @vitest/pretty-format@npm:^3.2.7": + version: 3.2.7 + resolution: "@vitest/pretty-format@npm:3.2.7" + dependencies: + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/f556dd5f9e5240c7ab054d795622292c1b23f6aad3332d1e250f33d801783efbb7883387640b76d22cc91b81f30cb735b40371ec3e4f5293e735495f45d624df + languageName: node + linkType: hard + +"@vitest/runner@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/runner@npm:3.2.7" + dependencies: + "@vitest/utils": "npm:3.2.7" + pathe: "npm:^2.0.3" + strip-literal: "npm:^3.0.0" + checksum: 10c0/d73ba6df95175ea2b545d37fde3e743d113ecd608693b444af1d4dc27956abe4d5b500e25e09ffe46ce720de6b7cec68c264fca20366fc6da0d9ce75c52047cc + languageName: node + linkType: hard + +"@vitest/snapshot@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/snapshot@npm:3.2.7" + dependencies: + "@vitest/pretty-format": "npm:3.2.7" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + checksum: 10c0/c58a17a2c98788e77d30d39837c024852eca91618efe9b53ec399cf76332365b8cc592a69617e0f7d696df716dcc341dac61c6cbea86ae98cdb49a4a84f48d19 + languageName: node + linkType: hard + +"@vitest/spy@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/spy@npm:3.2.7" + dependencies: + tinyspy: "npm:^4.0.3" + checksum: 10c0/cf2728b4ddc6137e0ca749215c7714845e8221a5f799e933bc623216a9958fd8b032cdd7f033ed8375df9e6ed84a18b64687ca2530f50e8a76bc3b1d16d88c42 + languageName: node + linkType: hard + +"@vitest/utils@npm:3.2.7": + version: 3.2.7 + resolution: "@vitest/utils@npm:3.2.7" + dependencies: + "@vitest/pretty-format": "npm:3.2.7" + loupe: "npm:^3.1.4" + tinyrainbow: "npm:^2.0.0" + checksum: 10c0/fea57d6cf66853926f54ea6e9bd249b707120b0abba565d96a3880c7f04d5c5cd4778546879b9074cc73c0b81b475c1873791d4b3b3e5a324115b5bcfd3a46bb + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -505,6 +992,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -548,7 +1042,7 @@ __metadata: languageName: node linkType: hard -"cac@npm:^6.7.12": +"cac@npm:^6.7.12, cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 @@ -575,6 +1069,26 @@ __metadata: languageName: node linkType: hard +"chai@npm:^5.2.0": + version: 5.3.3 + resolution: "chai@npm:5.3.3" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/b360fd4d38861622e5010c2f709736988b05c7f31042305fa3f4e9911f6adb80ccfb4e302068bf8ed10e835c2e2520cba0f5edc13d878b886987e5aa62483f53 + languageName: node + linkType: hard + +"check-error@npm:^2.1.1": + version: 2.1.3 + resolution: "check-error@npm:2.1.3" + checksum: 10c0/878e99038fb6476316b74668cd6a498c7e66df3efe48158fa40db80a06ba4258742ac3ee2229c4a2a98c5e73f5dff84eb3e50ceb6b65bbd8f831eafc8338607d + languageName: node + linkType: hard + "chokidar@npm:^3.5.1": version: 3.6.0 resolution: "chokidar@npm:3.6.0" @@ -654,6 +1168,25 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.4.1": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -707,6 +1240,13 @@ __metadata: languageName: node linkType: hard +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b + languageName: node + linkType: hard + "esbuild@npm:^0.19.2": version: 0.19.12 resolution: "esbuild@npm:0.19.12" @@ -787,6 +1327,104 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.27.0 || ^0.28.0": + version: 0.28.2 + resolution: "esbuild@npm:0.28.2" + dependencies: + "@esbuild/aix-ppc64": "npm:0.28.2" + "@esbuild/android-arm": "npm:0.28.2" + "@esbuild/android-arm64": "npm:0.28.2" + "@esbuild/android-x64": "npm:0.28.2" + "@esbuild/darwin-arm64": "npm:0.28.2" + "@esbuild/darwin-x64": "npm:0.28.2" + "@esbuild/freebsd-arm64": "npm:0.28.2" + "@esbuild/freebsd-x64": "npm:0.28.2" + "@esbuild/linux-arm": "npm:0.28.2" + "@esbuild/linux-arm64": "npm:0.28.2" + "@esbuild/linux-ia32": "npm:0.28.2" + "@esbuild/linux-loong64": "npm:0.28.2" + "@esbuild/linux-mips64el": "npm:0.28.2" + "@esbuild/linux-ppc64": "npm:0.28.2" + "@esbuild/linux-riscv64": "npm:0.28.2" + "@esbuild/linux-s390x": "npm:0.28.2" + "@esbuild/linux-x64": "npm:0.28.2" + "@esbuild/netbsd-arm64": "npm:0.28.2" + "@esbuild/netbsd-x64": "npm:0.28.2" + "@esbuild/openbsd-arm64": "npm:0.28.2" + "@esbuild/openbsd-x64": "npm:0.28.2" + "@esbuild/openharmony-arm64": "npm:0.28.2" + "@esbuild/sunos-x64": "npm:0.28.2" + "@esbuild/win32-arm64": "npm:0.28.2" + "@esbuild/win32-ia32": "npm:0.28.2" + "@esbuild/win32-x64": "npm:0.28.2" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-arm64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/openharmony-arm64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/9b19edb63bd7780fd2e8e65a1394a0e8a05a17d697f73f0647ffe3c134709d8dbe744ab3fd57b35c9470db268cae7678fafd3dcd3ce1f34fc590568c2433f5d2 + languageName: node + linkType: hard + +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1" @@ -804,6 +1442,13 @@ __metadata: languageName: node linkType: hard +"expect-type@npm:^1.2.1": + version: 1.4.0 + resolution: "expect-type@npm:1.4.0" + checksum: 10c0/d40d76b8570695d36587beb3cc28494da2ca3ec8f04e67f5622ed2d372d850e401a9adef19c6835e1a8173903f157c79540b34c7b3fbd7cd8ce726cc903c57b7 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -833,6 +1478,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.5.0": + version: 6.5.0 + resolution: "fdir@npm:6.5.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/e345083c4306b3aed6cb8ec551e26c36bab5c511e99ea4576a16750ddc8d3240e63826cc624f5ae17ad4dc82e68a253213b60d556c11bfad064b7607847ed07f + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -870,7 +1527,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:~2.3.2": +"fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": version: 2.3.3 resolution: "fsevents@npm:2.3.3" dependencies: @@ -880,7 +1537,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin": version: 2.3.3 resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" dependencies: @@ -1102,6 +1759,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.1": + version: 9.0.1 + resolution: "js-tokens@npm:9.0.1" + checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e + languageName: node + linkType: hard + "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -1144,6 +1808,13 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.2.1 + resolution: "loupe@npm:3.2.1" + checksum: 10c0/910c872cba291309664c2d094368d31a68907b6f5913e989d301b5c25f30e97d76d77f23ab3bf3b46d0f601ff0b6af8810c10c31b91d2c6b2f132809ca2cc705 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.2.2 resolution: "lru-cache@npm:10.2.2" @@ -1151,6 +1822,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.17": + version: 0.30.21 + resolution: "magic-string@npm:0.30.21" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.5.5" + checksum: 10c0/299378e38f9a270069fc62358522ddfb44e94244baa0d6a8980ab2a9b2490a1d03b236b447eee309e17eb3bddfa482c61259d47960eb018a904f0ded52780c4a + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.1 resolution: "make-fetch-happen@npm:13.0.1" @@ -1311,6 +1991,13 @@ __metadata: languageName: node linkType: hard +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -1322,6 +2009,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.18": + version: 3.3.19 + resolution: "nanoid@npm:3.3.19" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/ce7afa6258bfaa00bdc8de291cfe9913280eaa403b7d9cff3db4e4200c1913e7163577df74897958da144db473a96c67f74f57db0f542fbfe409df677bae3255 + languageName: node + linkType: hard + "negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" @@ -1425,6 +2121,27 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^2.0.3": + version: 2.0.3 + resolution: "pathe@npm:2.0.3" + checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1 + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.1 + resolution: "pathval@npm:2.0.1" + checksum: 10c0/460f4709479fbf2c45903a65655fc8f0a5f6d808f989173aeef5fdea4ff4f303dc13f7870303999add60ec49d4c14733895c0a869392e9866f1091fa64fd7581 + languageName: node + linkType: hard + +"picocolors@npm:^1.1.1": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -1432,6 +2149,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2, picomatch@npm:^4.0.3, picomatch@npm:^4.0.4": + version: 4.0.7 + resolution: "picomatch@npm:4.0.7" + checksum: 10c0/beb6ae02c43ae44e84883b90830196d9046b1726ead292adcf7f57945e0bb0d992d68563d87e03b484b6f3c9a5c6defda7523477f047d7f0e663f126cc01787f + languageName: node + linkType: hard + "pirates@npm:^4.0.1": version: 4.0.6 resolution: "pirates@npm:4.0.6" @@ -1457,6 +2181,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.5.6": + version: 8.5.28 + resolution: "postcss@npm:8.5.28" + dependencies: + nanoid: "npm:^3.3.18" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/9fe44215a6628d89c8a75184b92f5b2f77e16f9e5b13b39b9009bb7e8e6eccddf82c8854bae922db1f17ace6ef3445073fe38abff513caeb03e5285b1b55620a + languageName: node + linkType: hard + "proc-log@npm:^3.0.0": version: 3.0.0 resolution: "proc-log@npm:3.0.0" @@ -1588,6 +2323,99 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.43.0": + version: 4.63.4 + resolution: "rollup@npm:4.63.4" + dependencies: + "@napi-rs/lzma-linux-x64-gnu": "npm:1.5.1" + "@rollup/rollup-android-arm-eabi": "npm:4.63.4" + "@rollup/rollup-android-arm64": "npm:4.63.4" + "@rollup/rollup-darwin-arm64": "npm:4.63.4" + "@rollup/rollup-darwin-x64": "npm:4.63.4" + "@rollup/rollup-freebsd-arm64": "npm:4.63.4" + "@rollup/rollup-freebsd-x64": "npm:4.63.4" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.63.4" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.63.4" + "@rollup/rollup-linux-arm64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-arm64-musl": "npm:4.63.4" + "@rollup/rollup-linux-loong64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-loong64-musl": "npm:4.63.4" + "@rollup/rollup-linux-ppc64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-ppc64-musl": "npm:4.63.4" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-riscv64-musl": "npm:4.63.4" + "@rollup/rollup-linux-s390x-gnu": "npm:4.63.4" + "@rollup/rollup-linux-x64-gnu": "npm:4.63.4" + "@rollup/rollup-linux-x64-musl": "npm:4.63.4" + "@rollup/rollup-openbsd-x64": "npm:4.63.4" + "@rollup/rollup-openharmony-arm64": "npm:4.63.4" + "@rollup/rollup-win32-arm64-msvc": "npm:4.63.4" + "@rollup/rollup-win32-ia32-msvc": "npm:4.63.4" + "@rollup/rollup-win32-x64-gnu": "npm:4.63.4" + "@rollup/rollup-win32-x64-msvc": "npm:4.63.4" + "@types/estree": "npm:1.0.9" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-freebsd-arm64": + optional: true + "@rollup/rollup-freebsd-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-loong64-gnu": + optional: true + "@rollup/rollup-linux-loong64-musl": + optional: true + "@rollup/rollup-linux-ppc64-gnu": + optional: true + "@rollup/rollup-linux-ppc64-musl": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-riscv64-musl": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-openbsd-x64": + optional: true + "@rollup/rollup-openharmony-arm64": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-gnu": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 10c0/fb0aa9242126b8d01196a394edc98423d36e802d13741f9ec6f58559cda783b5e6d46a160f5cf8be6492c13df46786253c0a45267f0dac0294f6ca7d926ea916 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -1629,6 +2457,13 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.3": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -1678,6 +2513,13 @@ __metadata: languageName: node linkType: hard +"source-map-js@npm:^1.2.1": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + "source-map@npm:0.8.0-beta.0": version: 0.8.0-beta.0 resolution: "source-map@npm:0.8.0-beta.0" @@ -1703,6 +2545,20 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"std-env@npm:^3.9.0": + version: 3.10.0 + resolution: "std-env@npm:3.10.0" + checksum: 10c0/1814927a45004d36dde6707eaf17552a546769bc79a6421be2c16ce77d238158dfe5de30910b78ec30d95135cc1c59ea73ee22d2ca170f8b9753f84da34c427f + languageName: node + linkType: hard + "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -1750,6 +2606,15 @@ __metadata: languageName: node linkType: hard +"strip-literal@npm:^3.0.0": + version: 3.1.0 + resolution: "strip-literal@npm:3.1.0" + dependencies: + js-tokens: "npm:^9.0.1" + checksum: 10c0/50918f669915d9ad0fe4b7599902b735f853f2201c97791ead00104a654259c0c61bc2bc8fa3db05109339b61f4cf09e47b94ecc874ffbd0e013965223893af8 + languageName: node + linkType: hard + "sucrase@npm:^3.20.3": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -1800,6 +2665,51 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.9.0": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: 10c0/c3500b0f60d2eb8db65250afe750b66d51623057ee88720b7f064894a6cb7eb93360ca824a60a31ab16dab30c7b1f06efe0795b352e37914a9d4bad86386a20c + languageName: node + linkType: hard + +"tinyexec@npm:^0.3.2": + version: 0.3.2 + resolution: "tinyexec@npm:0.3.2" + checksum: 10c0/3efbf791a911be0bf0821eab37a3445c2ba07acc1522b1fa84ae1e55f10425076f1290f680286345ed919549ad67527d07281f1c19d584df3b74326909eb1f90 + languageName: node + linkType: hard + +"tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + +"tinypool@npm:^1.1.1": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 10c0/bf26727d01443061b04fa863f571016950888ea994ba0cd8cba3a1c51e2458d84574341ab8dbc3664f1c3ab20885c8cf9ff1cc4b18201f04c2cde7d317fff69b + languageName: node + linkType: hard + +"tinyrainbow@npm:^2.0.0": + version: 2.0.0 + resolution: "tinyrainbow@npm:2.0.0" + checksum: 10c0/c83c52bef4e0ae7fb8ec6a722f70b5b6fa8d8be1c85792e829f56c0e1be94ab70b293c032dc5048d4d37cfe678f1f5babb04bdc65fd123098800148ca989184f + languageName: node + linkType: hard + +"tinyspy@npm:^4.0.3": + version: 4.0.6 + resolution: "tinyspy@npm:4.0.6" + checksum: 10c0/1066760cea2a18ca69b9d934b73212ff5a45414387a67270b6944591a8afe59031e3d82b48cf7ca2d16b6c640d654e3a4a5726a63f2509aec3ef2d98f62ae5b8 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -1911,6 +2821,132 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:3.2.4": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.4.1" + es-module-lexer: "npm:^1.7.0" + pathe: "npm:^2.0.3" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/6ceca67c002f8ef6397d58b9539f80f2b5d79e103a18367288b3f00a8ab55affa3d711d86d9112fce5a7fa658a212a087a005a045eb8f4758947dd99af2a6c6b + languageName: node + linkType: hard + +"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": + version: 7.3.6 + resolution: "vite@npm:7.3.6" + dependencies: + esbuild: "npm:^0.27.0 || ^0.28.0" + fdir: "npm:^6.5.0" + fsevents: "npm:~2.3.3" + picomatch: "npm:^4.0.3" + postcss: "npm:^8.5.6" + rollup: "npm:^4.43.0" + tinyglobby: "npm:^0.2.15" + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/c6d359f84ad362f5c97ff7988b77c90add04162c60cdf6059375a7d9e3217848c53a8cb6b6c48517bef84b6bba4c9bc85319a889b5236acb7d5a2bc8cb7b9a8d + languageName: node + linkType: hard + +"vitest@npm:^3": + version: 3.2.7 + resolution: "vitest@npm:3.2.7" + dependencies: + "@types/chai": "npm:^5.2.2" + "@vitest/expect": "npm:3.2.7" + "@vitest/mocker": "npm:3.2.7" + "@vitest/pretty-format": "npm:^3.2.7" + "@vitest/runner": "npm:3.2.7" + "@vitest/snapshot": "npm:3.2.7" + "@vitest/spy": "npm:3.2.7" + "@vitest/utils": "npm:3.2.7" + chai: "npm:^5.2.0" + debug: "npm:^4.4.1" + expect-type: "npm:^1.2.1" + magic-string: "npm:^0.30.17" + pathe: "npm:^2.0.3" + picomatch: "npm:^4.0.2" + std-env: "npm:^3.9.0" + tinybench: "npm:^2.9.0" + tinyexec: "npm:^0.3.2" + tinyglobby: "npm:^0.2.14" + tinypool: "npm:^1.1.1" + tinyrainbow: "npm:^2.0.0" + vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" + vite-node: "npm:3.2.4" + why-is-node-running: "npm:^2.3.0" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/debug": ^4.1.12 + "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 + "@vitest/browser": 3.2.7 + "@vitest/ui": 3.2.7 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/debug": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: ./vitest.mjs + checksum: 10c0/4eb7a63a1d62b88c425bcbc609835055a5644a1994d6f47605fe396dddf732e3c0277c9ec89a028fe81557dc4051dd15fc15b9eba6a51d1466cd5c682ddc1261 + languageName: node + linkType: hard + "webidl-conversions@npm:^4.0.2": version: 4.0.2 resolution: "webidl-conversions@npm:4.0.2" @@ -1951,6 +2987,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" From 3a9599f0b1418fe3e011eecdd72b71af26a8c5a2 Mon Sep 17 00:00:00 2001 From: Eliott Gandiolle Date: Mon, 21 Sep 2026 17:48:33 +0200 Subject: [PATCH 3/3] example: add an Expo app that shows detected waterfalls on device A small Expo Go app (react-query v5) consuming the library through `file:..`. It taps `console.warn`, keeps the lines the detector really emits, and renders them, so the detection is visible on a device: one button fires two sequential dependent queries, the other two parallel ones. Run on an iPhone 17 Pro simulator (iOS 26.4): the sequential button adds `["user",1] --> ["posts-of-user",1]` to the list, the parallel button adds nothing. Exclude the example from the root tsconfig, it is its own project. Co-Authored-By: Claude Opus 5 (1M context) --- example/.gitignore | 3 + example/App.tsx | 122 ++++++++++++++++++++++++++++++++++++++++ example/README.md | 22 ++++++++ example/app.json | 12 ++++ example/index.ts | 8 +++ example/metro.config.js | 31 ++++++++++ example/package.json | 25 ++++++++ example/tsconfig.json | 6 ++ tsconfig.json | 4 +- 9 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 example/.gitignore create mode 100644 example/App.tsx create mode 100644 example/README.md create mode 100644 example/app.json create mode 100644 example/index.ts create mode 100644 example/metro.config.js create mode 100644 example/package.json create mode 100644 example/tsconfig.json diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..223ef7d --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,3 @@ +node_modules +package-lock.json +.expo diff --git a/example/App.tsx b/example/App.tsx new file mode 100644 index 0000000..268085f --- /dev/null +++ b/example/App.tsx @@ -0,0 +1,122 @@ +import { detectQueryWaterfalls } from "@bam.tech/tanstack-query-detect-waterfall"; +import { QueryClient } from "@tanstack/react-query"; +import { useEffect, useState } from "react"; +import { Pressable, SafeAreaView, ScrollView, StyleSheet, Text, View } from "react-native"; + +const queryClient = new QueryClient(); + +/** + * The detector reports through `console.warn`. We tap that channel and parse the + * `from --> to` lines out of its real payload so every detection it makes shows + * up on screen. Nothing is synthesised here. + */ +type Listener = (line: string) => void; +const listeners = new Set(); + +const originalWarn = console.warn.bind(console); +console.warn = (...args: unknown[]) => { + const message = String(args[0] ?? ""); + if (message.startsWith("Detected query waterfalls")) { + message + .split("\n") + .filter((line) => line.includes(" --> ")) + .forEach((line) => listeners.forEach((listener) => listener(line.trim()))); + } + originalWarn(...args); +}; + +detectQueryWaterfalls(queryClient); + +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +let run = 0; + +const fetchQuery = (name: string) => + queryClient.fetchQuery({ + queryKey: [name, run], + queryFn: async () => { + await delay(300); + return name; + }, + }); + +export default function App() { + const [waterfalls, setWaterfalls] = useState([]); + const [status, setStatus] = useState("idle"); + + useEffect(() => { + const listener: Listener = (line) => setWaterfalls((previous) => [...previous, line]); + listeners.add(listener); + return () => { + listeners.delete(listener); + }; + }, []); + + const runSequential = async () => { + run += 1; + setStatus("running 2 sequential queries…"); + const user = await fetchQuery("user"); + // `posts` only starts once `user` has resolved: a textbook waterfall. + await fetchQuery(`posts-of-${user}`); + setStatus("sequential run done"); + }; + + const runParallel = async () => { + run += 1; + setStatus("running 2 parallel queries…"); + await Promise.all([fetchQuery("profile"), fetchQuery("settings")]); + setStatus("parallel run done"); + }; + + return ( + + Waterfall detector E2E + @tanstack/react-query v5 · Expo Go + + + Run 2 SEQUENTIAL queries + + + Run 2 PARALLEL queries + + + + Status: {status} + + + Detected waterfalls: {waterfalls.length} + + + + {waterfalls.length === 0 ? ( + + No waterfall detected yet + + ) : ( + waterfalls.map((line, index) => ( + + + {line} + + + )) + )} + + + ); +} + +const styles = StyleSheet.create({ + screen: { flex: 1, backgroundColor: "#0f172a", paddingHorizontal: 20, paddingTop: 60 }, + title: { color: "#f8fafc", fontSize: 24, fontWeight: "700" }, + subtitle: { color: "#94a3b8", fontSize: 13, marginBottom: 24 }, + button: { backgroundColor: "#ef4444", borderRadius: 10, paddingVertical: 16, marginBottom: 12 }, + parallel: { backgroundColor: "#22c55e" }, + buttonLabel: { color: "#0f172a", fontSize: 16, fontWeight: "700", textAlign: "center" }, + status: { color: "#cbd5f5", fontSize: 14, marginTop: 8 }, + count: { color: "#f8fafc", fontSize: 18, fontWeight: "700", marginTop: 4, marginBottom: 12 }, + list: { flex: 1, backgroundColor: "#1e293b", borderRadius: 10, padding: 12 }, + empty: { color: "#64748b", fontStyle: "italic" }, + item: { borderBottomColor: "#334155", borderBottomWidth: 1, paddingVertical: 8 }, + itemText: { color: "#fca5a5", fontFamily: "Courier", fontSize: 13 }, +}); diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..9278772 --- /dev/null +++ b/example/README.md @@ -0,0 +1,22 @@ +# On-device example + +A minimal Expo app that runs the detector against **@tanstack/react-query v5** +on a real device/simulator, and renders every waterfall it reports on screen. +The library is consumed through `file:..`, i.e. the built `dist/` of this repo. + +The app taps `console.warn`, keeps the lines the detector actually emits, and +lists them: nothing on screen is synthesised. + +## Run it + +```bash +yarn build # at the repo root, the example consumes dist/ +cd example +npm install +npx expo start --ios # or --android; Expo Go is enough, the library is pure JS +``` + +- **Run 2 SEQUENTIAL queries** starts `["user", n]`, waits for it to resolve, + then starts `["posts-of-user", n]`. One waterfall appears in the list. +- **Run 2 PARALLEL queries** starts `["profile", n]` and `["settings", n]` at + once. The list does not grow. diff --git a/example/app.json b/example/app.json new file mode 100644 index 0000000..913e32c --- /dev/null +++ b/example/app.json @@ -0,0 +1,12 @@ +{ + "expo": { + "name": "waterfall-detector-example", + "slug": "waterfall-detector-example", + "version": "1.0.0", + "orientation": "portrait", + "userInterfaceStyle": "light", + "ios": { + "supportsTablet": true + } + } +} diff --git a/example/index.ts b/example/index.ts new file mode 100644 index 0000000..1d6e981 --- /dev/null +++ b/example/index.ts @@ -0,0 +1,8 @@ +import { registerRootComponent } from 'expo'; + +import App from './App'; + +// registerRootComponent calls AppRegistry.registerComponent('main', () => App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); diff --git a/example/metro.config.js b/example/metro.config.js new file mode 100644 index 0000000..d6be6f8 --- /dev/null +++ b/example/metro.config.js @@ -0,0 +1,31 @@ +const { getDefaultConfig } = require("expo/metro-config"); +const path = require("path"); + +const config = getDefaultConfig(__dirname); + +// The library is linked with `file:`, so without this it would resolve +// `@tanstack/query-core` / `lodash` from its OWN node_modules. Force both to the +// app's copies so the detector really runs against the app's react-query v5 +// instance. +const forced = { + "@tanstack/query-core": path.resolve(__dirname, "node_modules/@tanstack/query-core"), + lodash: path.resolve(__dirname, "node_modules/lodash"), +}; + +const defaultResolveRequest = config.resolver.resolveRequest; +config.resolver.resolveRequest = (context, moduleName, platform) => { + for (const [name, dir] of Object.entries(forced)) { + if (moduleName === name || moduleName.startsWith(`${name}/`)) { + return context.resolveRequest( + context, + path.join(dir, moduleName.slice(name.length)), + platform + ); + } + } + return (defaultResolveRequest ?? context.resolveRequest)(context, moduleName, platform); +}; + +config.watchFolders = [path.resolve(__dirname, "..")]; + +module.exports = config; diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..5b322ef --- /dev/null +++ b/example/package.json @@ -0,0 +1,25 @@ +{ + "name": "waterfall-detector-example", + "version": "1.0.0", + "main": "index.ts", + "dependencies": { + "@bam.tech/tanstack-query-detect-waterfall": "file:..", + "@tanstack/react-query": "^5.103.2", + "expo": "~57.0.24", + "expo-status-bar": "~57.0.1", + "lodash": "^4.18.1", + "react": "19.2.3", + "react-native": "0.86.3" + }, + "devDependencies": { + "@types/react": "~19.2.2", + "typescript": "~6.0.3" + }, + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web" + }, + "private": true +} diff --git a/example/tsconfig.json b/example/tsconfig.json new file mode 100644 index 0000000..b9567f6 --- /dev/null +++ b/example/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 4004a74..29a02ca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -105,5 +105,7 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + /* The example app is its own project with its own tsconfig. */ + "exclude": ["example", "dist", "node_modules"] }