From 0ac63b633ca60686ba42ef1d1239d241c2a46e5a Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 16:54:48 +0200 Subject: [PATCH 01/11] feat: decouple from cross-fetch --- .github/workflows/nodejs.yml | 5 +- .gitignore | 1 + README.md | 46 +- package-lock.json | 756 +++++++++++++++-------------- package.json | 30 +- setupVitest.ts | 5 - src/index.d.ts | 150 ------ src/index.js | 271 ----------- src/index.ts | 439 +++++++++++++++++ tests/{api.test.js => api.test.ts} | 223 +++++---- tests/{api.js => api.ts} | 13 +- tests/node.test.js | 9 - tests/node.test.ts | 13 + tsconfig.build.json | 12 + tsconfig.json | 40 +- types/test.ts | 19 +- vitest.config.ts | 3 - 17 files changed, 1075 insertions(+), 960 deletions(-) delete mode 100644 setupVitest.ts delete mode 100644 src/index.d.ts delete mode 100644 src/index.js create mode 100644 src/index.ts rename tests/{api.test.js => api.test.ts} (81%) rename tests/{api.js => api.ts} (81%) delete mode 100644 tests/node.test.js create mode 100644 tests/node.test.ts create mode 100644 tsconfig.build.json diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index f43ad24..543d495 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -25,5 +25,6 @@ jobs: node-version: ${{ matrix.node-version }} - run: npm install - run: npm run lint - - run: npm run tsc - - run: npm test + - run: npm run format:check + - run: npm run build + - run: npm run test diff --git a/.gitignore b/.gitignore index 56e70b0..aaa6383 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ npm-debug.log yarn-error.log coverage .idea +dist diff --git a/README.md b/README.md index aa50c52..29b9357 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ requests. It's easy to setup and you don't need a library like `nock` to get goi for mocking under the surface. This means that any of the `vi.fn()` methods are also available. For more information on the vitest mock API, check their docs [here](https://vitest.dev/guide/mocking.html) -It currently supports the mocking with the [`cross-fetch`](https://www.npmjs.com/package/cross-fetch) polyfill, so it -supports Node.js and any browser-like runtime. +As of version 0.4.0, `vitest-fetch-mock` mocks the global [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) method, +which should be present in all modern runtimes and browsers. ## Contents @@ -631,8 +631,6 @@ the most used ones. ```js // api.js -import 'cross-fetch'; - export function APIRequest(who) { if (who === 'facebook') { return fetch('https://facebook.com'); @@ -749,16 +747,14 @@ For convenience, all the conditional mocking functions also accept optional para #### Conditional Mocking examples ```js -import crossFetch from 'cross-fetch'; - -vi.mock('cross-fetch', async () => { - const crossFetchActual = await requireActual('cross-fetch'); +vi.mock('fetch', async () => { + const fetchActual = globalThis.fetch; const mocked = {}; - for (const key of Object.keys(crossFetchActual)) { - if (typeof crossFetchActual[key] === 'function') { - mocked[key] = vi.fn(crossFetchActual[key]); + for (const key of Object.keys(fetchActual)) { + if (typeof fetchActual[key] === 'function') { + mocked[key] = vi.fn(fetchActual[key]); } else { - mocked[key] = crossFetchActual[key]; + mocked[key] = fetchActual[key]; } } return mocked; @@ -768,18 +764,18 @@ describe('conditional mocking', () => { const realResponse = 'REAL FETCH RESPONSE' const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE' const testUrl = defaultRequestUri - let crossFetchSpy + let fetchSpy beforeEach(() => { fetch.resetMocks() fetch.mockResponse(mockedDefaultResponse) - vi.mocked(crossFetch) + vi.mocked(fetch) .mockImplementation(async () => Promise.resolve(new Response(realResponse)) ) }) afterEach(() => { - vi.mocked(crossFetch).mockRestore() + vi.mocked(fetch).mockRestore() }) const expectMocked = async (uri, response = mockedDefaultResponse) => { @@ -981,16 +977,14 @@ describe('conditional mocking', () => { ``` ```js -import crossFetch from 'cross-fetch'; - -vi.mock('cross-fetch', async () => { - const crossFetchActual = await requireActual('cross-fetch'); +vi.mock('fetch', async () => { + const fetchActual = globalThis.fetch; const mocked = {}; - for (const key of Object.keys(crossFetchActual)) { - if (typeof crossFetchActual[key] === 'function') { - mocked[key] = vi.fn(crossFetchActual[key]); + for (const key of Object.keys(fetchActual)) { + if (typeof fetchActual[key] === 'function') { + mocked[key] = vi.fn(fetchActual[key]); } else { - mocked[key] = crossFetchActual[key]; + mocked[key] = fetchActual[key]; } } return mocked; @@ -1007,15 +1001,15 @@ describe('conditional mocking complex', () => { const realResponse = 'REAL FETCH RESPONSE'; const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'; const testUrl = defaultRequestUri; - let crossFetchSpy; + let fetchSpy; beforeEach(() => { fetch.resetMocks(); fetch.mockResponse(mockedDefaultResponse); - vi.mocked(crossFetch).mockImplementation(async () => Promise.resolve(new Response(realResponse))); + vi.mocked(fetch).mockImplementation(async () => Promise.resolve(new Response(realResponse))); }); afterEach(() => { - vi.mocked(crossFetch).mockRestore(); + vi.mocked(fetch).mockRestore(); }); describe('complex example', () => { diff --git a/package-lock.json b/package-lock.json index 0e14b76..3d5897e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,21 +8,18 @@ "name": "vitest-fetch-mock", "version": "0.3.0", "license": "MIT", - "dependencies": { - "cross-fetch": "^4.0.0" - }, "devDependencies": { - "@types/node": "^20.14.10", + "@types/node": "^22.0.0", "@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/parser": "^7.16.0", "eslint": "^8.56.0", "jsdom": "^24.1.0", "prettier": "^3.3.2", - "typescript": "~5.5.3", - "vitest": "^2.0.2" + "typescript": "~5.5.0", + "vitest": "2.0.2" }, "engines": { - "node": ">=14.14.0" + "node": ">=18.0.0" }, "peerDependencies": { "vitest": ">=2.0.0" @@ -33,6 +30,7 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -49,6 +47,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -65,6 +64,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -81,6 +81,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -97,6 +98,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -113,6 +115,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -129,6 +132,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -145,6 +149,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -161,6 +166,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -177,6 +183,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -193,6 +200,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -209,6 +217,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -225,6 +234,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -241,6 +251,7 @@ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -257,6 +268,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -273,6 +285,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -289,6 +302,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -305,6 +319,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -321,6 +336,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -337,6 +353,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -353,6 +370,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -369,6 +387,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -385,6 +404,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -401,6 +421,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -549,6 +570,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -563,6 +585,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -572,6 +595,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -580,13 +604,15 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -628,226 +654,244 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", - "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", - "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", - "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", - "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", - "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", - "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", - "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", - "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", - "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", - "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", - "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", - "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", - "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", - "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", - "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", - "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz", + "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -1046,6 +1090,7 @@ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.2.tgz", "integrity": "sha512-nKAvxBYqcDugYZ4nJvnm5OR8eDJdgWjk4XM9owQKUjzW70q0icGV2HVnQOyYsp906xJaBDUXw0+9EHw2T8e0mQ==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/spy": "2.0.2", "@vitest/utils": "2.0.2", @@ -1057,10 +1102,11 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", - "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, + "license": "MIT", "dependencies": { "tinyrainbow": "^1.2.0" }, @@ -1073,6 +1119,7 @@ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.2.tgz", "integrity": "sha512-OCh437Vi8Wdbif1e0OvQcbfM3sW4s2lpmOjAE7qfLrpzJX2M7J1IQlNvEcb/fu6kaIB9n9n35wS0G2Q3en5kHg==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/utils": "2.0.2", "pathe": "^1.1.2" @@ -1086,6 +1133,7 @@ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.2.tgz", "integrity": "sha512-Yc2ewhhZhx+0f9cSUdfzPRcsM6PhIb+S43wxE7OG0kTxqgqzo8tHkXFuFlndXeDMp09G3sY/X5OAo/RfYydf1g==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.0.2", "magic-string": "^0.30.10", @@ -1095,11 +1143,25 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", + "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vitest/spy": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.2.tgz", "integrity": "sha512-MgwJ4AZtCgqyp2d7WcQVE8aNG5vQ9zu9qMPYQHjsld/QVsrvg78beNrXdO4HYkP0lDahCO3P4F27aagIag+SGQ==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^3.0.0" }, @@ -1112,6 +1174,7 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.2.tgz", "integrity": "sha512-pxCY1v7kmOCWYWjzc0zfjGTA3Wmn8PKnlPvSrsA643P1NHl1fOyXj2Q9SaNlrlFE+ivCsxM80Ov3AR82RmHCWQ==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/pretty-format": "2.0.2", "estree-walker": "^3.0.3", @@ -1122,6 +1185,19 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/utils/node_modules/@vitest/pretty-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", + "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -1215,6 +1291,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" } @@ -1257,6 +1334,7 @@ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1275,6 +1353,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", "dev": true, + "license": "MIT", "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -1307,6 +1386,7 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 16" } @@ -1347,14 +1427,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1401,12 +1473,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1428,6 +1501,7 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1489,6 +1563,7 @@ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1693,6 +1768,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -1711,6 +1787,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -1870,6 +1947,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1878,20 +1956,12 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -2047,6 +2117,7 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } @@ -2164,6 +2235,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2291,28 +2363,28 @@ "dev": true }, "node_modules/loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "MIT" }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", @@ -2362,6 +2434,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2385,10 +2458,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.7", @@ -2401,6 +2475,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -2414,49 +2489,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -2472,6 +2510,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -2499,6 +2538,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -2620,22 +2660,25 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/pathval": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 14.16" } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -2650,9 +2693,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -2668,10 +2711,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2784,12 +2828,13 @@ } }, "node_modules/rollup": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", - "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -2799,22 +2844,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.1", - "@rollup/rollup-android-arm64": "4.18.1", - "@rollup/rollup-darwin-arm64": "4.18.1", - "@rollup/rollup-darwin-x64": "4.18.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", - "@rollup/rollup-linux-arm-musleabihf": "4.18.1", - "@rollup/rollup-linux-arm64-gnu": "4.18.1", - "@rollup/rollup-linux-arm64-musl": "4.18.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", - "@rollup/rollup-linux-riscv64-gnu": "4.18.1", - "@rollup/rollup-linux-s390x-gnu": "4.18.1", - "@rollup/rollup-linux-x64-gnu": "4.18.1", - "@rollup/rollup-linux-x64-musl": "4.18.1", - "@rollup/rollup-win32-arm64-msvc": "4.18.1", - "@rollup/rollup-win32-ia32-msvc": "4.18.1", - "@rollup/rollup-win32-x64-msvc": "4.18.1", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, @@ -2909,6 +2954,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -2926,10 +2972,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -2963,6 +3010,7 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3007,10 +3055,11 @@ "dev": true }, "node_modules/tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", - "dev": true + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, "node_modules/tinypool": { "version": "1.0.0", @@ -3026,15 +3075,17 @@ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -3115,10 +3166,11 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3128,10 +3180,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" }, "node_modules/universalify": { "version": "0.2.0", @@ -3162,14 +3215,15 @@ } }, "node_modules/vite": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", - "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -3188,6 +3242,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -3205,6 +3260,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -3221,6 +3279,7 @@ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.2.tgz", "integrity": "sha512-w4vkSz1Wo+NIQg8pjlEn0jQbcM/0D+xVaYjhw3cvarTanLLBh54oNiRbsT8PNK5GfuST0IlVXjsNRoNlqvY/fw==", "dev": true, + "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.5", @@ -3243,6 +3302,7 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.2.tgz", "integrity": "sha512-WlpZ9neRIjNBIOQwBYfBSr0+of5ZCbxT2TVGKW4Lv0c8+srCFIiRdsP7U009t8mMn821HQ4XKgkx5dVWpyoyLw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@vitest/expect": "2.0.2", @@ -3796,130 +3856,130 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", - "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", - "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", - "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", - "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", - "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", - "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", - "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", - "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", - "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", - "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", - "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", - "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", - "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", - "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", - "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", - "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "dev": true, "optional": true }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true }, "@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.7.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.7.tgz", + "integrity": "sha512-SRxCrrg9CL/y54aiMCG3edPKdprgMVGDXjA3gB8UmmBW5TcXzRUYAh8EWzTnSJFAd1rgImPELza+A3bJ+qxz8Q==", "dev": true, "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "@typescript-eslint/eslint-plugin": { @@ -4037,9 +4097,9 @@ } }, "@vitest/pretty-format": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", - "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", + "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", "dev": true, "requires": { "tinyrainbow": "^1.2.0" @@ -4064,6 +4124,17 @@ "@vitest/pretty-format": "2.0.2", "magic-string": "^0.30.10", "pathe": "^1.1.2" + }, + "dependencies": { + "@vitest/pretty-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", + "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "dev": true, + "requires": { + "tinyrainbow": "^1.2.0" + } + } } }, "@vitest/spy": { @@ -4085,6 +4156,17 @@ "estree-walker": "^3.0.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" + }, + "dependencies": { + "@vitest/pretty-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.2.tgz", + "integrity": "sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==", + "dev": true, + "requires": { + "tinyrainbow": "^1.2.0" + } + } } }, "acorn": { @@ -4255,14 +4337,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "requires": { - "node-fetch": "^2.6.12" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4302,12 +4376,12 @@ } }, "debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" } }, "decimal.js": { @@ -4665,12 +4739,6 @@ "dev": true, "optional": true }, - "get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true - }, "get-stream": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", @@ -4974,21 +5042,18 @@ "dev": true }, "loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", - "dev": true, - "requires": { - "get-func-name": "^2.0.1" - } + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true }, "magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "merge-stream": { @@ -5044,9 +5109,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "nanoid": { @@ -5061,35 +5126,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } - }, "npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -5218,9 +5254,9 @@ "dev": true }, "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "picomatch": { @@ -5230,14 +5266,14 @@ "dev": true }, "postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" } }, "prelude-ls": { @@ -5304,28 +5340,28 @@ } }, "rollup": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", - "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", "dev": true, "requires": { - "@rollup/rollup-android-arm-eabi": "4.18.1", - "@rollup/rollup-android-arm64": "4.18.1", - "@rollup/rollup-darwin-arm64": "4.18.1", - "@rollup/rollup-darwin-x64": "4.18.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", - "@rollup/rollup-linux-arm-musleabihf": "4.18.1", - "@rollup/rollup-linux-arm64-gnu": "4.18.1", - "@rollup/rollup-linux-arm64-musl": "4.18.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", - "@rollup/rollup-linux-riscv64-gnu": "4.18.1", - "@rollup/rollup-linux-s390x-gnu": "4.18.1", - "@rollup/rollup-linux-x64-gnu": "4.18.1", - "@rollup/rollup-linux-x64-musl": "4.18.1", - "@rollup/rollup-win32-arm64-msvc": "4.18.1", - "@rollup/rollup-win32-ia32-msvc": "4.18.1", - "@rollup/rollup-win32-x64-msvc": "4.18.1", - "@types/estree": "1.0.5", + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "@types/estree": "1.0.6", "fsevents": "~2.3.2" } }, @@ -5399,9 +5435,9 @@ "dev": true }, "source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true }, "stackback": { @@ -5459,9 +5495,9 @@ "dev": true }, "tinybench": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", - "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true }, "tinypool": { @@ -5477,9 +5513,9 @@ "dev": true }, "tinyspy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", - "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true }, "to-regex-range": { @@ -5535,15 +5571,15 @@ "dev": true }, "typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "universalify": { @@ -5572,15 +5608,15 @@ } }, "vite": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", - "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", "dev": true, "requires": { "esbuild": "^0.21.3", "fsevents": "~2.3.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" } }, "vite-node": { diff --git a/package.json b/package.json index 786ab49..88a0314 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,19 @@ "version": "0.3.0", "description": "fetch mock for vitest", "type": "module", - "main": "src/index.js", - "module": "src/index.js", - "files": [ - "src", - "types/index.d.ts" - ], + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": ["./dist/**.*"], "scripts": { + "type-check": "tsc", "test": "vitest run", - "lint": "eslint . && prettier --check .", - "tsc": "tsc" + "test:watch": "vitest run --watch", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier src --write", + "format:check": "prettier src --check", + "build": "tsc -p tsconfig.build.json" }, "repository": { "type": "git", @@ -40,20 +43,17 @@ }, "homepage": "https://github.com/IanVS/vitest-fetch-mock#readme", "engines": { - "node": ">=14.14.0" - }, - "dependencies": { - "cross-fetch": "^4.0.0" + "node": ">=18.0.0" }, "devDependencies": { - "@types/node": "^20.14.10", + "@types/node": "^22.0.0", "@typescript-eslint/eslint-plugin": "^7.16.0", "@typescript-eslint/parser": "^7.16.0", "eslint": "^8.56.0", "jsdom": "^24.1.0", "prettier": "^3.3.2", - "typescript": "~5.5.3", - "vitest": "^2.0.2" + "typescript": "~5.5.0", + "vitest": "2.0.2" }, "peerDependencies": { "vitest": ">=2.0.0" diff --git a/setupVitest.ts b/setupVitest.ts deleted file mode 100644 index c2f81ad..0000000 --- a/setupVitest.ts +++ /dev/null @@ -1,5 +0,0 @@ -import createFetchMock from 'vitest-fetch-mock'; -import { vi } from 'vitest'; - -const fetcher = createFetchMock(vi); -fetcher.enableMocks(); diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index 9060d18..0000000 --- a/src/index.d.ts +++ /dev/null @@ -1,150 +0,0 @@ -// TypeScript Version: 3.0 -import Global = NodeJS.Global; -import { vitest } from 'vitest'; -import type { Mock } from 'vitest'; - -declare global { - const fetchMock: FetchMock; - namespace NodeJS { - interface Global { - fetch: FetchMock; - } - } -} - -export interface GlobalWithFetchMock extends Global { - fetchMock: FetchMock; - fetch: FetchMock; -} - -export interface FetchMock - extends Mock<(resource: string | Request | undefined, options: RequestInit | undefined) => Promise> { - (input: string | Request, init?: RequestInit): Promise; - - /** - * Response mocking - */ - mockResponse(fn: MockResponseInitFunction): FetchMock; - mockResponse(response: string, responseInit?: MockParams): FetchMock; - - /** - * Response mocking - */ - mockResponseOnce(fn: MockResponseInitFunction): FetchMock; - mockResponseOnce(response: string, responseInit?: MockParams): FetchMock; - - /** - * alias for mockResponseOnce - */ - once(fn: MockResponseInitFunction): FetchMock; - once(url: string, responseInit?: MockParams): FetchMock; - - /** - * Response mocking for multiple calls - */ - mockResponses(...responses: Array): FetchMock; - - /** - * Error/Reject mocking - */ - mockReject(error?: ErrorOrFunction): FetchMock; - /** - * Error/Reject mocking - */ - mockRejectOnce(error?: ErrorOrFunction): FetchMock; - /** - * Error/Reject mocking - */ - mockAbort(): FetchMock; - /** - * Error/Reject mocking - */ - mockAbortOnce(): FetchMock; - - /** - * Conditional Mocking - */ - isMocking(input: string | Request): boolean; - - /** - * Conditional Mocking - */ - doMock(fn?: MockResponseInitFunction): FetchMock; - doMock(response: string, responseInit?: MockParams): FetchMock; - - /** - * Conditional Mocking - */ - doMockOnce(fn?: MockResponseInitFunction): FetchMock; - doMockOnce(response: string, responseInit?: MockParams): FetchMock; - /** - * alias for doMockOnce - */ - mockOnce(fn?: MockResponseInitFunction): FetchMock; - mockOnce(response: string, responseInit?: MockParams): FetchMock; - - doMockIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - doMockIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - /** - * alias for doMockIf - */ - mockIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - mockIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - - doMockOnceIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - doMockOnceIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - /** - * alias for doMocKOnceIf - */ - mockOnceIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - mockOnceIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - - dontMock(fn?: MockResponseInitFunction): FetchMock; - dontMock(response: string, responseInit?: MockParams): FetchMock; - - dontMockOnce(fn?: MockResponseInitFunction): FetchMock; - dontMockOnce(response: string, responseInit?: MockParams): FetchMock; - - dontMockIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - dontMockIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - - dontMockOnceIf(urlOrPredicate: UrlOrPredicate, fn?: MockResponseInitFunction): FetchMock; - dontMockOnceIf(urlOrPredicate: UrlOrPredicate, response: string, responseInit?: MockParams): FetchMock; - - /** - * Returns all the requests that have been made to the mocked fetch function. - * Does not include aborted requests. - */ - requests(): Request[]; - - resetMocks(): void; - enableMocks(): void; - disableMocks(): void; -} - -export interface MockParams { - status?: number; - statusText?: string; - headers?: string[][] | { [key: string]: string }; // HeadersInit - url?: string; - /** Set >= 1 to have redirected return true. Only applicable to Node.js */ - counter?: number; -} - -export interface MockResponseInit extends MockParams { - body?: string; - init?: MockParams; -} - -export type ErrorOrFunction = Error | ((...args: any[]) => Promise); -export type UrlOrPredicate = string | RegExp | ((input: Request) => boolean); - -export type MockResponseInitFunction = ( - request: Request -) => MockResponseInit | string | Promise; - -declare const fetchMock: FetchMock; - -declare function createMocker(vi: typeof vitest): FetchMock; - -export default createMocker; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 1e83fcd..0000000 --- a/src/index.js +++ /dev/null @@ -1,271 +0,0 @@ -import crossFetch from 'cross-fetch'; - -// Modified from https://github.com/jimmywarting/node-domexception, -// which is commonjs only. -if (!globalThis.DOMException) { - try { - const { MessageChannel } = await import('worker_threads'), - port = new MessageChannel().port1, - ab = new ArrayBuffer(); - port.postMessage(ab, [ab, ab]); - } catch (err) { - err.constructor.name === 'DOMException' && (globalThis.DOMException = err.constructor); - } -} - -export default function createFetchMocker(vi) { - globalThis.fetch = crossFetch; - globalThis.Response = crossFetch.Response; - globalThis.Headers = crossFetch.Headers; - globalThis.Request = crossFetch.Request; - - const ActualResponse = Response; - - function responseWrapper(body, init) { - if (body && typeof body.constructor === 'function' && body.constructor.__isFallback) { - const response = new ActualResponse(null, init); - response.body = body; - - const actualClone = response.clone; - response.clone = () => { - const clone = actualClone.call(response); - const [body1, body2] = body.tee(); - response.body = body1; - clone.body = body2; - return clone; - }; - - return response; - } - - return new ActualResponse(body, init); - } - - function responseInit(resp, init) { - if (typeof resp.init === 'object') { - return resp.init; - } else { - init = Object.assign({}, init || {}); - for (const field of ['status', 'statusText', 'headers', 'url']) { - if (field in resp) { - init[field] = resp[field]; - } - } - return init; - } - } - - function requestMatches(urlOrPredicate) { - const predicate = - urlOrPredicate instanceof RegExp - ? (input) => urlOrPredicate.test(input.url) - : typeof urlOrPredicate === 'string' - ? (input) => input.url === urlOrPredicate - : urlOrPredicate; - return (input, reqInit) => { - const req = normalizeRequest(input, reqInit); - return [predicate(req), req]; - }; - } - - function requestNotMatches(urlOrPredicate) { - const matches = requestMatches(urlOrPredicate); - return (input) => { - const result = matches(input); - return [!result[0], result[1]]; - }; - } - - function staticMatches(value) { - return (input, reqInit) => { - return [value, normalizeRequest(input, reqInit)]; - }; - } - - const isFn = (unknown) => typeof unknown === 'function'; - - const isMocking = vi.fn(staticMatches(true)); - - const abortError = () => new DOMException('The operation was aborted. ', 'AbortError'); - - const abort = () => { - throw abortError(); - }; - - const abortAsync = () => { - return Promise.reject(abortError()); - }; - - const toPromise = (val) => (val instanceof Promise ? val : Promise.resolve(val)); - - const normalizeResponse = (bodyOrFunction, init) => (input, reqInit) => { - const [mocked, request] = isMocking(input, reqInit); - return mocked - ? isFn(bodyOrFunction) - ? toPromise(bodyOrFunction(request)).then((resp) => { - if (request.signal && request.signal.aborted) { - abort(); - } - return typeof resp === 'string' - ? responseWrapper(resp, init) - : responseWrapper(resp.body, responseInit(resp, init)); - }) - : new Promise((resolve, reject) => { - if (request.signal && request.signal.aborted) { - reject(abortError()); - return; - } - resolve(responseWrapper(bodyOrFunction, init)); - }) - : crossFetch.fetch(input, reqInit); - }; - - const normalizeRequest = (input, reqInit) => { - if (input instanceof Request) { - if (input.signal && input.signal.aborted) { - abort(); - } - return input; - } else if (typeof input === 'string') { - if (reqInit && reqInit.signal && reqInit.signal.aborted) { - abort(); - } - return new Request(input, reqInit); - } else if (typeof input.toString === 'function') { - if (reqInit && reqInit.signal && reqInit.signal.aborted) { - abort(); - } - return new Request(input.toString(), reqInit); - } else { - throw new TypeError('Unable to parse input as string or Request'); - } - }; - - const normalizeError = (errorOrFunction) => - isFn(errorOrFunction) ? errorOrFunction : () => Promise.reject(errorOrFunction); - - const fetch = vi.fn(normalizeResponse('')); - fetch.Headers = Headers; - fetch.Response = responseWrapper; - fetch.Request = Request; - fetch.mockResponse = (bodyOrFunction, init) => fetch.mockImplementation(normalizeResponse(bodyOrFunction, init)); - - fetch.mockReject = (errorOrFunction) => fetch.mockImplementation(normalizeError(errorOrFunction)); - - fetch.mockAbort = () => fetch.mockImplementation(abortAsync); - fetch.mockAbortOnce = () => fetch.mockImplementationOnce(abortAsync); - - const mockResponseOnce = (bodyOrFunction, init) => - fetch.mockImplementationOnce(normalizeResponse(bodyOrFunction, init)); - - fetch.mockResponseOnce = mockResponseOnce; - - fetch.once = mockResponseOnce; - - fetch.mockRejectOnce = (errorOrFunction) => fetch.mockImplementationOnce(normalizeError(errorOrFunction)); - - fetch.mockResponses = (...responses) => { - responses.forEach((response) => { - if (Array.isArray(response)) { - const [body, init] = response; - fetch.mockImplementationOnce(normalizeResponse(body, init)); - } else { - fetch.mockImplementationOnce(normalizeResponse(response)); - } - }); - return fetch; - }; - - fetch.isMocking = (req, reqInit) => isMocking(req, reqInit)[0]; - - fetch.mockIf = fetch.doMockIf = (urlOrPredicate, bodyOrFunction, init) => { - isMocking.mockImplementation(requestMatches(urlOrPredicate)); - if (bodyOrFunction) { - fetch.mockResponse(bodyOrFunction, init); - } - return fetch; - }; - - fetch.dontMockIf = (urlOrPredicate, bodyOrFunction, init) => { - isMocking.mockImplementation(requestNotMatches(urlOrPredicate)); - if (bodyOrFunction) { - fetch.mockResponse(bodyOrFunction, init); - } - return fetch; - }; - - fetch.mockOnceIf = fetch.doMockOnceIf = (urlOrPredicate, bodyOrFunction, init) => { - isMocking.mockImplementationOnce(requestMatches(urlOrPredicate)); - if (bodyOrFunction) { - mockResponseOnce(bodyOrFunction, init); - } - return fetch; - }; - - fetch.dontMockOnceIf = (urlOrPredicate, bodyOrFunction, init) => { - isMocking.mockImplementationOnce(requestNotMatches(urlOrPredicate)); - if (bodyOrFunction) { - mockResponseOnce(bodyOrFunction, init); - } - return fetch; - }; - - fetch.dontMock = () => { - isMocking.mockImplementation(staticMatches(false)); - return fetch; - }; - - fetch.dontMockOnce = () => { - isMocking.mockImplementationOnce(staticMatches(false)); - return fetch; - }; - - fetch.doMock = (bodyOrFunction, init) => { - isMocking.mockImplementation(staticMatches(true)); - if (bodyOrFunction) { - fetch.mockResponse(bodyOrFunction, init); - } - return fetch; - }; - - fetch.mockOnce = fetch.doMockOnce = (bodyOrFunction, init) => { - isMocking.mockImplementationOnce(staticMatches(true)); - if (bodyOrFunction) { - mockResponseOnce(bodyOrFunction, init); - } - return fetch; - }; - - fetch.requests = () => { - const requests = []; - fetch.mock.calls.forEach((call) => { - try { - let req = normalizeRequest(call[0], call[1]); - requests.push(req); - } catch (e) { - // ignore - } - }); - return requests; - }; - - fetch.resetMocks = () => { - fetch.mockReset(); - isMocking.mockReset(); - - // reset to default implementation with each reset - fetch.mockImplementation(normalizeResponse('')); - fetch.doMock(); - fetch.isMocking = (req, reqInit) => isMocking(req, reqInit)[0]; - }; - - fetch.enableMocks = () => { - globalThis.fetchMock = globalThis.fetch = fetch; - }; - - fetch.disableMocks = () => { - globalThis.fetch = crossFetch; - }; - - return fetch; -} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..17dad40 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,439 @@ +import { vi } from 'vitest'; +import type { Mock } from '@vitest/spy'; + +// type-definitions +export type FetchMock = Mock & FetchMockObject; + +class FetchMockObject { + private readonly isMocking = vi.fn(always(true)); + + constructor( + private mockedFetch: Mock, + private originalFetch: typeof global.fetch, + private chainingResultProvider: () => FetchMock + ) {} + + // enable/disable + enableMocks(): FetchMock { + globalThis.fetch = this.mockedFetch; + return this.chainingResultProvider(); + } + + disableMocks(): FetchMock { + globalThis.fetch = this.originalFetch; + return this.chainingResultProvider(); + } + + // reset + resetMocks(): FetchMock { + this.mockedFetch.mockRestore(); + return this.chainingResultProvider(); + } + + // mocking functions + mockResponse(responseProvider: ResponseProvider): FetchMock; + mockResponse(response: ResponseBody, params?: MockParams): FetchMock; + mockResponse(responseProviderOrBody: ResponseProvider | ResponseBody, params?: MockParams): FetchMock; + mockResponse(responseProviderOrBody: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + this.mockedFetch.mockImplementation((input: RequestInput, requestInit?: RequestInit) => { + if (!this.isMocking(input, requestInit)) { + return this.originalFetch(input, requestInit); + } + + const request = normalizeRequest(input, requestInit); + return buildResponse(request, responseProviderOrBody, params); + }); + + return this.chainingResultProvider(); + } + + mockResponseOnce(responseProvider: ResponseProvider): FetchMock; + mockResponseOnce(response: ResponseBody, params?: MockParams): FetchMock; + mockResponseOnce(responseProviderOrBody: ResponseProvider | ResponseBody, params?: MockParams): FetchMock; + mockResponseOnce(responseProviderOrBody: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + this.mockedFetch.mockImplementationOnce((input: RequestInput, requestInit?: RequestInit) => { + if (!this.isMocking(input, requestInit)) { + return this.originalFetch(input, requestInit); + } + const request = normalizeRequest(input, requestInit); + return buildResponse(request, responseProviderOrBody, params); + }); + return this.chainingResultProvider(); + } + + mockResponseIf(urlOrPredicate: UrlOrPredicate, responseProvider: ResponseProvider): FetchMock; + mockResponseIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + mockResponseIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + this.mockedFetch.mockImplementation((input: RequestInput, requestInit?: RequestInit) => { + if (!this.isMocking(input, requestInit)) { + return this.originalFetch(input, requestInit); + } + const request = normalizeRequest(input, requestInit); + return requestMatches(request, urlOrPredicate) + ? buildResponse(request, responseProviderOrBody, params) + : this.originalFetch(input, requestInit); + }); + return this.chainingResultProvider(); + } + + mockResponseOnceIf(urlOrPredicate: UrlOrPredicate, responseProvider: ResponseProvider): FetchMock; + mockResponseOnceIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + mockResponseOnceIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + this.isMocking.mockImplementationOnce((input, requestInit) => + requestMatches(normalizeRequest(input, requestInit), urlOrPredicate) + ); + this.mockedFetch.mockImplementationOnce((input: RequestInput, requestInit?: RequestInit) => { + if (!this.isMocking(input, requestInit)) { + return this.originalFetch(input, requestInit); + } + const request = normalizeRequest(input, requestInit); + return requestMatches(request, urlOrPredicate) + ? buildResponse(request, responseProviderOrBody, params) + : this.originalFetch(input, requestInit); + }); + return this.chainingResultProvider(); + } + + mockResponses(...responses: Array): FetchMock { + responses.forEach((response) => { + if (Array.isArray(response)) { + const [body, init] = response; + this.mockedFetch.mockImplementationOnce((input) => { + if (!this.isMocking(input)) { + return this.originalFetch(input); + } + const request = normalizeRequest(input); + return buildResponse(request, body, init); + }); + } else { + this.mockedFetch.mockImplementationOnce((input) => { + if (!this.isMocking(input)) { + return this.originalFetch(input); + } + const request = normalizeRequest(input); + return buildResponse(request, response); + }); + } + }); + return this.chainingResultProvider(); + } + + // abort + mockAbort(): FetchMock { + this.mockedFetch.mockImplementation(() => abortAsync()); + return this.chainingResultProvider(); + } + + mockAbortOnce(): FetchMock { + this.mockedFetch.mockImplementationOnce(() => abortAsync()); + return this.chainingResultProvider(); + } + + // reject (error) + mockReject(error?: ErrorOrFunction): FetchMock { + this.mockedFetch.mockImplementation(() => normalizeError(error)); + return this.chainingResultProvider(); + } + + mockRejectOnce(error?: ErrorOrFunction): FetchMock { + this.mockedFetch.mockImplementationOnce(() => normalizeError(error)); + return this.chainingResultProvider(); + } + + // enable/disable + doMock(responseProvider?: ResponseProvider): FetchMock; + doMock(response: ResponseBody, params?: MockParams): FetchMock; + doMock(responseProviderOrBody?: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + this.isMocking.mockImplementation(always(true)); + if (responseProviderOrBody) { + this.mockResponse(responseProviderOrBody, params); + } + return this.chainingResultProvider(); + } + + doMockOnce(responseProvider?: ResponseProvider): FetchMock; + doMockOnce(response: ResponseBody, params?: MockParams): FetchMock; + doMockOnce(responseProviderOrBody?: ResponseProvider | ResponseBody, params?: MockParams): FetchMock; + doMockOnce(responseProviderOrBody?: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + this.isMocking.mockImplementationOnce(always(true)); + if (responseProviderOrBody) { + this.mockResponseOnce(responseProviderOrBody, params); + } + return this.chainingResultProvider(); + } + + doMockIf(urlOrPredicate: UrlOrPredicate, responseProvider?: ResponseProvider): FetchMock; + doMockIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + doMockIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock; + doMockIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + this.isMocking.mockImplementation((input, requestInit) => + requestMatches(normalizeRequest(input, requestInit), urlOrPredicate) + ); + if (responseProviderOrBody) { + this.mockResponse(responseProviderOrBody, params); + } + return this.chainingResultProvider(); + } + + doMockOnceIf(urlOrPredicate: UrlOrPredicate, responseProvider?: ResponseProvider): FetchMock; + doMockOnceIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + doMockOnceIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock; + doMockOnceIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + this.isMocking.mockImplementationOnce((input, requestInit) => + requestMatches(normalizeRequest(input, requestInit), urlOrPredicate) + ); + if (responseProviderOrBody) { + this.mockResponseOnce(responseProviderOrBody, params); + } + return this.chainingResultProvider(); + } + + dontMock(): FetchMock { + this.isMocking.mockImplementation(always(false)); + return this.chainingResultProvider(); + } + + dontMockOnce(): FetchMock { + this.isMocking.mockImplementationOnce(always(false)); + return this.chainingResultProvider(); + } + + dontMockIf(urlOrPredicate: UrlOrPredicate): FetchMock { + this.isMocking.mockImplementation((input, requestInit) => + requestNotMatches(normalizeRequest(input, requestInit), urlOrPredicate) + ); + return this.chainingResultProvider(); + } + + dontMockOnceIf(urlOrPredicate: UrlOrPredicate): FetchMock { + this.isMocking.mockImplementationOnce((input, requestInit) => + requestNotMatches(normalizeRequest(input, requestInit), urlOrPredicate) + ); + return this.chainingResultProvider(); + } + + // recording + requests(): Request[] { + return this.mockedFetch.mock.calls + .map(([input, requestInit]) => { + try { + return normalizeRequest(input, requestInit); + } catch (e) { + return undefined; + } + }) + .filter((it) => it !== undefined); + } + + // aliases + + /** + * alias for mockResponseOnce + */ + once(responseProvider: ResponseProvider): FetchMock; + once(response: ResponseBody, params?: MockParams): FetchMock; + once(responseProviderOrBody: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + return this.mockResponseOnce(responseProviderOrBody, params); + } + + /** + * alias for doMockOnce + */ + mockOnce(responseProvider?: ResponseProvider): FetchMock; + mockOnce(response: ResponseBody, params?: MockParams): FetchMock; + mockOnce(responseProviderOrBody?: ResponseProvider | ResponseBody, params?: MockParams): FetchMock { + return this.doMockOnce(responseProviderOrBody, params); + } + + /** + * alias for doMockIf + */ + mockIf(urlOrPredicate: UrlOrPredicate, responseProvider?: ResponseProvider): FetchMock; + mockIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + mockIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + return this.doMockIf(urlOrPredicate, responseProviderOrBody, params); + } + + /** + * alias for doMockOnceIf + */ + mockOnceIf(urlOrPredicate: UrlOrPredicate, responseProvider?: ResponseProvider): FetchMock; + mockOnceIf(urlOrPredicate: UrlOrPredicate, response: ResponseBody, params?: MockParams): FetchMock; + mockOnceIf( + urlOrPredicate: UrlOrPredicate, + responseProviderOrBody?: ResponseProvider | ResponseBody, + params?: MockParams + ): FetchMock { + return this.doMockOnceIf(urlOrPredicate, responseProviderOrBody, params); + } + + // mockOnceIf(); // doMocKOnceIf +} + +type UrlOrPredicate = string | RegExp | ((input: Request) => boolean); +type RequestInput = string | URL | Request; +type ResponseProvider = (request: Request) => ResponseLike | Promise; +type ResponseLike = MockResponse | ResponseBody | Response; +type ResponseBody = string; +type ErrorOrFunction = Error | string | ((...args: any[]) => Promise); + +export interface MockParams { + status?: number; + statusText?: string; + headers?: [string, string][] | Record; // HeadersInit + url?: string; + counter?: number; +} + +export interface MockResponse extends MockParams { + body?: string; +} + +// class FetchMock + +// factory +export function createFetchMock(): FetchMock { + const isMocking = vi.fn(always(true)); + + const originalFetch = globalThis.fetch; + const mockedFetch = vi.fn((input, requestInit) => { + if (!isMocking(input, requestInit)) { + return originalFetch(input, requestInit); + } + return buildResponse(normalizeRequest(input, requestInit), ''); + }) as FetchMock; + + const fetchMock: FetchMock = mockedFetch as FetchMock; + const fetchMockObject = new FetchMockObject(mockedFetch, globalThis.fetch, () => fetchMock); + + copyMethods(fetchMockObject, fetchMock); + + return mockedFetch; +} + +function requestMatches(request: Request, urlOrPredicate: UrlOrPredicate): boolean { + if (urlOrPredicate instanceof RegExp) { + return urlOrPredicate.test(request.url); + } else if (typeof urlOrPredicate === 'string') { + return request.url === urlOrPredicate; + } else { + return urlOrPredicate(request); + } +} + +function requestNotMatches(request: Request, urlOrPredicate: UrlOrPredicate): boolean { + return !requestMatches(request, urlOrPredicate); +} + +function normalizeRequest(input: RequestInput, requestInit?: RequestInit): Request { + if (input instanceof Request) { + if (input.signal && input.signal.aborted) { + abort(); + } + return input; + } else if (typeof input === 'string') { + if (requestInit && requestInit.signal && requestInit.signal.aborted) { + abort(); + } + return new Request(input, requestInit); + } else { + if (requestInit && requestInit.signal && requestInit.signal.aborted) { + abort(); + } + return new Request(input.toString(), requestInit); + } +} + +async function buildResponse( + request: Request, + responseProviderOrBody: ResponseProvider | ResponseBody, + params?: MockParams +): Promise { + if (typeof responseProviderOrBody === 'string') { + const responseBody = responseProviderOrBody as ResponseBody; + + if (request.signal && request.signal.aborted) { + abort(); + } + + return new Response(responseBody, params); + } else { + const responseProvider = responseProviderOrBody as ResponseProvider; + const mockResponse = await responseProvider(request); + + if (request.signal && request.signal.aborted) { + abort(); + } + + return typeof mockResponse === 'string' + ? new Response(mockResponse, params) + : mockResponse instanceof Response + ? mockResponse + : patchUrl(new Response(mockResponse.body, { ...params, ...mockResponse }), mockResponse.url ?? params?.url); + } +} + +// see: https://stackoverflow.com/questions/70002424/url-is-empty-when-defining-a-response-object +function patchUrl(response: Response, url?: string): Response { + if (url) { + Object.defineProperty(response, 'url', { value: url }); + } + + return response; +} + +function always(toggle: boolean): (input: RequestInput, requestInit?: RequestInit) => boolean { + return () => toggle; +} + +const normalizeError = (errorOrFunction?: ErrorOrFunction): Promise => + typeof errorOrFunction === 'function' ? errorOrFunction() : Promise.reject(errorOrFunction); + +function abortError(): Error { + return new DOMException('The operation was aborted.'); +} + +function abort(): never { + throw abortError(); +} + +function abortAsync(): Promise { + return Promise.reject(abortError()); +} + +function copyMethods(source: T, target: T) { + Object.getOwnPropertyNames(FetchMockObject.prototype).forEach((propertyName) => { + const propertyFromSource = source[propertyName as keyof T]; + if (propertyName !== 'constructor' && typeof propertyFromSource === 'function') { + target[propertyName as keyof T] = propertyFromSource.bind(source); + } + }); +} diff --git a/tests/api.test.js b/tests/api.test.ts similarity index 81% rename from tests/api.test.js rename to tests/api.test.ts index c337ee0..37e082e 100644 --- a/tests/api.test.js +++ b/tests/api.test.ts @@ -1,27 +1,38 @@ -import { describe, afterEach, beforeEach, it, test, expect, vi } from 'vitest'; -import { APIRequest, APIRequest2, defaultRequestUri, request } from './api'; -import crossFetch from 'cross-fetch'; +import { describe, beforeEach, it, test, expect, vi, afterEach, beforeAll, afterAll } from 'vitest'; +import { APIRequest, APIRequest2, defaultRequestUri, request } from './api.js'; +import { createFetchMock, type FetchMock, type MockResponse } from '../src/index.js'; -describe('testing mockResponse and alias once', () => { - beforeEach(() => { +describe('testing mockResponse', () => { + const fetch = createFetchMock(); + + beforeAll(() => { + fetch.enableMocks(); + }); + + afterEach(() => { fetch.resetMocks(); }); + + afterAll(() => { + fetch.disableMocks(); + }); + it('mocks a response', async () => { - fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }, { status: 200 })); + fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }), { status: 200 }); const response = await APIRequest('google'); expect(response).toEqual({ secret_data: 'abcde' }); expect(fetch.mock.calls.length).toEqual(1); - expect(fetch.mock.calls[0][0]).toEqual('https://google.com'); + expect(fetch.mock.calls[0]![0]).toEqual('https://google.com'); expect(fetch.requests().length).toEqual(1); - expect(fetch.requests()[0].url).toEqual('https://google.com/'); + expect(fetch.requests()[0]!.url).toEqual('https://google.com/'); }); it('mocks a response with chaining', async () => { fetch - .mockResponseOnce(JSON.stringify({ secret_data: '12345' }, { status: 200 })) - .mockResponseOnce(JSON.stringify({ secret_data: '67891' }, { status: 200 })); + .mockResponseOnce(JSON.stringify({ secret_data: '12345' }), { status: 200 }) + .mockResponseOnce(JSON.stringify({ secret_data: '67891' }), { status: 200 }); const response = await APIRequest('facebook'); @@ -29,18 +40,18 @@ describe('testing mockResponse and alias once', () => { expect(fetch.mock.calls.length).toEqual(2); - expect(fetch.mock.calls[0][0]).toEqual('https://facebook.com/someOtherResource'); - expect(fetch.mock.calls[1][0]).toEqual('https://facebook.com'); + expect(fetch.mock.calls[0]![0]).toEqual('https://facebook.com/someOtherResource'); + expect(fetch.mock.calls[1]![0]).toEqual('https://facebook.com'); }); it('mocks a response with alias .once', async () => { - fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }, { status: 200 })); + fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }), { status: 200 }); const response = await APIRequest('google'); expect(response).toEqual({ secret_data: 'abcde' }); expect(fetch.mock.calls.length).toEqual(1); - expect(fetch.mock.calls[0][0]).toEqual('https://google.com'); + expect(fetch.mock.calls[0]![0]).toEqual('https://google.com'); }); it('mocks a response with chaining with alias .once', async () => { @@ -54,46 +65,57 @@ describe('testing mockResponse and alias once', () => { expect(fetch.mock.calls.length).toEqual(2); - expect(fetch.mock.calls[0][0]).toEqual('https://facebook.com/someOtherResource'); - expect(fetch.requests()[0].url).toEqual('https://facebook.com/someOtherResource'); - expect(fetch.mock.calls[1][0]).toEqual('https://facebook.com'); + expect(fetch.mock.calls[0]![0]).toEqual('https://facebook.com/someOtherResource'); + expect(fetch.requests()[0]!.url).toEqual('https://facebook.com/someOtherResource'); + expect(fetch.mock.calls[1]![0]).toEqual('https://facebook.com'); }); it('supports URLs', async () => { - fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }, { status: 200 })); + fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }), { status: 200 }); const response = await APIRequest('instagram'); expect(response).toEqual({ secret_data: 'abcde' }); expect(fetch.mock.calls.length).toEqual(1); - expect(fetch.mock.calls[0][0]).toEqual(new URL('https://instagram.com')); + expect(fetch.mock.calls[0]![0]).toEqual(new URL('https://instagram.com')); }); it('returns normalized requests', async () => { - fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }, { status: 200 })); + fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }), { status: 200 }); const response = await APIRequest('instagram'); expect(response).toEqual({ secret_data: 'abcde' }); expect(fetch.requests().length).toEqual(1); - expect(fetch.requests()[0].method).toEqual('GET'); + expect(fetch.requests()[0]!.method).toEqual('GET'); }); it('supports an object with a stringifier', async () => { - fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }, { status: 200 })); + fetch.mockResponseOnce(JSON.stringify({ secret_data: 'abcde' }), { status: 200 }); const response = await APIRequest('instagram'); expect(response).toEqual({ secret_data: 'abcde' }); expect(fetch.mock.calls.length).toEqual(1); - expect(fetch.mock.calls[0][0]).toEqual(new URL('https://instagram.com')); + expect(fetch.mock.calls[0]![0]).toEqual(new URL('https://instagram.com')); }); }); describe('testing mockResponses', () => { - beforeEach(() => { + const fetch = createFetchMock(); + + beforeAll(() => { + fetch.enableMocks(); + }); + + afterEach(() => { fetch.resetMocks(); }); + + afterAll(() => { + fetch.disableMocks(); + }); + it('mocks multiple responses', async () => { fetch.mockResponses( [JSON.stringify({ name: 'naruto', average_score: 79 })], @@ -107,14 +129,27 @@ describe('testing mockResponses', () => { ]); expect(fetch.mock.calls.length).toEqual(2); - expect(fetch.mock.calls[0][0]).toEqual('https://facebook.com/someOtherResource'); - expect(fetch.mock.calls[1][0]).toEqual('https://facebook.com'); + expect(fetch.mock.calls[0]![0]).toEqual('https://facebook.com/someOtherResource'); + expect(fetch.mock.calls[1]![0]).toEqual('https://facebook.com'); }); }); describe('Mocking aborts', () => { - beforeEach(() => { + const fetch = createFetchMock(); + + beforeAll(() => { + fetch.enableMocks(); + }); + + afterEach(() => { fetch.resetMocks(); + }); + + afterAll(() => { + fetch.disableMocks(); + }); + + beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { @@ -132,31 +167,37 @@ describe('Mocking aborts', () => { }); it('throws when passed an already aborted abort signal in the request init', () => { - if (typeof AbortController !== 'undefined') { - const c = new AbortController(); - c.abort(); - expect(() => fetch('/', { signal: c.signal })).toThrow(expect.any(DOMException)); - } + const c = new AbortController(); + c.abort(); + expect(() => fetch('/', { signal: c.signal })).toThrow(expect.any(DOMException)); }); it('rejects when aborted before resolved', async () => { - if (typeof AbortController !== 'undefined') { - const c = new AbortController(); - fetch.mockResponse(async () => { - vi.advanceTimersByTime(60); - return ''; - }); - setTimeout(() => c.abort(), 50); - await expect(fetch('/', { signal: c.signal })).rejects.toThrow(expect.any(DOMException)); - } + const c = new AbortController(); + fetch.mockResponse(async () => { + vi.advanceTimersByTime(60); + return ''; + }); + setTimeout(() => c.abort(), 50); + await expect(fetch('http://foo.bar/', { signal: c.signal })).rejects.toThrow(expect.any(DOMException)); }); }); describe('Mocking rejects', () => { - beforeEach(() => { + const fetch = createFetchMock(); + + beforeAll(() => { + fetch.enableMocks(); + }); + + afterEach(() => { fetch.resetMocks(); }); + afterAll(() => { + fetch.disableMocks(); + }); + it('mocking rejects', async () => { fetch.mockRejectOnce('fake error'); return expect(APIRequest2('google')).rejects.toEqual('fake error'); @@ -164,11 +205,21 @@ describe('Mocking rejects', () => { }); describe('request', () => { - beforeEach(() => { + const fetch = createFetchMock(); + + beforeAll(() => { + fetch.enableMocks(); + }); + + afterEach(() => { fetch.resetMocks(); }); - it('passes input and init to response function', () => { + afterAll(() => { + fetch.disableMocks(); + }); + + it('passes input and init to response function', async () => { const url = 'http://foo.bar/'; const requestInit = { headers: { @@ -184,12 +235,11 @@ describe('request', () => { fetch.mockResponse((input) => { expect(input).toHaveProperty('url', url); expect(input.headers.get('foo')).toEqual('bar'); - return Promise.resolve(response); - }, responseInit); - return fetch(url, requestInit).then((resp) => { - expect(resp.headers.get('bing')).toEqual(responseInit.headers.bing); - return expect(resp.text()).resolves.toEqual(response); + return Promise.resolve({ body: response, ...responseInit }); }); + const resp = await fetch(url, requestInit); + expect(resp.headers.get('bing')).toEqual(responseInit.headers.bing); + await expect(resp.text()).resolves.toEqual(response); }); it('returns object when response is json', async () => { @@ -244,7 +294,7 @@ describe('request', () => { const _response = await request(); throw Error('Should have rejected with error data'); } catch (error) { - expect(error.message).toBe(errorData.error); + expect(error).toMatchObject({ message: errorData.error }); } }); @@ -277,16 +327,12 @@ describe('request', () => { const errorData = { error: 'Uh oh, something has gone wrong. Please tweet us @randomapi about the issue. Thank you.', }; - fetch.mockRejectOnce(() => new Promise((_, reject) => setTimeout(() => reject(JSON.stringify(errorData)))), 100); - try { - await request(); - } catch (error) { - expect(error.message).toBe(errorData.error); - } + fetch.mockRejectOnce(() => new Promise((_, reject) => setTimeout(() => reject(JSON.stringify(errorData)), 100))); + expect(request()).rejects.toThrowError(errorData.error); }); it('resolves with function returning object body and init headers', async () => { - fetch.mockResponseOnce(() => Promise.resolve({ body: 'ok', init: { headers: { ding: 'dang' } } }), { + fetch.mockResponseOnce(() => Promise.resolve({ body: 'ok', headers: { ding: 'dang' } }), { headers: { bash: 'bang' }, }); @@ -296,17 +342,14 @@ describe('request', () => { return expect(response.text()).resolves.toEqual('ok'); }); - it('resolves with function returning object body and extends mock params', async () => { - fetch.mockResponseOnce( - () => ({ - body: 'ok', - headers: { ding: 'dang' }, - status: 201, - statusText: 'text', - url: 'http://foo', - }), - { headers: { bash: 'bang' } } - ); + it('resolves with function returning response', async () => { + fetch.mockResponseOnce(() => ({ + body: 'ok', + headers: { ding: 'dang' }, + status: 201, + statusText: 'text', + url: 'http://foo', + })); const response = await fetch('https://bar', {}); expect(response.headers.get('ding')).toEqual('dang'); @@ -329,24 +372,32 @@ describe('conditional mocking', () => { const realResponse = 'REAL FETCH RESPONSE'; const mockedDefaultResponse = 'MOCKED DEFAULT RESPONSE'; const testUrl = defaultRequestUri; - let crossFetchSpy; - beforeEach(() => { - fetch.resetMocks(); + let originalFetch: typeof global.fetch; + let fetch: FetchMock; + + beforeEach(async () => { + originalFetch = globalThis.fetch; + globalThis.fetch = vi.fn(async () => Promise.resolve(new Response(realResponse))); + fetch = createFetchMock(); + + await expectUnmocked(); + + fetch.enableMocks(); fetch.mockResponse(mockedDefaultResponse); - crossFetchSpy = vi - .spyOn(crossFetch, 'fetch') - .mockImplementation(async () => Promise.resolve(new Response(realResponse))); + + await expectMocked(); }); afterEach(() => { - crossFetchSpy.mockRestore(); + fetch.disableMocks(); + globalThis.fetch = originalFetch; }); - const expectMocked = async (uri, response = mockedDefaultResponse) => { + const expectMocked = async (uri?: string, response = mockedDefaultResponse) => { return expect(request(uri)).resolves.toEqual(response); }; - const expectUnmocked = async (uri) => { + const expectUnmocked = async (uri?: string) => { return expect(request(uri)).resolves.toEqual(realResponse); }; @@ -425,7 +476,7 @@ describe('conditional mocking', () => { fetch.doMockOnceIf('http://foo/', response).doMockOnceIf('http://foo2/', response2); await expectMocked('http://foo/', response); await expectMocked('http://foo2/', response2); - //await expectMocked('http://foo3', mockedDefaultResponse) + await expectMocked('http://foo3', mockedDefaultResponse); }); it('mocks when matches regex', async () => { fetch.doMockOnceIf(new RegExp(testUrl)); @@ -693,13 +744,11 @@ describe('conditional mocking', () => { }); it('enable/disable', async () => { - const createMocker = (await import('../src/index')).default; - console.log(createMocker); - const fetchMock = createMocker(vi); - fetchMock.disableMocks(); - expect(vi.isMockFunction(fetch)).toBe(false); - fetchMock.enableMocks(); - expect(vi.isMockFunction(fetch)).toBe(true); - fetchMock.disableMocks(); - global.fetch = fetchMock; + expect(vi.isMockFunction(globalThis.fetch)).toBe(false); + const fetch = createFetchMock(); + expect(vi.isMockFunction(globalThis.fetch)).toBe(false); + fetch.enableMocks(); + expect(vi.isMockFunction(globalThis.fetch)).toBe(true); + fetch.disableMocks(); + expect(vi.isMockFunction(globalThis.fetch)).toBe(false); }); diff --git a/tests/api.js b/tests/api.ts similarity index 81% rename from tests/api.js rename to tests/api.ts index 5a455a4..3c666c3 100644 --- a/tests/api.js +++ b/tests/api.ts @@ -1,6 +1,4 @@ -import 'cross-fetch/polyfill'; - -export async function APIRequest(who) { +export async function APIRequest(who: string) { if (who === 'facebook') { const call1 = fetch('https://facebook.com/someOtherResource').then((res) => res.json()); const call2 = fetch('https://facebook.com').then((res) => res.json()); @@ -10,16 +8,13 @@ export async function APIRequest(who) { } else if (who === 'instagram') { return fetch(new URL('https://instagram.com')).then((res) => res.json()); } else if (who === 'bing') { - const stringifier = { - toString: () => 'https://bing.com', - }; - return fetch(stringifier).then((res) => res.json()); + return fetch(new URL('https://bing.com')).then((res) => res.json()); } else { return fetch('https://google.com').then((res) => res.json()); } } -export function APIRequest2(who) { +export function APIRequest2(who: string) { if (who === 'google') { return fetch('https://google.com').then((res) => res.json()); } else { @@ -32,7 +27,7 @@ export const defaultRequestUri = 'https://randomuser.me/api'; export function request(uri = defaultRequestUri) { return fetch(uri, {}) .then((response) => { - const contentType = response.headers.get('content-type'); + const contentType = response.headers.get('content-type')!; if (/application\/json/.test(contentType)) { return response.json(); diff --git a/tests/node.test.js b/tests/node.test.js deleted file mode 100644 index db16465..0000000 --- a/tests/node.test.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @jest-environment node - */ -import { it, expect } from 'vitest'; - -it('rejects with a dom exception', () => { - fetch.mockAbort(); - expect(fetch('/')).rejects.toThrow(expect.any(globalThis.DOMException)); -}); diff --git a/tests/node.test.ts b/tests/node.test.ts new file mode 100644 index 0000000..ed05860 --- /dev/null +++ b/tests/node.test.ts @@ -0,0 +1,13 @@ +/** + * @jest-environment node + */ +import { it, expect } from 'vitest'; +import { createFetchMock } from '../src/index.js'; + +it('rejects with a dom exception', () => { + const mock = createFetchMock(); + mock.enableMocks(); + mock.mockAbort(); + expect(fetch('/')).rejects.toThrow(expect.any(DOMException)); + mock.disableMocks(); +}); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..abe8194 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist" + }, + "include": [ + "./setupVitest.ts", + "./src/**/*.ts", + "./src/**/*.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json index f352e74..c1d7f13 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,32 @@ { "compilerOptions": { - "module": "es2020", - "moduleResolution": "node", - "lib": ["es2015", "dom"], - "baseUrl": ".", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": [ + "es2022" + ], + "baseUrl": "./", "paths": { - "vitest-fetch-mock": ["."] + "vitest-fetch-mock": [ + "./src" + ] }, - "noImplicitAny": true, - "noImplicitThis": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "noEmit": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true - } + "strict": true, + "esModuleInterop": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "verbatimModuleSyntax": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "sourceMap": true, + "declaration": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": [ + "./src/**/*.ts" + ] } diff --git a/types/test.ts b/types/test.ts index c5ea750..aa1ad76 100644 --- a/types/test.ts +++ b/types/test.ts @@ -1,7 +1,6 @@ -import setupFm, { MockResponseInit } from 'vitest-fetch-mock'; -import {vi} from 'vitest'; +import { MockResponseInit, createFetchMock } from 'vitest-fetch-mock'; -const fm = setupFm(vi); +const fetchMock = createFetchMock(); fetchMock.mockResponse(JSON.stringify({foo: "bar"})); fetchMock.mockResponse(JSON.stringify({foo: "bar"}), { @@ -108,10 +107,10 @@ function someSyncStringHandler(): string { return JSON.stringify({foo: "bar"}); } -fm.enableMocks(); -fm.disableMocks(); -fm.doMock(); -fm.dontMock(); -fm.doMockOnce(); -fm.dontMockOnce(); -fm.mockOnce(); +fetchMock.enableMocks(); +fetchMock.disableMocks(); +fetchMock.doMock(); +fetchMock.dontMock(); +fetchMock.doMockOnce(); +fetchMock.dontMockOnce(); +fetchMock.mockOnce(); diff --git a/vitest.config.ts b/vitest.config.ts index 4b2f9fa..75f855e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,7 +6,4 @@ export default defineConfig({ 'vitest-fetch-mock': './src/index', }, }, - test: { - setupFiles: ['./setupVitest.ts'], - }, }); From 3f2d8a6a10b02988e16bb8076b493000df2a6a2c Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:00:58 +0200 Subject: [PATCH 02/11] fix: make "createFetchMock" default export again --- src/index.ts | 2 +- tests/api.test.ts | 2 +- tests/node.test.ts | 2 +- types/test.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 17dad40..494348f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -320,7 +320,7 @@ export interface MockResponse extends MockParams { // class FetchMock // factory -export function createFetchMock(): FetchMock { +export default function createFetchMock(): FetchMock { const isMocking = vi.fn(always(true)); const originalFetch = globalThis.fetch; diff --git a/tests/api.test.ts b/tests/api.test.ts index 37e082e..d16e53a 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -1,6 +1,6 @@ import { describe, beforeEach, it, test, expect, vi, afterEach, beforeAll, afterAll } from 'vitest'; import { APIRequest, APIRequest2, defaultRequestUri, request } from './api.js'; -import { createFetchMock, type FetchMock, type MockResponse } from '../src/index.js'; +import createFetchMock, { type FetchMock, type MockResponse } from '../src/index.js'; describe('testing mockResponse', () => { const fetch = createFetchMock(); diff --git a/tests/node.test.ts b/tests/node.test.ts index ed05860..2d245ca 100644 --- a/tests/node.test.ts +++ b/tests/node.test.ts @@ -2,7 +2,7 @@ * @jest-environment node */ import { it, expect } from 'vitest'; -import { createFetchMock } from '../src/index.js'; +import createFetchMock from '../src/index.js'; it('rejects with a dom exception', () => { const mock = createFetchMock(); diff --git a/types/test.ts b/types/test.ts index aa1ad76..248aca9 100644 --- a/types/test.ts +++ b/types/test.ts @@ -1,6 +1,6 @@ -import { MockResponseInit, createFetchMock } from 'vitest-fetch-mock'; +import setupFm, { MockResponseInit } from 'vitest-fetch-mock'; -const fetchMock = createFetchMock(); +const fetchMock = setupFm(); fetchMock.mockResponse(JSON.stringify({foo: "bar"})); fetchMock.mockResponse(JSON.stringify({foo: "bar"}), { From 615a6f569210e4821943e2726d265df4864f4bfd Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:06:47 +0200 Subject: [PATCH 03/11] style: remove comment --- src/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 494348f..aad1a6e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -294,8 +294,6 @@ class FetchMockObject { ): FetchMock { return this.doMockOnceIf(urlOrPredicate, responseProviderOrBody, params); } - - // mockOnceIf(); // doMocKOnceIf } type UrlOrPredicate = string | RegExp | ((input: Request) => boolean); @@ -317,8 +315,6 @@ export interface MockResponse extends MockParams { body?: string; } -// class FetchMock - // factory export default function createFetchMock(): FetchMock { const isMocking = vi.fn(always(true)); From 006260eb29ddf67aa92be51bb9a20b66730ca79a Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:09:15 +0200 Subject: [PATCH 04/11] test: restore another test --- tests/api.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/api.test.ts b/tests/api.test.ts index d16e53a..764bb62 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -219,7 +219,7 @@ describe('request', () => { fetch.disableMocks(); }); - it('passes input and init to response function', async () => { + it('passes input and init to response function', () => { const url = 'http://foo.bar/'; const requestInit = { headers: { @@ -235,11 +235,12 @@ describe('request', () => { fetch.mockResponse((input) => { expect(input).toHaveProperty('url', url); expect(input.headers.get('foo')).toEqual('bar'); - return Promise.resolve({ body: response, ...responseInit }); + return Promise.resolve(response); + }, responseInit); + return fetch(url, requestInit).then((resp) => { + expect(resp.headers.get('bing')).toEqual(responseInit.headers.bing); + return expect(resp.text()).resolves.toEqual(response); }); - const resp = await fetch(url, requestInit); - expect(resp.headers.get('bing')).toEqual(responseInit.headers.bing); - await expect(resp.text()).resolves.toEqual(response); }); it('returns object when response is json', async () => { From c3bf36ebb605e3206c716c1e25a3201feec69db0 Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:13:09 +0200 Subject: [PATCH 05/11] docs: restore JSDoc block --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index aad1a6e..1574ac6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -308,6 +308,7 @@ export interface MockParams { statusText?: string; headers?: [string, string][] | Record; // HeadersInit url?: string; + /** Set >= 1 to have redirected return true. Only applicable to Node.js */ counter?: number; } From 6f280f0ba1c20d6c9acabde592c336590ec2eb84 Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:14:27 +0200 Subject: [PATCH 06/11] test: restore another test --- tests/api.test.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/api.test.ts b/tests/api.test.ts index 764bb62..bddba4f 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -343,14 +343,17 @@ describe('request', () => { return expect(response.text()).resolves.toEqual('ok'); }); - it('resolves with function returning response', async () => { - fetch.mockResponseOnce(() => ({ - body: 'ok', - headers: { ding: 'dang' }, - status: 201, - statusText: 'text', - url: 'http://foo', - })); + it('resolves with function returning object body and extends mock params', async () => { + fetch.mockResponseOnce( + () => ({ + body: 'ok', + headers: { ding: 'dang' }, + status: 201, + statusText: 'text', + url: 'http://foo', + }), + { headers: { bash: 'bang' } } + ); const response = await fetch('https://bar', {}); expect(response.headers.get('ding')).toEqual('dang'); From 39d889108ae3aa36d1885bc6f3bcab52a72bce6d Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Mon, 21 Oct 2024 17:16:10 +0200 Subject: [PATCH 07/11] chore: remove incorrect include records in tsconfig.build.json --- tsconfig.build.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index abe8194..27a1fcd 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -5,8 +5,6 @@ "outDir": "./dist" }, "include": [ - "./setupVitest.ts", - "./src/**/*.ts", "./src/**/*.ts" ] } From 2cb921937da1dfe95846771c2efb80ae5cff059b Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Wed, 23 Oct 2024 00:05:44 +0200 Subject: [PATCH 08/11] fix: revert signature change --- src/index.ts | 6 +++--- tests/api.test.ts | 14 +++++++------- tests/node.test.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index 1574ac6..e94c236 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ -import { vi } from 'vitest'; +import { vi as vitest } from 'vitest'; import type { Mock } from '@vitest/spy'; // type-definitions export type FetchMock = Mock & FetchMockObject; class FetchMockObject { - private readonly isMocking = vi.fn(always(true)); + private readonly isMocking = vitest.fn(always(true)); constructor( private mockedFetch: Mock, @@ -317,7 +317,7 @@ export interface MockResponse extends MockParams { } // factory -export default function createFetchMock(): FetchMock { +export default function createFetchMock(vi: typeof vitest): FetchMock { const isMocking = vi.fn(always(true)); const originalFetch = globalThis.fetch; diff --git a/tests/api.test.ts b/tests/api.test.ts index bddba4f..d0e44e4 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -3,7 +3,7 @@ import { APIRequest, APIRequest2, defaultRequestUri, request } from './api.js'; import createFetchMock, { type FetchMock, type MockResponse } from '../src/index.js'; describe('testing mockResponse', () => { - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); beforeAll(() => { fetch.enableMocks(); @@ -102,7 +102,7 @@ describe('testing mockResponse', () => { }); describe('testing mockResponses', () => { - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); beforeAll(() => { fetch.enableMocks(); @@ -135,7 +135,7 @@ describe('testing mockResponses', () => { }); describe('Mocking aborts', () => { - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); beforeAll(() => { fetch.enableMocks(); @@ -184,7 +184,7 @@ describe('Mocking aborts', () => { }); describe('Mocking rejects', () => { - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); beforeAll(() => { fetch.enableMocks(); @@ -205,7 +205,7 @@ describe('Mocking rejects', () => { }); describe('request', () => { - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); beforeAll(() => { fetch.enableMocks(); @@ -383,7 +383,7 @@ describe('conditional mocking', () => { beforeEach(async () => { originalFetch = globalThis.fetch; globalThis.fetch = vi.fn(async () => Promise.resolve(new Response(realResponse))); - fetch = createFetchMock(); + fetch = createFetchMock(vi); await expectUnmocked(); @@ -749,7 +749,7 @@ describe('conditional mocking', () => { it('enable/disable', async () => { expect(vi.isMockFunction(globalThis.fetch)).toBe(false); - const fetch = createFetchMock(); + const fetch = createFetchMock(vi); expect(vi.isMockFunction(globalThis.fetch)).toBe(false); fetch.enableMocks(); expect(vi.isMockFunction(globalThis.fetch)).toBe(true); diff --git a/tests/node.test.ts b/tests/node.test.ts index 2d245ca..e7043eb 100644 --- a/tests/node.test.ts +++ b/tests/node.test.ts @@ -1,11 +1,11 @@ /** * @jest-environment node */ -import { it, expect } from 'vitest'; +import { it, expect, vi } from 'vitest'; import createFetchMock from '../src/index.js'; it('rejects with a dom exception', () => { - const mock = createFetchMock(); + const mock = createFetchMock(vi); mock.enableMocks(); mock.mockAbort(); expect(fetch('/')).rejects.toThrow(expect.any(DOMException)); From afe69c329829b311d84ff27f20b108bc3ea67327 Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Wed, 23 Oct 2024 09:39:33 +0200 Subject: [PATCH 09/11] fix: enable type-checking in types/test.ts --- .eslintrc.cjs | 2 +- src/index.ts | 20 ++++++++++++++------ tests/api.test.ts | 2 +- tsconfig.eslint.json | 4 ---- tsconfig.json | 6 ++++-- types/test.ts | 9 +++++---- 6 files changed, 25 insertions(+), 18 deletions(-) delete mode 100644 tsconfig.eslint.json diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e6cfb04..5b958ba 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -8,7 +8,7 @@ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { tsconfigRootDir: __dirname, - project: './tsconfig.eslint.json', + project: './tsconfig.json', // Because we are setting "type": "module" in our package.json, all `.js` files are treated as modules // Sometimes we will want a commonjs file, like this eslint config, in which case we use the .cjs extension extraFileExtensions: ['.cjs'], diff --git a/src/index.ts b/src/index.ts index e94c236..ea3b883 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import type { Mock } from '@vitest/spy'; export type FetchMock = Mock & FetchMockObject; class FetchMockObject { - private readonly isMocking = vitest.fn(always(true)); + public readonly isMocking = vitest.fn(always(true)); constructor( private mockedFetch: Mock, @@ -139,12 +139,16 @@ class FetchMockObject { // reject (error) mockReject(error?: ErrorOrFunction): FetchMock { - this.mockedFetch.mockImplementation(() => normalizeError(error)); + this.mockedFetch.mockImplementation((input: RequestInput, requestInit?: RequestInit) => + normalizeError(normalizeRequest(input, requestInit), error) + ); return this.chainingResultProvider(); } mockRejectOnce(error?: ErrorOrFunction): FetchMock { - this.mockedFetch.mockImplementationOnce(() => normalizeError(error)); + this.mockedFetch.mockImplementationOnce((input: RequestInput, requestInit?: RequestInit) => + normalizeError(normalizeRequest(input, requestInit), error) + ); return this.chainingResultProvider(); } @@ -301,7 +305,7 @@ type RequestInput = string | URL | Request; type ResponseProvider = (request: Request) => ResponseLike | Promise; type ResponseLike = MockResponse | ResponseBody | Response; type ResponseBody = string; -type ErrorOrFunction = Error | string | ((...args: any[]) => Promise); +type ErrorOrFunction = Error | ResponseBody | ResponseProvider; export interface MockParams { status?: number; @@ -411,8 +415,12 @@ function always(toggle: boolean): (input: RequestInput, requestInit?: RequestIni return () => toggle; } -const normalizeError = (errorOrFunction?: ErrorOrFunction): Promise => - typeof errorOrFunction === 'function' ? errorOrFunction() : Promise.reject(errorOrFunction); +const normalizeError = async (request: Request, errorOrFunction?: ErrorOrFunction): Promise => + errorOrFunction instanceof Error + ? Promise.reject(errorOrFunction) + : typeof errorOrFunction === 'function' + ? buildResponse(request, errorOrFunction) + : Promise.reject(errorOrFunction); function abortError(): Error { return new DOMException('The operation was aborted.'); diff --git a/tests/api.test.ts b/tests/api.test.ts index d0e44e4..b16a066 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -277,7 +277,7 @@ describe('request', () => { try { const response = await request(); - expect(response.type).toBe(contentType); + expect(response).toMatchObject({ message: contentType }); } catch (e) { console.log(e); } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json deleted file mode 100644 index 6223530..0000000 --- a/tsconfig.eslint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["./", "./src"] -} diff --git a/tsconfig.json b/tsconfig.json index c1d7f13..51e76b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "baseUrl": "./", "paths": { "vitest-fetch-mock": [ - "./src" + "./src/index.ts" ] }, "strict": true, @@ -27,6 +27,8 @@ "noEmit": true }, "include": [ - "./src/**/*.ts" + "./src/**/*.ts", + "./tests/**/*.ts", + "./types/**/*.ts" ] } diff --git a/types/test.ts b/types/test.ts index 248aca9..55965f2 100644 --- a/types/test.ts +++ b/types/test.ts @@ -1,6 +1,7 @@ -import setupFm, { MockResponseInit } from 'vitest-fetch-mock'; +import setupFm, { type MockResponse } from 'vitest-fetch-mock'; +import { vi } from 'vitest'; -const fetchMock = setupFm(); +const fetchMock = setupFm(vi); fetchMock.mockResponse(JSON.stringify({foo: "bar"})); fetchMock.mockResponse(JSON.stringify({foo: "bar"}), { @@ -85,14 +86,14 @@ fetchMock.doMockOnce(); fetchMock.dontMockOnce(); fetchMock.mockOnce(); -async function someAsyncHandler(): Promise { +async function someAsyncHandler(): Promise { return { status: 200, body: await someAsyncStringHandler() }; } -function someSyncHandler(): MockResponseInit { +function someSyncHandler(): MockResponse { return { status: 200, body: someSyncStringHandler() From 7c462d09cb988a4aa2844455256dc9d57722872d Mon Sep 17 00:00:00 2001 From: dirkluijk Date: Wed, 23 Oct 2024 19:57:21 +0200 Subject: [PATCH 10/11] fix: fix eslint config --- .eslintrc.cjs | 3 --- package.json | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5b958ba..291a40e 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -9,9 +9,6 @@ module.exports = { parserOptions: { tsconfigRootDir: __dirname, project: './tsconfig.json', - // Because we are setting "type": "module" in our package.json, all `.js` files are treated as modules - // Sometimes we will want a commonjs file, like this eslint config, in which case we use the .cjs extension - extraFileExtensions: ['.cjs'], }, plugins: ['@typescript-eslint'], rules: { diff --git a/package.json b/package.json index 88a0314..aaddcac 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "type-check": "tsc", "test": "vitest run", "test:watch": "vitest run --watch", - "lint": "eslint .", - "lint:fix": "eslint . --fix", + "lint": "eslint src", + "lint:fix": "eslint src --fix", "format": "prettier src --write", "format:check": "prettier src --check", "build": "tsc -p tsconfig.build.json" From e9a25eeba246ed670ac8882ede13acb5a14dd5dc Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Thu, 24 Oct 2024 09:44:57 -0400 Subject: [PATCH 11/11] Format --- src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index ea3b883..d94cb0b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -416,11 +416,11 @@ function always(toggle: boolean): (input: RequestInput, requestInit?: RequestIni } const normalizeError = async (request: Request, errorOrFunction?: ErrorOrFunction): Promise => - errorOrFunction instanceof Error - ? Promise.reject(errorOrFunction) - : typeof errorOrFunction === 'function' - ? buildResponse(request, errorOrFunction) - : Promise.reject(errorOrFunction); + errorOrFunction instanceof Error + ? Promise.reject(errorOrFunction) + : typeof errorOrFunction === 'function' + ? buildResponse(request, errorOrFunction) + : Promise.reject(errorOrFunction); function abortError(): Error { return new DOMException('The operation was aborted.');