diff --git a/.changeset/config.json b/.changeset/config.json index 6c36d9f..7e3bff1 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", - "changelog": false, + "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], "linked": [], diff --git a/.changeset/observational-interception.md b/.changeset/observational-interception.md new file mode 100644 index 0000000..986a1f6 --- /dev/null +++ b/.changeset/observational-interception.md @@ -0,0 +1,7 @@ +--- +"@hsblabs/fetch-interceptor": minor +--- + +Preserve original fetch and XHR outcomes when request or response observation fails, normalize XHR no-body and status-0 responses safely, and make adapter installation transactional. + +Narrow `FetchInterceptorError` by transport and remove the runtime-only options type from package exports. diff --git a/CHANGELOG.md b/CHANGELOG.md index dece445..abfc47c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # CHANGELOG +## 0.2.0 + +- Add `onError` support for failed `fetch` and `XMLHttpRequest` requests. +- Normalize failures with transport, reason, and cause details. +- Clean up XHR terminal event listeners correctly across repeated sends. +- Add workspace type checking through `pnpm test:types`. + +## 0.1.1 + +- Preserve `RequestInit` overrides passed to `fetch(request, init)`. +- Restore fetch and XHR globals correctly when multiple interceptors stop out of order. +- Preserve original network results when interceptor callbacks throw or reject. +- Normalize JSON-style XHR responses into readable standard `Response` bodies. + +## 0.1.0 + +- Release the first feature-complete public version of the fetch/XHR interceptor library. + ## 0.0.1 - Initial release of `@hsblabs/fetch-interceptor`. diff --git a/README.md b/README.md index 3a3558c..4ebd0b1 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,15 @@ The library is designed to stay small at the call site. The example below interc import { createFetchInterceptor } from "@hsblabs/fetch-interceptor"; const interceptor = createFetchInterceptor({ - matcher: (req) => { - const url = new URL(req.url); - return url.pathname.includes("/api/target-data") && req.method === "GET"; + matcher: (request) => { + const url = new URL(request.url); + return ( + url.pathname.includes("/api/target-data") && request.method === "GET" + ); }, - onIntercept: async (req, res) => { + onIntercept: async (request, response) => { try { - const data = await res.json(); + const data = await response.json(); console.log("Intercepted data:", data); // For example, forward data from a Chrome extension's main world @@ -69,10 +71,10 @@ const interceptor = createFetchInterceptor({ console.error("Failed to parse intercepted response:", error); } }, - onError: (req, error) => { + onError: (request, error) => { console.error( "Intercepted request failed:", - req.url, + request.url, error.transport, error.reason, error.cause, @@ -87,7 +89,7 @@ interceptor.start(); // interceptor.stop(); ``` -If `matcher` throws, or if `onIntercept`/`onError` throws or returns a rejected promise, the library reports the failure with `console.error` and preserves the original network result. When the underlying fetch/XHR request itself fails before producing a response, `onError` receives a normalized descriptor containing the transport, failure reason, and raw cause. +If matching, response normalization, or a consumer callback fails, the library reports the failure with `console.error` and preserves the original network result. Only an underlying fetch/XHR failure is passed to `onError`. An XHR load with status 0 is represented by `Response.error()`, the only standard `Response` value with status 0; its body and headers are therefore unavailable. ## API Reference @@ -99,16 +101,16 @@ Creates an interceptor instance used to start and stop traffic interception. | Property | Type | Description | | --- | --- | --- | -| `matcher` | `((req: Request) => boolean)?` | Predicate that decides whether a request should be intercepted. When omitted, all matching traffic is intercepted. Exceptions are reported and treated as a non-match. | -| `onIntercept` | `(req: Request, res: Response) => void \| Promise` | Callback invoked when a matching request completes successfully. `res` is a cloned response for fetch, or an equivalent standard `Response` for XHR. Exceptions and rejected promises are reported without changing the original request outcome. | -| `onError` | `(req: Request, error: FetchInterceptorError) => void \| Promise` | Callback invoked when a matching request fails before a response is produced. `error.transport` identifies `fetch` or `xhr`, `error.reason` is `error`, `abort`, or `timeout`, and `error.cause` contains the raw fetch rejection or XHR terminal event. Exceptions and rejected promises are reported without changing the original request outcome. | +| `matcher` | `((request: Request) => boolean)?` | Predicate that decides whether a request should be intercepted. When omitted, all traffic is intercepted. Exceptions are reported and treated as a non-match. | +| `onIntercept` | `(request: Request, response: Response) => void \| Promise` | Callback invoked when a matching request completes successfully. `response` is an independent clone for fetch, or an equivalent standard `Response` for XHR. Exceptions and rejected promises are reported without changing the original request outcome. | +| `onError` | `(request: Request, error: FetchInterceptorError) => void \| Promise` | Callback invoked only when the underlying transport fails before producing a response. Fetch can report `error` or `abort`; XHR can also report `timeout`. `cause` contains the raw fetch rejection or XHR terminal event. | ### `FetchInterceptor` | Method | Description | | --- | --- | -| `start()` | Overrides `fetch` and `XMLHttpRequest` to begin interception. Calling it more than once is safe. | -| `stop()` | Stops interception and restores the original browser APIs. | +| `start()` | Overrides `fetch` and `XMLHttpRequest` to begin interception. Calling it more than once is safe. If installation fails, completed patches are rolled back and the interceptor remains stopped. | +| `stop()` | Stops interception and attempts to restore every original browser API even if one restoration fails. | ## Use Cases diff --git a/docs/README/ja.md b/docs/README/ja.md index f62926a..22e67b9 100644 --- a/docs/README/ja.md +++ b/docs/README/ja.md @@ -53,13 +53,15 @@ pnpm test:e2e:browser import { createFetchInterceptor } from "@hsblabs/fetch-interceptor"; const interceptor = createFetchInterceptor({ - matcher: (req) => { - const url = new URL(req.url); - return url.pathname.includes("/api/target-data") && req.method === "GET"; + matcher: (request) => { + const url = new URL(request.url); + return ( + url.pathname.includes("/api/target-data") && request.method === "GET" + ); }, - onIntercept: async (req, res) => { + onIntercept: async (request, response) => { try { - const data = await res.json(); + const data = await response.json(); console.log("Intercepted data:", data); // たとえば Chrome 拡張の main world から isolated world へ @@ -69,6 +71,15 @@ const interceptor = createFetchInterceptor({ console.error("Failed to parse intercepted response:", error); } }, + onError: (request, error) => { + console.error( + "Intercepted request failed:", + request.url, + error.transport, + error.reason, + error.cause, + ); + }, }); interceptor.start(); @@ -78,6 +89,8 @@ interceptor.start(); // interceptor.stop(); ``` +条件判定、レスポンス正規化、利用側コールバックのいずれかが失敗しても、ライブラリは `console.error` へ報告し、元の通信結果を維持します。`onError` に渡されるのは基盤の fetch/XHR 自体が失敗した場合だけです。status 0 で完了した XHR は、status 0 を持てる唯一の標準 `Response` である `Response.error()` として表現されるため、本文とヘッダーは利用できません。 + ## API リファレンス ### `createFetchInterceptor(options: FetchInterceptorOptions): FetchInterceptor` @@ -88,15 +101,16 @@ interceptor.start(); | プロパティ | 型 | 説明 | | --- | --- | --- | -| `matcher` | `((req: Request) => boolean)?` | リクエストを傍受するかを判定する述語です。省略時はすべての通信を傍受します。 | -| `onIntercept` | `(req: Request, res: Response) => void` | 条件に一致した通信完了時に呼ばれるコールバックです。`res` は fetch では clone されたレスポンス、XHR では等価な標準 `Response` です。 | +| `matcher` | `((request: Request) => boolean)?` | リクエストを傍受するかを判定する述語です。省略時はすべての通信を傍受します。例外は報告され、条件不一致として扱われます。 | +| `onIntercept` | `(request: Request, response: Response) => void \| Promise` | 条件に一致した通信完了時に呼ばれるコールバックです。`response` は fetch では独立した clone、XHR では等価な標準 `Response` です。コールバックの失敗は元の通信結果を変更しません。 | +| `onError` | `(request: Request, error: FetchInterceptorError) => void \| Promise` | レスポンス生成前に基盤の通信が失敗した場合だけ呼ばれます。fetch は `error` または `abort`、XHR は加えて `timeout` を報告できます。 | ### `FetchInterceptor` | メソッド | 説明 | | --- | --- | -| `start()` | `fetch` と `XMLHttpRequest` を上書きして傍受を開始します。複数回呼んでも安全です。 | -| `stop()` | 傍受を停止し、元のブラウザ API を復元します。 | +| `start()` | `fetch` と `XMLHttpRequest` を上書きして傍受を開始します。導入に失敗した場合は完了済みの変更を戻し、停止状態を維持します。 | +| `stop()` | 傍受を停止します。一部の復元に失敗しても、必要な復元をすべて試みます。 | ## ユースケース diff --git a/docs/README/ko.md b/docs/README/ko.md index 705da0d..de4f6e6 100644 --- a/docs/README/ko.md +++ b/docs/README/ko.md @@ -53,13 +53,15 @@ pnpm test:e2e:browser import { createFetchInterceptor } from "@hsblabs/fetch-interceptor"; const interceptor = createFetchInterceptor({ - matcher: (req) => { - const url = new URL(req.url); - return url.pathname.includes("/api/target-data") && req.method === "GET"; + matcher: (request) => { + const url = new URL(request.url); + return ( + url.pathname.includes("/api/target-data") && request.method === "GET" + ); }, - onIntercept: async (req, res) => { + onIntercept: async (request, response) => { try { - const data = await res.json(); + const data = await response.json(); console.log("Intercepted data:", data); // 예를 들어 Chrome 확장의 main world 에서 @@ -69,6 +71,15 @@ const interceptor = createFetchInterceptor({ console.error("Failed to parse intercepted response:", error); } }, + onError: (request, error) => { + console.error( + "Intercepted request failed:", + request.url, + error.transport, + error.reason, + error.cause, + ); + }, }); interceptor.start(); @@ -78,6 +89,8 @@ interceptor.start(); // interceptor.stop(); ``` +매칭, 응답 정규화 또는 사용자 콜백이 실패해도 라이브러리는 `console.error` 로 보고하고 원래 네트워크 결과를 유지합니다. 기반 fetch/XHR 자체가 실패한 경우에만 `onError` 를 호출합니다. status 0 으로 완료된 XHR 은 status 0 을 가질 수 있는 유일한 표준 `Response` 인 `Response.error()` 로 표현되므로 본문과 헤더는 사용할 수 없습니다. + ## API 레퍼런스 ### `createFetchInterceptor(options: FetchInterceptorOptions): FetchInterceptor` @@ -88,15 +101,16 @@ interceptor.start(); | 속성 | 타입 | 설명 | | --- | --- | --- | -| `matcher` | `((req: Request) => boolean)?` | 요청을 가로챌지 결정하는 predicate 입니다. 생략하면 모든 트래픽을 가로챕니다. | -| `onIntercept` | `(req: Request, res: Response) => void` | 조건에 맞는 요청이 완료되면 호출되는 콜백입니다. `res` 는 fetch 에서는 clone 된 응답이고, XHR 에서는 이에 상응하는 표준 `Response` 입니다. | +| `matcher` | `((request: Request) => boolean)?` | 요청을 가로챌지 결정하는 predicate 입니다. 생략하면 모든 트래픽을 가로챕니다. 예외는 보고되고 조건 불일치로 처리됩니다. | +| `onIntercept` | `(request: Request, response: Response) => void \| Promise` | 조건에 맞는 요청이 완료되면 호출됩니다. `response` 는 fetch 에서는 독립된 clone 이고, XHR 에서는 이에 상응하는 표준 `Response` 입니다. 콜백 실패는 원래 네트워크 결과를 변경하지 않습니다. | +| `onError` | `(request: Request, error: FetchInterceptorError) => void \| Promise` | 응답이 만들어지기 전에 기반 전송이 실패한 경우에만 호출됩니다. fetch 는 `error` 또는 `abort`, XHR 은 추가로 `timeout` 을 보고할 수 있습니다. | ### `FetchInterceptor` | 메서드 | 설명 | | --- | --- | -| `start()` | `fetch` 와 `XMLHttpRequest` 를 override 하여 인터셉션을 시작합니다. 여러 번 호출해도 안전합니다. | -| `stop()` | 인터셉션을 중지하고 원래 브라우저 API 를 복원합니다. | +| `start()` | `fetch` 와 `XMLHttpRequest` 를 override 하여 인터셉션을 시작합니다. 설치가 실패하면 완료된 변경을 되돌리고 중지 상태를 유지합니다. | +| `stop()` | 인터셉션을 중지합니다. 일부 복원이 실패해도 필요한 모든 복원을 시도합니다. | ## 사용 사례 diff --git a/docs/README/zh-CN.md b/docs/README/zh-CN.md index f80ce55..6a23741 100644 --- a/docs/README/zh-CN.md +++ b/docs/README/zh-CN.md @@ -53,13 +53,15 @@ pnpm test:e2e:browser import { createFetchInterceptor } from "@hsblabs/fetch-interceptor"; const interceptor = createFetchInterceptor({ - matcher: (req) => { - const url = new URL(req.url); - return url.pathname.includes("/api/target-data") && req.method === "GET"; + matcher: (request) => { + const url = new URL(request.url); + return ( + url.pathname.includes("/api/target-data") && request.method === "GET" + ); }, - onIntercept: async (req, res) => { + onIntercept: async (request, response) => { try { - const data = await res.json(); + const data = await response.json(); console.log("Intercepted data:", data); // 例如,将数据从 Chrome 扩展的 main world @@ -69,6 +71,15 @@ const interceptor = createFetchInterceptor({ console.error("Failed to parse intercepted response:", error); } }, + onError: (request, error) => { + console.error( + "Intercepted request failed:", + request.url, + error.transport, + error.reason, + error.cause, + ); + }, }); interceptor.start(); @@ -78,6 +89,8 @@ interceptor.start(); // interceptor.stop(); ``` +匹配、响应规范化或调用方回调失败时,库会通过 `console.error` 报告并保留原始网络结果。只有底层 fetch/XHR 本身失败时才会调用 `onError`。以 status 0 完成的 XHR 会表示为 `Response.error()`,因为它是唯一能持有 status 0 的标准 `Response`;因此响应体和响应头不可用。 + ## API 参考 ### `createFetchInterceptor(options: FetchInterceptorOptions): FetchInterceptor` @@ -88,15 +101,16 @@ interceptor.start(); | 属性 | 类型 | 说明 | | --- | --- | --- | -| `matcher` | `((req: Request) => boolean)?` | 用于判断请求是否应被拦截的谓词。省略时将拦截所有流量。 | -| `onIntercept` | `(req: Request, res: Response) => void` | 当匹配请求完成时调用的回调。`res` 在 fetch 场景中是克隆后的响应,在 XHR 场景中是等价的标准 `Response`。 | +| `matcher` | `((request: Request) => boolean)?` | 用于判断请求是否应被拦截的谓词。省略时将拦截所有流量。异常会被报告并视为不匹配。 | +| `onIntercept` | `(request: Request, response: Response) => void \| Promise` | 当匹配请求完成时调用的回调。`response` 在 fetch 场景中是独立克隆,在 XHR 场景中是等价的标准 `Response`。回调失败不会改变原始网络结果。 | +| `onError` | `(request: Request, error: FetchInterceptorError) => void \| Promise` | 仅在底层传输于生成响应前失败时调用。fetch 可报告 `error` 或 `abort`,XHR 还可报告 `timeout`。 | ### `FetchInterceptor` | 方法 | 说明 | | --- | --- | -| `start()` | 覆盖 `fetch` 和 `XMLHttpRequest` 以开始拦截。重复调用也是安全的。 | -| `stop()` | 停止拦截并恢复原始浏览器 API。 | +| `start()` | 覆盖 `fetch` 和 `XMLHttpRequest` 以开始拦截。安装失败时会回滚已完成的修改并保持停止状态。 | +| `stop()` | 停止拦截。即使某一步恢复失败,也会尝试所有必要的恢复操作。 | ## 使用场景 diff --git a/docs/adr/2026-08-01-observational-interception.md b/docs/adr/2026-08-01-observational-interception.md new file mode 100644 index 0000000..dda9b0f --- /dev/null +++ b/docs/adr/2026-08-01-observational-interception.md @@ -0,0 +1,31 @@ +--- +title: Preserve observational interception across all failure paths +status: accepted +date: 2026-08-01T02:37:54+09:00 +agent: GPT-5 Codex +--- + +# Preserve observational interception across all failure paths + +## Context + +The interceptor promises that matcher and callback failures do not change the original network result. The same invariant must also cover request and response normalization, callback response cloning, startup, and shutdown. + +The existing implementation has two independent handler paths used by production and tests. It also exposes resolved runtime options from the package entrypoint and permits transport/reason error combinations that the implementation cannot produce. + +## Decision + +- Only a rejection from the underlying `fetch` or an XHR terminal failure event is reported through `onError`. +- Matcher, callback, cloning, and normalization failures are reported to `console.error` and never replace the original network result. +- XHR statuses 204, 205, and 304 produce a `Response` with a null body. XHR status 0 is represented by `Response.error()`, the standard `Response` value that can carry status 0. +- Installing the fetch and XHR adapters is transactional. A failed installation restores every adapter already installed and leaves the interceptor stopped. +- `FetchInterceptorError` is a discriminated union keyed by `transport`, so fetch cannot report the XHR-only `timeout` reason. +- Tests exercise callback and lifecycle behavior through `createFetchInterceptor`. Pure request/response normalization remains an internal seam with focused tests. +- Only consumer-facing types are exported from the package entrypoint. Resolved runtime options remain internal. + +## Consequences + +- The original fetch/XHR outcome remains authoritative even when observation cannot be completed. +- XHR status 0 cannot retain response body or headers because the standard `Response` interface has no constructible successful status-0 representation. +- The earlier direct-handler testing approach is superseded. Normalization helpers remain directly testable, while transport behavior is tested through the public interface. +- Public declaration comments must be preserved so consumers can see lifecycle and failure contracts in editor tooling. diff --git a/docs/tickets/2026-08-01-code-quality-hardening.md b/docs/tickets/2026-08-01-code-quality-hardening.md new file mode 100644 index 0000000..f079b31 --- /dev/null +++ b/docs/tickets/2026-08-01-code-quality-hardening.md @@ -0,0 +1,24 @@ +--- +title: Code quality hardening tickets +status: completed +date: 2026-08-01 +--- + +# Code quality hardening tickets + +## Completion criteria + +- Observation failures never alter the original fetch/XHR outcome or invoke `onError` as a transport failure. +- Every representable XHR terminal success produces a standard `Response` without throwing. +- Startup and shutdown keep registry state and installed global adapters consistent on failure. +- Public types exclude runtime-only details and make invalid transport/reason combinations unrepresentable. +- Production behavior has one implementation path, naming is explicit, pure normalization is separated from global I/O, and public JSDoc ships in declarations. +- Unit, type, build, Node E2E, and browser E2E checks pass. + +## Tracer tickets + +- [x] QH-1: Add public-interface regressions for consumed fetch responses, XHR no-body/status-0 responses, callback failures, and failed startup. +- [x] QH-2: Make response observation safe and normalize XHR no-body/status-0 responses. Blocked by QH-1. +- [x] QH-3: Make lifecycle transitions transactional and model transport failures as a discriminated union. Blocked by QH-1. +- [x] QH-4: Remove duplicate handler paths, separate XHR normalization, tighten exports and naming, and preserve useful public JSDoc. Blocked by QH-2 and QH-3. +- [x] QH-5: Run declaration, unit, type, build, Node E2E, and browser E2E verification. Blocked by QH-1 through QH-4. diff --git a/docs/tickets/2026-08-01-release-0.3.0.md b/docs/tickets/2026-08-01-release-0.3.0.md new file mode 100644 index 0000000..9dab61c --- /dev/null +++ b/docs/tickets/2026-08-01-release-0.3.0.md @@ -0,0 +1,55 @@ +--- +title: 0.3.0 release readiness +status: ready +date: 2026-08-01 +package: "@hsblabs/fetch-interceptor" +target: 0.3.0 +--- + +# 0.3.0 release readiness + +## Goal + +Prepare the observational-interception hardening changes for a safe `0.3.0` release without publishing, tagging, pushing, or consuming the pending Changeset. + +## Release invariants + +- The npm `latest` tag remains on `0.2.0` until the release PR is intentionally merged. +- The pending minor Changeset remains the source of the `0.3.0` version and release notes. +- The package contains only the declared runtime files and has no runtime dependencies. +- A failed release gate must stop publication before `changeset publish` runs. +- npm versions are immutable; rollback means deprecating a bad version and publishing a corrective patch, not overwriting it. + +## Readiness tickets + +- [x] REL-1: Refresh the development and release toolchain until the dependency audit has no critical or high findings. +- [x] REL-2: Restore generated changelog maintenance and backfill the missing published release notes. +- [x] REL-3: Pass frozen install, lint, type, unit, build, Node E2E, and real-browser E2E checks. +- [x] REL-4: Inspect the packed tarball and verify it from a clean consumer project. +- [x] REL-5: Confirm the Changeset plan, current npm dist-tag, release workflow, and rollback procedure. + +## Verification snapshot + +- `pnpm install --frozen-lockfile`: passed. +- `pnpm audit --audit-level high`: passed with no critical or high findings; the remaining low and moderate findings are confined to the Vite development toolchain. `pnpm audit --prod` reports no known vulnerabilities. +- `pnpm check`: passed with 45 unit tests, workspace type checks, lint, and the production build. +- Node E2E: 4 tests passed. +- Browser E2E: 5 tests passed with installed Google Chrome. +- Version preview: generated `0.3.0`, updated `CHANGELOG.md`, and consumed only the pending Changeset. +- Package preview: 8 declared files, 14.1 kB compressed, no runtime dependencies, and a clean-consumer smoke test passed. +- Publish preview: `pnpm publish --dry-run --no-git-checks` completed for `0.3.0` without publishing. +- Registry state: npm `latest` remains `0.2.0`; no open pull request exists at preparation time. + +## Operator-controlled release sequence + +1. Push the prepared commits through the normal review path. +2. Confirm that the Changesets action opens or updates the `Version Packages` PR for `0.3.0`. +3. Review the generated version and changelog diff. +4. Merge the version PR only when publication is intended. +5. Verify the publish workflow, npm version and dist-tag, package contents, and GitHub tag independently. + +## Rollback + +- Before publication: do not merge the `Version Packages` PR, or revert the candidate changes through the normal review path. +- After publication: deprecate the affected version, publish a corrective patch, and change `latest` only with explicit owner approval. +- Never attempt to overwrite an existing npm version. diff --git a/e2e/fixtures/browser/app.js b/e2e/fixtures/browser/app.js index d450c21..eed657b 100644 --- a/e2e/fixtures/browser/app.js +++ b/e2e/fixtures/browser/app.js @@ -68,7 +68,7 @@ function sendXhr(url, body) { const xhr = new XMLHttpRequest(); xhr.open("POST", url); xhr.setRequestHeader("content-type", "application/json"); - xhr.addEventListener("load", () => resolve()); + xhr.addEventListener("load", () => setTimeout(resolve, 0)); xhr.addEventListener("error", () => reject(new Error("XHR request failed.")), ); @@ -128,6 +128,30 @@ async function runXhrScenario({ useMatcher = false } = {}) { return recorder.flush(); } +async function runXhrNoContentScenario() { + const recorder = createEventRecorder(); + let responseStatus = null; + const interceptor = createFetchInterceptor({ + onIntercept: (request, response) => { + responseStatus = response.status; + recorder.onIntercept(request, response); + }, + }); + + interceptor.start(); + + try { + await sendXhr("/api/no-content", { source: "xhr-no-content" }); + } finally { + interceptor.stop(); + } + + return { + events: await recorder.flush(), + responseStatus, + }; +} + async function runConcurrentFetchScenario() { const recorderA = createEventRecorder(); const recorderB = createEventRecorder(); @@ -220,5 +244,6 @@ window.e2e = { runConcurrentFetchScenario, runConcurrentXhrScenario, runFetchScenario, + runXhrNoContentScenario, runXhrScenario, }; diff --git a/e2e/package.json b/e2e/package.json index d9bc8d0..f0a778d 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -18,10 +18,10 @@ "node": ">=22" }, "devDependencies": { - "@playwright/test": "^1.50.0", - "@types/node": "^25.4.0", - "tsx": "^4.19.2", + "@playwright/test": "^1.62.1", + "@types/node": "^25.9.5", + "tsx": "^4.23.1", "typescript": "^5.9.3", - "vitest": "^4.0.18" + "vitest": "^4.1.10" } } diff --git a/e2e/src/fixture-server.ts b/e2e/src/fixture-server.ts index 8ae7c67..eb03bf7 100644 --- a/e2e/src/fixture-server.ts +++ b/e2e/src/fixture-server.ts @@ -72,6 +72,12 @@ async function handleApiRequest( return; } + if (requestUrl.pathname === "/api/no-content") { + response.statusCode = 204; + response.end(); + return; + } + const delayMs = Number(requestUrl.searchParams.get("delayMs") ?? "0"); if (Number.isFinite(delayMs) && delayMs > 0) { diff --git a/e2e/tests/browser.browser.spec.ts b/e2e/tests/browser.browser.spec.ts index 48d8331..a6eb570 100644 --- a/e2e/tests/browser.browser.spec.ts +++ b/e2e/tests/browser.browser.spec.ts @@ -32,6 +32,10 @@ declare global { runXhrScenario: (options?: { useMatcher?: boolean; }) => Promise; + runXhrNoContentScenario: () => Promise<{ + events: BrowserIntercept[]; + responseStatus: number | null; + }>; }; } } @@ -77,6 +81,31 @@ test("intercepts real browser xhr traffic and respects matcher filters", async ( }); }); +test("normalizes a real browser xhr 204 response without a body", async ({ + page, +}) => { + const browserErrors: string[] = []; + page.on("console", (message) => { + if (message.type() === "error") { + browserErrors.push(message.text()); + } + }); + page.on("pageerror", (error) => browserErrors.push(error.message)); + + await page.goto("/", { waitUntil: "networkidle" }); + + const result = await page.evaluate(() => + window.e2e.runXhrNoContentScenario(), + ); + + expect(browserErrors).toEqual([]); + expect(result.responseStatus).toBe(204); + expect(result.events).toHaveLength(1); + expect(result.events[0]?.request.url).toContain("/api/no-content"); + expect(result.events[0]?.response.status).toBe(204); + expect(result.events[0]?.response.body).toBe(""); +}); + test("keeps remaining browser fetch interceptors active and snapshots in-flight requests", async ({ page, }) => { diff --git a/package.json b/package.json index 28b80ce..53af58d 100644 --- a/package.json +++ b/package.json @@ -59,11 +59,11 @@ }, "devDependencies": { "@biomejs/biome": "2.4.5", - "@changesets/cli": "^2.30.0", - "@types/node": "^25.4.0", - "tsdown": "^0.21.2", + "@changesets/cli": "^2.31.1", + "@types/node": "^25.9.5", + "tsdown": "^0.22.14", "typescript": "^5.9.3", - "vitest": "^4.0.18" + "vitest": "^4.1.10" }, "exports": { ".": "./dist/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80685fe..f9fcc5f 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + vite: 7.3.5 + importers: .: @@ -12,66 +15,45 @@ importers: specifier: 2.4.5 version: 2.4.5 '@changesets/cli': - specifier: ^2.30.0 - version: 2.30.0(@types/node@25.4.0) + specifier: ^2.31.1 + version: 2.31.1(@types/node@25.9.5) '@types/node': - specifier: ^25.4.0 - version: 25.4.0 + specifier: ^25.9.5 + version: 25.9.5 tsdown: - specifier: ^0.21.2 - version: 0.21.2(typescript@5.9.3) + specifier: ^0.22.14 + version: 0.22.14(tsx@4.23.1)(typescript@5.9.3)(unrun@0.2.32(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)) typescript: specifier: ^5.9.3 version: 5.9.3 vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2) + specifier: ^4.1.10 + version: 4.1.10(@types/node@25.9.5)(vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2)) e2e: devDependencies: '@playwright/test': - specifier: ^1.50.0 - version: 1.58.2 + specifier: ^1.62.1 + version: 1.62.1 '@types/node': - specifier: ^25.4.0 - version: 25.4.0 + specifier: ^25.9.5 + version: 25.9.5 tsx: - specifier: ^4.19.2 - version: 4.21.0 + specifier: ^4.23.1 + version: 4.23.1 typescript: specifier: ^5.9.3 version: 5.9.3 vitest: - specifier: ^4.0.18 - version: 4.0.18(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2) + specifier: ^4.1.10 + version: 4.1.10(@types/node@25.9.5)(vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2)) packages: - '@babel/generator@8.0.0-rc.2': - resolution: {integrity: sha512-oCQ1IKPwkzCeJzAPb7Fv8rQ9k5+1sG8mf2uoHiMInPYvkRfrDJxbTIbH51U+jstlkghus0vAi3EBvkfvEsYNLQ==} - engines: {node: ^20.19.0 || >=22.12.0} - - '@babel/helper-string-parser@8.0.0-rc.2': - resolution: {integrity: sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ==} - engines: {node: ^20.19.0 || >=22.12.0} - - '@babel/helper-validator-identifier@8.0.0-rc.2': - resolution: {integrity: sha512-xExUBkuXWJjVuIbO7z6q7/BA9bgfJDEhVL0ggrggLMbg0IzCUWGT1hZGE8qUH7Il7/RD/a6cZ3AAFrrlp1LF/A==} - engines: {node: ^20.19.0 || >=22.12.0} - - '@babel/parser@8.0.0-rc.2': - resolution: {integrity: sha512-29AhEtcq4x8Dp3T72qvUMZHx0OMXCj4Jy/TEReQa+KWLln524Cj1fWb3QFi0l/xSpptQBR6y9RNEXuxpFvwiUQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@babel/types@8.0.0-rc.2': - resolution: {integrity: sha512-91gAaWRznDwSX4E2tZ1YjBuIfnQVOFDCQ2r0Toby0gu4XEbyF623kXLMA8d4ZbCu+fINcrudkmEcwSUHgDDkNw==} - engines: {node: ^20.19.0 || >=22.12.0} - '@biomejs/biome@2.4.5': resolution: {integrity: sha512-OWNCyMS0Q011R6YifXNOg6qsOg64IVc7XX6SqGsrGszPbkVCoaO7Sr/lISFnXZ9hjQhDewwZ40789QmrG0GYgQ==} engines: {node: '>=14.21.3'} @@ -129,30 +111,30 @@ packages: cpu: [x64] os: [win32] - '@changesets/apply-release-plan@7.1.0': - resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} - '@changesets/assemble-release-plan@6.0.9': - resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.30.0': - resolution: {integrity: sha512-5D3Nk2JPqMI1wK25pEymeWRSlSMdo5QOGlyfrKg0AOufrUcjEE3RQgaCpHoBiM31CSNrtSgdJ0U6zL1rLDDfBA==} + '@changesets/cli@2.31.1': + resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} hasBin: true - '@changesets/config@3.1.3': - resolution: {integrity: sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw==} + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - '@changesets/get-dependents-graph@2.1.3': - resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} - '@changesets/get-release-plan@4.0.15': - resolution: {integrity: sha512-Q04ZaRPuEVZtA+auOYgFaVQQSA98dXiVe/yFaZfY7hoSmQICHGvP0TF4u3EDNHWmmCS4ekA/XSpKlSM2PyTS2g==} + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -184,167 +166,323 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/core@2.0.0-alpha.3': + resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@2.0.0-alpha.3': + resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emnapi/wasi-threads@2.0.1': + resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -358,27 +496,21 @@ packages: '@types/node': optional: true - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@napi-rs/wasm-runtime@1.1.1': - resolution: {integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==} + '@napi-rs/wasm-runtime@1.2.2': + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -395,9 +527,12 @@ packages: '@oxc-project/types@0.115.0': resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} - '@playwright/test@1.58.2': - resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} - engines: {node: '>=18'} + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} + + '@playwright/test@1.62.1': + resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==} + engines: {node: '>=20'} hasBin: true '@quansync/fs@1.0.0': @@ -409,30 +544,60 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.2.1': + resolution: {integrity: sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.0-rc.9': resolution: {integrity: sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.2.1': + resolution: {integrity: sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.0.0-rc.9': resolution: {integrity: sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.2.1': + resolution: {integrity: sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.0.0-rc.9': resolution: {integrity: sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.2.1': + resolution: {integrity: sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': resolution: {integrity: sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': + resolution: {integrity: sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -440,6 +605,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.2.1': + resolution: {integrity: sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -447,6 +619,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.2.1': + resolution: {integrity: sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -454,6 +633,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.1': + resolution: {integrity: sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -461,6 +647,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.1': + resolution: {integrity: sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -468,6 +661,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.1': + resolution: {integrity: sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -475,175 +675,207 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.2.1': + resolution: {integrity: sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': resolution: {integrity: sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.2.1': + resolution: {integrity: sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': resolution: {integrity: sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.2.1': + resolution: {integrity: sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': resolution: {integrity: sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.2.1': + resolution: {integrity: sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': resolution: {integrity: sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.1': + resolution: {integrity: sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.0-rc.9': resolution: {integrity: sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==} - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/rollup-android-arm-eabi@4.62.3': + resolution: {integrity: sha512-c0wdcekXtQvvn5Tsrk/+op/gUArrbWaFduBnTLP2l1cKLSQs4diMWjJw3m6A0DdzT8dAAX95KpkJ3qynCePbmw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@rollup/rollup-android-arm64@4.62.3': + resolution: {integrity: sha512-3YjElDdWN+qXAFbJ/CzPV+0wspLqh54k/I6GfdYtEJRqg7buSgc1yPM3B+93j1M4neobtkATHZTmxK2AMVGfnA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@rollup/rollup-darwin-arm64@4.62.3': + resolution: {integrity: sha512-Pch2pFNOxxz1hTjypIdPyRTR6riiwRl84+VcN9djS680fw+Co1nAJINrdpqp7KV0NvyuU8ilZXZCjd7ykJl1GQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@rollup/rollup-darwin-x64@4.62.3': + resolution: {integrity: sha512-LEuncFUHFiF8t4yZVZvvZA1wk0pjAscRnsrn1EfTEmN4HXotBi2YtcnLRyaK6UbuczW7xZS5ES+81Rdz8Z0T6g==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + '@rollup/rollup-freebsd-arm64@4.62.3': + resolution: {integrity: sha512-zvBUvsQUpOWALdDsk6qbS8bXf2VxmPisuudNDrY7x0p0jBdsoZl8HsHczIOgkQiZldmcacMKtBzpoGVNeIe2bQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@rollup/rollup-freebsd-x64@4.62.3': + resolution: {integrity: sha512-C2KmNrcSem/AMg984H/dev+si0lieQGdXdR/lYGJnuumXnFb9Y7QdiI62obFdLlxRYLBv4P0eUVIDbD4c1vVvw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': + resolution: {integrity: sha512-ggXnsTAEzNQx74XpunRsiZ9aBZDsI7XIa0hm2nzR9f4WzH5/f/d73ZSDaC5ejJ8YLY4NW+V3wr0tjOaeCq8hqA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.3': + resolution: {integrity: sha512-2vng+FlzNUhKZxtej3IUqJgbZoQk2M/dwQM20+ULV0R/E/8tr9/P6uEf2iiGIk4HL0zMKh5Jry7mUHdUOvyGgA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@rollup/rollup-linux-arm64-gnu@4.62.3': + resolution: {integrity: sha512-LLLFZKt4/Nraf9rxDkhiU8QVgLF4WmCkfr0L4fj0fPfIZFBib0DeiFk1hhaYKd03LFAFJcxHslhDFlNJLylf5Q==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@rollup/rollup-linux-arm64-musl@4.62.3': + resolution: {integrity: sha512-WJkdQCvS9sWNOUBJZfQRKpZGFBztRzcowI+nndmflKgU4XY+3a420FgTOSKTsVqJbnzSxeT4vaJalpOaPo2YCQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + '@rollup/rollup-linux-loong64-gnu@4.62.3': + resolution: {integrity: sha512-PwHXCCS2n64/1Ot6rP1YEYA02MGYBcQlr8CSZZyrUG2O7NH6NklYmvr9v3Jy+5e/eDeNchc/ukmKJi9LuflMIQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + '@rollup/rollup-linux-loong64-musl@4.62.3': + resolution: {integrity: sha512-vUjxINQu3RC8NZS3ykk1gN65gIz8pAopOq2HXuZhiIxHdx7TFvDG+jgrdSgInu1Eza4/Rfi2VzZgyIgEH4WOaw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@rollup/rollup-linux-ppc64-gnu@4.62.3': + resolution: {integrity: sha512-wzko4aJ13+0G3kGnviCg5gnXFKd40izKsrf2uOw12US4XqprkDrmwOpeW14aSNa37V8bfPcz5Fkob6LZ3BAPmA==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + '@rollup/rollup-linux-ppc64-musl@4.62.3': + resolution: {integrity: sha512-8120ue0JUMSwy11stlwnfdX3pPd+WZYGCDBwEHWtIHi6pOpZmsEF5QKB7a/UN+XFdqvobxz98kv8RTqikyCEBw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.3': + resolution: {integrity: sha512-XLFHnR3tXMjbOCh2vtVJHmxt+995uJsTERQyseFDRA0xxMxyTZPLa3OIUlyFaO4mF/Lu0FjmWHCuPXJT1n/IOg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@rollup/rollup-linux-riscv64-musl@4.62.3': + resolution: {integrity: sha512-se6yXvNGMIl0f+RQzyh7XAmia8/9kplQx424wnG2w0C1oi6XgO6Y8otKhdXFHbHs88Ihavzmvh1NWjuovE76BQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@rollup/rollup-linux-s390x-gnu@4.62.3': + resolution: {integrity: sha512-gNoxRefktVIiGflpONuxWWXZAzIQG++z9qHO3xKwk4WdDMuQja3JHGfE1u0i3PfPDyvhypdk+WrgIJqLhGG7sg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@rollup/rollup-linux-x64-gnu@4.62.3': + resolution: {integrity: sha512-V4KtWtQfAFMU7+9/A/VDps/VI8CHd3cYz0L8sgJzz8qK7eY7wI4ruFD82UYIYvW9Z4DtlTfhQcsl4XyPHW5uSg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@rollup/rollup-linux-x64-musl@4.62.3': + resolution: {integrity: sha512-LBx9LYXvj2CBkMkjLdNAWLwH0MLMin7do2VcVo9kVPibGLkY0BQQut2fv7NVqkXqZ/CrAu9LqDHVV1xHCMpCPw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + '@rollup/rollup-openbsd-x64@4.62.3': + resolution: {integrity: sha512-ABVf3Q0RCu7NcyCCOZQI0pJ3GuSdfSl8EXcy88QtdceIMIoCUdfhsJChZ64L9zVM2aJHjde1Bhn5uqSRcX9ySA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@rollup/rollup-openharmony-arm64@4.62.3': + resolution: {integrity: sha512-+2Cy/ldweGBLlPIKsQLF8U5N44a0KDdbrk1rAjHOM9M2K+kGdIVjHLmmrZIcx+9Ny3ke/1JomCsDI1ocb11+sg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@rollup/rollup-win32-arm64-msvc@4.62.3': + resolution: {integrity: sha512-dtZvzc8BedpSaFNy75x6uiWwAGTH+aZHDtdrqP6qk+WcLJrfti6sGje1ZJ9UxyzDLF23d/mV+PaMwuC0hL7UVA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@rollup/rollup-win32-ia32-msvc@4.62.3': + resolution: {integrity: sha512-Rj8Ra4noo+aYy7sKBggCx0407mws34kAb1ySyWuq5DAtFBQdkSwnsjCgPrhPe9cvgBKZIukpE+CVHvORCS93kQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + '@rollup/rollup-win32-x64-gnu@4.62.3': + resolution: {integrity: sha512-vp7N084ew/odXn2gi/mzm9mUkQu9l6AiN6dt4IeUM2Uvm9o+cVmP+YkqbMOteLbiGgqBBlJZjIMYVCfOOIVbVQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@rollup/rollup-win32-x64-msvc@4.62.3': + resolution: {integrity: sha512-MOG/3gTOn4Fwf574RVOaY61I5o6P90legkFADiTyn1hyjNydT+cerU2rLUwPdZkKKyJ+iT+K9p7WXK4LM1Ka6g==} cpu: [x64] os: [win32] '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -651,46 +883,168 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/jsesc@2.5.1': - resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@25.4.0': - resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + vite: 7.3.5 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + + '@yuku-codegen/binding-darwin-arm64@0.8.1': + resolution: {integrity: sha512-Ck0RmDBLc+R0dZpfTXmJFAvcuPf+npbJF1T/VhC9pPDGjNEOGwn4DwFcjhg//DfNIO+xgktsLSt5S4v0MFrToA==} + cpu: [arm64] + os: [darwin] + + '@yuku-codegen/binding-darwin-x64@0.8.1': + resolution: {integrity: sha512-6BDLLWi8wHbCghgkajS+OeVJX2jLY9pY9HV/qH9daLP1XCyMSPssEiXjKyPiJq+St5b+zcPgMEBF8cgJDNhMMQ==} + cpu: [x64] + os: [darwin] + + '@yuku-codegen/binding-freebsd-x64@0.8.1': + resolution: {integrity: sha512-Rd1PHfw2mxC8y00kWxYWDClnBC1NbSwPuq0zKLYPggE7mlOnV+eQ15futtaz/Q4BP+jOtEv9elxvP7by20h6Kw==} + cpu: [x64] + os: [freebsd] + + '@yuku-codegen/binding-linux-arm-gnu@0.8.1': + resolution: {integrity: sha512-/XGncN6jQ/CwXNgWwo+XnsUmILY1q1mbS1f38XnYOHw3DOKOISxVIAlD9IsmdrHLAUAncFlgFS3ky4OO9fvjyg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-arm-musl@0.8.1': + resolution: {integrity: sha512-Rxut53qx3JbTkQ8+A6I8Lrplwz55HQVCjEsTsWF1uOpfgALdvuKSMPIE0gVqefEEcOSA9ASklbNacmXwiqnaMw==} + cpu: [arm] + os: [linux] + libc: [musl] - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@yuku-codegen/binding-linux-arm64-gnu@0.8.1': + resolution: {integrity: sha512-hBNkUPHX04BjB2oIIgIlYT2tog9x0rmBVkZep0f4LLYyFhdJfYM/s2pJ0c8ehQhhkQaJFokW4CxGhiVvVLlzWg==} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@yuku-codegen/binding-linux-arm64-musl@0.8.1': + resolution: {integrity: sha512-ePZZnmOxArA4jA3AWlJj3WdY5z1ahQSLwAYrv7mozkJVcrBz2wZgy8l9ukcHFpTxPyhxCAea4gBSZEqqsCYE3A==} + cpu: [arm64] + os: [linux] + libc: [musl] - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@yuku-codegen/binding-linux-x64-gnu@0.8.1': + resolution: {integrity: sha512-LGAEXnQ4Xg/BtOuEirABwa9ByUTXFjealqXXAHVFb8EEmZu5QeHLtsdB2MA2IcYVAgW/QPkyqfnd80R4FltI2A==} + cpu: [x64] + os: [linux] + libc: [glibc] - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@yuku-codegen/binding-linux-x64-musl@0.8.1': + resolution: {integrity: sha512-PPDMcQIKCNTOaLBBKh9SBM1EAPcsvnlShMAxq2D/twnSCSEUI9LfrR+G9J4P3o+46ZS4WN8mjZsj8p4PKOnKgw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-win32-arm64@0.8.1': + resolution: {integrity: sha512-W8A1QpstKYhg4e56s13L4ByQprQ1OXY3QFoNPIh6+4v/DEgqfhJ9ApU++e7zHQCQrhYjyQC/uWtxUcRqpyL/7Q==} + cpu: [arm64] + os: [win32] + + '@yuku-codegen/binding-win32-x64@0.8.1': + resolution: {integrity: sha512-zH7g7pLjfl3uZaNvYzd1PnCerU9fvhS16B7Z0z4Ai0S/oaGI53Y2xh8ylFDvDvmquEP7vay4O/zuLgvZeEsxVA==} + cpu: [x64] + os: [win32] + + '@yuku-parser/binding-darwin-arm64@0.8.1': + resolution: {integrity: sha512-iKQinrhsdxsGJ38msw4KaD7yotWw5N2nqMLeOPD+ttpZQKJc9nGL3VHMtISzCJ9Q09g0N3m91mYRFnIbXfhhig==} + cpu: [arm64] + os: [darwin] + + '@yuku-parser/binding-darwin-x64@0.8.1': + resolution: {integrity: sha512-k7zt2H3sWbYzwTGAK2TT6ma18w1XpwFAWgmBHiaMGHVe63aArrtO+55r6g7chXJ4joB6lb8SxYFyRQ974xfawQ==} + cpu: [x64] + os: [darwin] + + '@yuku-parser/binding-freebsd-x64@0.8.1': + resolution: {integrity: sha512-tQjrlkE/vYEBXZ+pM59FerIVD4Zfs2OpRvN3e8oo12pTcAbwTTxteZKWaedfUsZr+gKO4tLLAaFaFIwa5fQJFw==} + cpu: [x64] + os: [freebsd] + + '@yuku-parser/binding-linux-arm-gnu@0.8.1': + resolution: {integrity: sha512-LZQgyer5d5i1f5oot+x6EhANXPD4FeCgz2CiSiWFSFBeG1U2m5jz65LD8gZwzy4CP4QVxQFyX9BazVRskTbwuQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm-musl@0.8.1': + resolution: {integrity: sha512-69G1N4bPkygrus/ahtFxVdv9lzfvS65fuyMdDknn5BZU/gfFGBJyHeJ8UGgLPTyR3YptlJMuZhEkdgp6Qhfz2g==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-arm64-gnu@0.8.1': + resolution: {integrity: sha512-XL5+JpKCl1ghQTnZezy41NzmPdk4rZ6s0Z8qvGsgNPJ6yrSD7Ne8iz20SZZd/nW720qHiJy/ux62rwzZ+/clMg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm64-musl@0.8.1': + resolution: {integrity: sha512-Rj290yfrWlrV4/+HrAuoeJXMHdoNubJWKdhg1QDa/mvqa8MUYFd4IHuVYjP/WdQzEgOAWGrLC48vcnge6U3jbg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-x64-gnu@0.8.1': + resolution: {integrity: sha512-d3IDkhAxiUTVuGI6BKIMKUyLe7ZxMY5yLqyvvTqJD1WPnE0eJTsLeRFRBv7/HXsnAzDs0IgvoG5bjvUkHZ5kSg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-x64-musl@0.8.1': + resolution: {integrity: sha512-Oyp+2gGYKK/AvN2ZoauRn1LGPdkjME5/TE/l4a9mT0JdNeiRTvno42383bt04MWnAZcYS4Hv7YsiFOXUBeLdpA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-win32-arm64@0.8.1': + resolution: {integrity: sha512-H77lYgICWRZycGmLaojzg0aSs3Z3v8SZWkcEjl1Ql3s+Km2SyiQcZ7RGq++Wh5nJxpiDJGAzYDC74XqMNm2ZHg==} + cpu: [arm64] + os: [win32] + + '@yuku-parser/binding-win32-x64@0.8.1': + resolution: {integrity: sha512-ABniFzqYYn64NUEgmHTb6JxEzAlpmPu4cpMFjMBlR6cJXqL+vd7K1ae/yrt+NhUduHkJWJqG4lbHbV2yp9el3w==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.8.1': + resolution: {integrity: sha512-HcGEV3kOn9evBTm2ARYOFNKAI7sY9twQozCVd9EVdlsBlnKi0a2iv8xXxYVHFCBQpX3SvvPXBhk8lcK6NUTdNg==} ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -700,8 +1054,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + ansis@4.3.1: + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} argparse@1.0.10: @@ -718,17 +1072,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-kit@3.0.0-beta.1: - resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} - engines: {node: '>=20.19.0'} - better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - birpc@4.0.0: - resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -741,15 +1088,18 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} @@ -759,28 +1109,33 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dts-resolver@2.1.3: - resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} - engines: {node: '>=20.19.0'} + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} peerDependencies: oxc-resolver: '>=11.0.0' peerDependenciesMeta: oxc-resolver: optional: true - empathic@2.0.0: - resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -792,8 +1147,8 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} extendable-error@0.1.7: @@ -841,8 +1196,9 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - get-tsconfig@4.13.6: - resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + get-tsconfig@5.0.0-beta.5: + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -855,24 +1211,24 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - hookable@6.0.1: - resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} - human-id@4.1.3: - resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} + human-id@4.2.0: + resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} hasBin: true - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-without-cache@0.2.5: - resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} - engines: {node: '>=20.19.0'} + import-without-cache@0.4.0: + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -897,17 +1253,12 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + js-yaml@3.15.1: + resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==} hasBin: true - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true jsonfile@4.0.0: @@ -935,13 +1286,14 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} @@ -987,30 +1339,30 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - playwright-core@1.58.2: - resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} - engines: {node: '>=18'} + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} hasBin: true - playwright@1.58.2: - resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} - engines: {node: '>=18'} + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} hasBin: true - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.25: + resolution: {integrity: sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==} engines: {node: ^10 || ^12 || >=14} prettier@2.8.8: @@ -1042,20 +1394,20 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown-plugin-dts@0.22.5: - resolution: {integrity: sha512-M/HXfM4cboo+jONx9Z0X+CUf3B5tCi7ni+kR5fUW50Fp9AlZk0oVLesibGWgCXDKFp5lpgQ9yhKoImUFjl3VZw==} - engines: {node: '>=20.19.0'} + rolldown-plugin-dts@0.27.14: + resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} + engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: - '@ts-macro/tsc': ^0.3.6 - '@typescript/native-preview': '>=7.0.0-dev.20250601.1' - rolldown: ^1.0.0-rc.3 - typescript: ^5.0.0 || ^6.0.0-beta - vue-tsc: ~3.2.0 + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: - '@ts-macro/tsc': - optional: true '@typescript/native-preview': optional: true + '@volar/typescript': + optional: true typescript: optional: true vue-tsc: @@ -1066,8 +1418,13 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + rolldown@1.2.1: + resolution: {integrity: sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.62.3: + resolution: {integrity: sha512-Gu0c0iH9FzgX1L1t7ByIbbS3Vmdz+6KHm/EsqmmC71gUQ82yvZRkTK6XzrFObSka91WUVdynqp6nsfilzr5k6Q==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1077,8 +1434,8 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -1114,8 +1471,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -1132,16 +1489,16 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: @@ -1152,18 +1509,20 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - tsdown@0.21.2: - resolution: {integrity: sha512-pP8eAcd1XAWjl5gjosuJs0BAuVoheUe3V8VDHx31QK7YOgXjcCMsBSyFWO3CMh/CSUkjRUzR96JtGH3WJFTExQ==} - engines: {node: '>=20.19.0'} + tsdown@0.22.14: + resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} + engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 - '@tsdown/css': 0.21.2 - '@tsdown/exe': 0.21.2 + '@tsdown/css': 0.22.14 + '@tsdown/exe': 0.22.14 '@vitejs/devtools': '*' - publint: ^0.3.0 - typescript: ^5.0.0 + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 unplugin-unused: ^0.5.0 + unrun: '*' peerDependenciesMeta: '@arethetypeswrong/core': optional: true @@ -1175,16 +1534,20 @@ packages: optional: true publint: optional: true + tsx: + optional: true typescript: optional: true unplugin-unused: optional: true + unrun: + optional: true tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.21.0: - resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + tsx@4.23.1: + resolution: {integrity: sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -1196,8 +1559,8 @@ packages: unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} @@ -1213,8 +1576,12 @@ packages: synckit: optional: true - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + verkit@0.3.1: + resolution: {integrity: sha512-w2Eo8LSIIoW7qxNBzT7/17k+bh8plXo7G3dHjEIDqPlnluhzaxr9JX8F28VSYEtDvc1/a3WBDih6xNUZseebXg==} + engines: {node: '>=18.12.0'} + + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1253,20 +1620,23 @@ packages: yaml: optional: true - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' + vite: 7.3.5 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -1280,6 +1650,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -1302,31 +1676,18 @@ packages: engines: {node: '>= 14'} hasBin: true -snapshots: - - '@babel/generator@8.0.0-rc.2': - dependencies: - '@babel/parser': 8.0.0-rc.2 - '@babel/types': 8.0.0-rc.2 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@types/jsesc': 2.5.1 - jsesc: 3.1.0 + yuku-ast@0.8.1: + resolution: {integrity: sha512-+k1E2f08y0k1+vpdD1KUCzDh2JXvwirMa8YR2Jr7VCS4zAo3eT5A/HbJCqaVGd6idaPY0gwLM9C4seEY4h10Hw==} - '@babel/helper-string-parser@8.0.0-rc.2': {} + yuku-codegen@0.8.1: + resolution: {integrity: sha512-/4Sbg9K9HpCe3PidmMbs9TzJ62gq3KmQY279TB0EGKdMoM8LfcmIg4E9wS2J/ylbFuQaWcYqf5dBKr8zubdlKA==} - '@babel/helper-validator-identifier@8.0.0-rc.2': {} + yuku-parser@0.8.1: + resolution: {integrity: sha512-YvUz2jdFMIq0QjN8LmI32ox+RBCn3l8VToAXVQ5QFfLTxSxmGZS6JP80ptePEju+CpeTdINLmUm7Ewodzh1QnQ==} - '@babel/parser@8.0.0-rc.2': - dependencies: - '@babel/types': 8.0.0-rc.2 +snapshots: - '@babel/runtime@7.28.6': {} - - '@babel/types@8.0.0-rc.2': - dependencies: - '@babel/helper-string-parser': 8.0.0-rc.2 - '@babel/helper-validator-identifier': 8.0.0-rc.2 + '@babel/runtime@7.29.7': {} '@biomejs/biome@2.4.5': optionalDependencies: @@ -1363,9 +1724,9 @@ snapshots: '@biomejs/cli-win32-x64@2.4.5': optional: true - '@changesets/apply-release-plan@7.1.0': + '@changesets/apply-release-plan@7.1.1': dependencies: - '@changesets/config': 3.1.3 + '@changesets/config': 3.1.4 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.4 '@changesets/should-skip-package': 0.1.2 @@ -1377,30 +1738,30 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 - '@changesets/assemble-release-plan@6.0.9': + '@changesets/assemble-release-plan@6.0.10': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-dependents-graph': 2.1.4 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 - semver: 7.7.4 + semver: 7.8.5 '@changesets/changelog-git@0.2.1': dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.30.0(@types/node@25.4.0)': + '@changesets/cli@2.31.1(@types/node@25.9.5)': dependencies: - '@changesets/apply-release-plan': 7.1.0 - '@changesets/assemble-release-plan': 6.0.9 + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.3 + '@changesets/config': 3.1.4 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.15 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 @@ -1408,7 +1769,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.4.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.5) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -1417,16 +1778,16 @@ snapshots: package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.7.4 + semver: 7.8.5 spawndamnit: 3.0.1 term-size: 2.2.1 transitivePeerDependencies: - '@types/node' - '@changesets/config@3.1.3': + '@changesets/config@3.1.4': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-dependents-graph': 2.1.4 '@changesets/logger': 0.1.1 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 @@ -1438,17 +1799,17 @@ snapshots: dependencies: extendable-error: 0.1.7 - '@changesets/get-dependents-graph@2.1.3': + '@changesets/get-dependents-graph@2.1.4': dependencies: '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.1 - semver: 7.7.4 + semver: 7.8.5 - '@changesets/get-release-plan@4.0.15': + '@changesets/get-release-plan@4.0.16': dependencies: - '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.3 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 '@changesets/pre': 2.0.2 '@changesets/read': 0.6.7 '@changesets/types': 6.1.0 @@ -1471,7 +1832,7 @@ snapshots: '@changesets/parse@0.4.3': dependencies: '@changesets/types': 6.1.0 - js-yaml: 4.1.1 + js-yaml: 4.3.1 '@changesets/pre@2.0.2': dependencies: @@ -1503,145 +1864,211 @@ snapshots: dependencies: '@changesets/types': 6.1.0 fs-extra: 7.0.1 - human-id: 4.1.3 + human-id: 4.2.0 prettier: 2.8.8 - '@emnapi/core@1.8.1': + '@emnapi/core@2.0.0-alpha.3': dependencies: - '@emnapi/wasi-threads': 1.1.0 + '@emnapi/wasi-threads': 2.0.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@2.0.0-alpha.3': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.1.0': + '@emnapi/wasi-threads@2.0.1': dependencies: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/linux-mips64el@0.28.1': optional: true - '@inquirer/external-editor@1.0.3(@types/node@25.4.0)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 - optionalDependencies: - '@types/node': 25.4.0 + '@esbuild/linux-ppc64@0.27.7': + optional: true - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 + '@esbuild/linux-ppc64@0.28.1': + optional: true - '@jridgewell/resolve-uri@3.1.2': {} + '@esbuild/linux-riscv64@0.27.7': + optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true - '@jridgewell/trace-mapping@0.3.31': + '@esbuild/win32-x64@0.27.7': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@inquirer/external-editor@1.0.3(@types/node@25.9.5)': dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 25.9.5 + + '@jridgewell/sourcemap-codec@1.5.5': {} '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.7 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 globby: 11.1.0 read-yaml-file: 1.1.0 - '@napi-rs/wasm-runtime@1.1.1': + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)': dependencies: - '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.8.1 - '@tybys/wasm-util': 0.10.1 + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@tybys/wasm-util': 0.10.3 optional: true '@nodelib/fs.scandir@2.1.5': @@ -1656,11 +2083,14 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@oxc-project/types@0.115.0': {} + '@oxc-project/types@0.115.0': + optional: true - '@playwright/test@1.58.2': + '@oxc-project/types@0.142.0': {} + + '@playwright/test@1.62.1': dependencies: - playwright: 1.58.2 + playwright: 1.62.1 '@quansync/fs@1.0.0': dependencies: @@ -1669,130 +2099,185 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-rc.9': optional: true + '@rolldown/binding-android-arm64@1.2.1': + optional: true + '@rolldown/binding-darwin-arm64@1.0.0-rc.9': optional: true + '@rolldown/binding-darwin-arm64@1.2.1': + optional: true + '@rolldown/binding-darwin-x64@1.0.0-rc.9': optional: true + '@rolldown/binding-darwin-x64@1.2.1': + optional: true + '@rolldown/binding-freebsd-x64@1.0.0-rc.9': optional: true + '@rolldown/binding-freebsd-x64@1.2.1': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.1': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.1': + optional: true + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-arm64-musl@1.2.1': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.1': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.1': + optional: true + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-x64-gnu@1.2.1': + optional: true + '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': optional: true + '@rolldown/binding-linux-x64-musl@1.2.1': + optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': + '@rolldown/binding-openharmony-arm64@1.2.1': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.9(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)': dependencies: - '@napi-rs/wasm-runtime': 1.1.1 + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@rolldown/binding-wasm32-wasi@1.2.1': + dependencies: + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.1': + optional: true + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': optional: true - '@rolldown/pluginutils@1.0.0-rc.9': {} + '@rolldown/binding-win32-x64-msvc@1.2.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rolldown/pluginutils@1.0.0-rc.9': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/rollup-android-arm-eabi@4.62.3': + optional: true + + '@rollup/rollup-android-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rollup/rollup-darwin-arm64@4.62.3': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rollup/rollup-darwin-x64@4.62.3': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rollup/rollup-freebsd-arm64@4.62.3': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rollup/rollup-freebsd-x64@4.62.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@rollup/rollup-linux-arm64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@rollup/rollup-linux-arm64-musl@4.62.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@rollup/rollup-linux-loong64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@rollup/rollup-linux-loong64-musl@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@rollup/rollup-linux-ppc64-musl@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@rollup/rollup-linux-riscv64-musl@4.62.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@rollup/rollup-linux-s390x-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@rollup/rollup-linux-x64-gnu@4.62.3': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@rollup/rollup-linux-x64-musl@4.62.3': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@rollup/rollup-openbsd-x64@4.62.3': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@rollup/rollup-openharmony-arm64@4.62.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@rollup/rollup-win32-arm64-msvc@4.62.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@rollup/rollup-win32-ia32-msvc@4.62.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@rollup/rollup-win32-x64-gnu@4.62.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@rollup/rollup-win32-x64-msvc@4.62.3': optional: true '@standard-schema/spec@1.1.0': {} - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true @@ -1804,60 +2289,128 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} - - '@types/jsesc@2.5.1': {} + '@types/estree@1.0.9': {} '@types/node@12.20.55': {} - '@types/node@25.4.0': + '@types/node@25.9.5': dependencies: - undici-types: 7.18.2 + undici-types: 7.24.6 - '@vitest/expect@4.0.18': + '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.1 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2))': + '@vitest/mocker@4.1.10(vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2))': dependencies: - '@vitest/spy': 4.0.18 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2) + vite: 7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2) - '@vitest/pretty-format@4.0.18': + '@vitest/pretty-format@4.1.10': dependencies: - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.1 - '@vitest/runner@4.0.18': + '@vitest/runner@4.1.10': dependencies: - '@vitest/utils': 4.0.18 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - '@vitest/snapshot@4.0.18': + '@vitest/snapshot@4.1.10': dependencies: - '@vitest/pretty-format': 4.0.18 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.18': {} + '@vitest/spy@4.1.10': {} - '@vitest/utils@4.0.18': + '@vitest/utils@4.1.10': dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + '@vitest/pretty-format': 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@yuku-codegen/binding-darwin-arm64@0.8.1': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.8.1': + optional: true + + '@yuku-codegen/binding-freebsd-x64@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-arm-gnu@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.8.1': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.8.1': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.8.1': + optional: true + + '@yuku-codegen/binding-win32-x64@0.8.1': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.8.1': + optional: true + + '@yuku-parser/binding-darwin-x64@0.8.1': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.8.1': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.8.1': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.8.1': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.8.1': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.8.1': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.8.1': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.8.1': + optional: true + + '@yuku-parser/binding-win32-arm64@0.8.1': + optional: true + + '@yuku-parser/binding-win32-x64@0.8.1': + optional: true + + '@yuku-toolchain/types@0.8.1': {} ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} - ansis@4.2.0: {} + ansis@4.3.1: {} argparse@1.0.10: dependencies: @@ -1869,18 +2422,10 @@ snapshots: assertion-error@2.0.1: {} - ast-kit@3.0.0-beta.1: - dependencies: - '@babel/parser': 8.0.0-rc.2 - estree-walker: 3.0.3 - pathe: 2.0.3 - better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - birpc@4.0.0: {} - braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -1889,7 +2434,9 @@ snapshots: chai@6.2.2: {} - chardet@2.1.1: {} + chardet@2.2.0: {} + + convert-source-map@2.0.0: {} cross-spawn@7.0.6: dependencies: @@ -1897,7 +2444,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - defu@6.1.4: {} + defu@6.1.7: {} detect-indent@6.1.0: {} @@ -1905,53 +2452,82 @@ snapshots: dependencies: path-type: 4.0.0 - dts-resolver@2.1.3: {} + dts-resolver@3.0.0: {} - empathic@2.0.0: {} + empathic@2.0.1: {} enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - es-module-lexer@1.7.0: {} + es-module-lexer@2.3.1: {} - esbuild@0.27.3: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 esprima@4.0.1: {} estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 - expect-type@1.3.0: {} + expect-type@1.4.0: {} extendable-error@0.1.7: {} @@ -1967,9 +2543,9 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.5 fill-range@7.1.1: dependencies: @@ -1998,7 +2574,7 @@ snapshots: fsevents@2.3.3: optional: true - get-tsconfig@4.13.6: + get-tsconfig@5.0.0-beta.5: dependencies: resolve-pkg-maps: 1.0.0 @@ -2017,17 +2593,17 @@ snapshots: graceful-fs@4.2.11: {} - hookable@6.0.1: {} + hookable@6.1.1: {} - human-id@4.1.3: {} + human-id@4.2.0: {} - iconv-lite@0.7.2: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 ignore@5.3.2: {} - import-without-cache@0.2.5: {} + import-without-cache@0.4.0: {} is-extglob@2.1.1: {} @@ -2045,17 +2621,15 @@ snapshots: isexe@2.0.0: {} - js-yaml@3.14.2: + js-yaml@3.15.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.1: + js-yaml@4.3.1: dependencies: argparse: 2.0.1 - jsesc@3.1.0: {} - jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -2075,13 +2649,13 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 mri@1.2.0: {} - nanoid@3.3.11: {} + nanoid@3.3.16: {} - obug@2.1.1: {} + obug@2.1.4: {} outdent@0.5.0: {} @@ -2115,23 +2689,23 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} - picomatch@4.0.3: {} + picomatch@4.0.5: {} pify@4.0.1: {} - playwright-core@1.58.2: {} + playwright-core@1.62.1: {} - playwright@1.58.2: + playwright@1.62.1: dependencies: - playwright-core: 1.58.2 + playwright-core: 1.62.1 optionalDependencies: fsevents: 2.3.2 - postcss@8.5.6: + postcss@8.5.25: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -2146,7 +2720,7 @@ snapshots: read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 - js-yaml: 3.14.2 + js-yaml: 3.15.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -2156,24 +2730,21 @@ snapshots: reusify@1.1.0: {} - rolldown-plugin-dts@0.22.5(rolldown@1.0.0-rc.9)(typescript@5.9.3): - dependencies: - '@babel/generator': 8.0.0-rc.2 - '@babel/helper-validator-identifier': 8.0.0-rc.2 - '@babel/parser': 8.0.0-rc.2 - '@babel/types': 8.0.0-rc.2 - ast-kit: 3.0.0-beta.1 - birpc: 4.0.0 - dts-resolver: 2.1.3 - get-tsconfig: 4.13.6 - obug: 2.1.1 - rolldown: 1.0.0-rc.9 + rolldown-plugin-dts@0.27.14(rolldown@1.2.1)(typescript@5.9.3): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.4 + rolldown: 1.2.1 + yuku-ast: 0.8.1 + yuku-codegen: 0.8.1 + yuku-parser: 0.8.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - oxc-resolver - rolldown@1.0.0-rc.9: + rolldown@1.0.0-rc.9(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3): dependencies: '@oxc-project/types': 0.115.0 '@rolldown/pluginutils': 1.0.0-rc.9 @@ -2190,39 +2761,64 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9 '@rolldown/binding-linux-x64-musl': 1.0.0-rc.9 '@rolldown/binding-openharmony-arm64': 1.0.0-rc.9 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true - rollup@4.59.0: + rolldown@1.2.1: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.142.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.1 + '@rolldown/binding-darwin-arm64': 1.2.1 + '@rolldown/binding-darwin-x64': 1.2.1 + '@rolldown/binding-freebsd-x64': 1.2.1 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.1 + '@rolldown/binding-linux-arm64-gnu': 1.2.1 + '@rolldown/binding-linux-arm64-musl': 1.2.1 + '@rolldown/binding-linux-ppc64-gnu': 1.2.1 + '@rolldown/binding-linux-s390x-gnu': 1.2.1 + '@rolldown/binding-linux-x64-gnu': 1.2.1 + '@rolldown/binding-linux-x64-musl': 1.2.1 + '@rolldown/binding-openharmony-arm64': 1.2.1 + '@rolldown/binding-wasm32-wasi': 1.2.1 + '@rolldown/binding-win32-arm64-msvc': 1.2.1 + '@rolldown/binding-win32-x64-msvc': 1.2.1 + + rollup@4.62.3: + dependencies: + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 + '@rollup/rollup-android-arm-eabi': 4.62.3 + '@rollup/rollup-android-arm64': 4.62.3 + '@rollup/rollup-darwin-arm64': 4.62.3 + '@rollup/rollup-darwin-x64': 4.62.3 + '@rollup/rollup-freebsd-arm64': 4.62.3 + '@rollup/rollup-freebsd-x64': 4.62.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.3 + '@rollup/rollup-linux-arm-musleabihf': 4.62.3 + '@rollup/rollup-linux-arm64-gnu': 4.62.3 + '@rollup/rollup-linux-arm64-musl': 4.62.3 + '@rollup/rollup-linux-loong64-gnu': 4.62.3 + '@rollup/rollup-linux-loong64-musl': 4.62.3 + '@rollup/rollup-linux-ppc64-gnu': 4.62.3 + '@rollup/rollup-linux-ppc64-musl': 4.62.3 + '@rollup/rollup-linux-riscv64-gnu': 4.62.3 + '@rollup/rollup-linux-riscv64-musl': 4.62.3 + '@rollup/rollup-linux-s390x-gnu': 4.62.3 + '@rollup/rollup-linux-x64-gnu': 4.62.3 + '@rollup/rollup-linux-x64-musl': 4.62.3 + '@rollup/rollup-openbsd-x64': 4.62.3 + '@rollup/rollup-openharmony-arm64': 4.62.3 + '@rollup/rollup-win32-arm64-msvc': 4.62.3 + '@rollup/rollup-win32-ia32-msvc': 4.62.3 + '@rollup/rollup-win32-x64-gnu': 4.62.3 + '@rollup/rollup-win32-x64-msvc': 4.62.3 fsevents: 2.3.3 run-parallel@1.2.0: @@ -2231,7 +2827,7 @@ snapshots: safer-buffer@2.1.2: {} - semver@7.7.4: {} + semver@7.8.5: {} shebang-command@2.0.0: dependencies: @@ -2256,7 +2852,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.10.0: {} + std-env@4.2.0: {} strip-ansi@6.0.1: dependencies: @@ -2268,14 +2864,14 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.0.2: {} + tinyexec@1.2.4: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 - tinyrainbow@3.0.3: {} + tinyrainbow@3.1.1: {} to-regex-range@5.0.1: dependencies: @@ -2283,40 +2879,39 @@ snapshots: tree-kill@1.2.2: {} - tsdown@0.21.2(typescript@5.9.3): + tsdown@0.22.14(tsx@4.23.1)(typescript@5.9.3)(unrun@0.2.32(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3)): dependencies: - ansis: 4.2.0 + ansis: 4.3.1 cac: 7.0.0 - defu: 6.1.4 - empathic: 2.0.0 - hookable: 6.0.1 - import-without-cache: 0.2.5 - obug: 2.1.1 - picomatch: 4.0.3 - rolldown: 1.0.0-rc.9 - rolldown-plugin-dts: 0.22.5(rolldown@1.0.0-rc.9)(typescript@5.9.3) - semver: 7.7.4 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.4 + picomatch: 4.0.5 + rolldown: 1.2.1 + rolldown-plugin-dts: 0.27.14(rolldown@1.2.1)(typescript@5.9.3) + tinyexec: 1.2.4 + tinyglobby: 0.2.17 tree-kill: 1.2.2 unconfig-core: 7.5.0 - unrun: 0.2.32 + verkit: 0.3.1 optionalDependencies: + tsx: 4.23.1 typescript: 5.9.3 + unrun: 0.2.32(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) transitivePeerDependencies: - - '@ts-macro/tsc' - '@typescript/native-preview' + - '@volar/typescript' - oxc-resolver - - synckit - vue-tsc tslib@2.8.1: optional: true - tsx@4.21.0: + tsx@4.23.1: dependencies: - esbuild: 0.27.3 - get-tsconfig: 4.13.6 + esbuild: 0.28.1 optionalDependencies: fsevents: 2.3.3 @@ -2327,64 +2922,60 @@ snapshots: '@quansync/fs': 1.0.0 quansync: 1.0.0 - undici-types@7.18.2: {} + undici-types@7.24.6: {} universalify@0.1.2: {} - unrun@0.2.32: + unrun@0.2.32(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3): dependencies: - rolldown: 1.0.0-rc.9 + rolldown: 1.0.0-rc.9(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true - vite@7.3.1(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2): + verkit@0.3.1: {} + + vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2): dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.59.0 - tinyglobby: 0.2.15 + esbuild: 0.27.7 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.25 + rollup: 4.62.3 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.4.0 + '@types/node': 25.9.5 fsevents: 2.3.3 - tsx: 4.21.0 + tsx: 4.23.1 yaml: 2.4.2 - vitest@4.0.18(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2): - dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 - expect-type: 1.3.0 + vitest@4.1.10(@types/node@25.9.5)(vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2)): + dependencies: + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.4 pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.4.0)(tsx@4.21.0)(yaml@2.4.2) + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 7.3.5(@types/node@25.9.5)(tsx@4.23.1)(yaml@2.4.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.4.0 + '@types/node': 25.9.5 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml which@2.0.2: dependencies: @@ -2397,3 +2988,40 @@ snapshots: yaml@2.4.2: optional: true + + yuku-ast@0.8.1: + dependencies: + '@yuku-toolchain/types': 0.8.1 + + yuku-codegen@0.8.1: + dependencies: + '@yuku-toolchain/types': 0.8.1 + optionalDependencies: + '@yuku-codegen/binding-darwin-arm64': 0.8.1 + '@yuku-codegen/binding-darwin-x64': 0.8.1 + '@yuku-codegen/binding-freebsd-x64': 0.8.1 + '@yuku-codegen/binding-linux-arm-gnu': 0.8.1 + '@yuku-codegen/binding-linux-arm-musl': 0.8.1 + '@yuku-codegen/binding-linux-arm64-gnu': 0.8.1 + '@yuku-codegen/binding-linux-arm64-musl': 0.8.1 + '@yuku-codegen/binding-linux-x64-gnu': 0.8.1 + '@yuku-codegen/binding-linux-x64-musl': 0.8.1 + '@yuku-codegen/binding-win32-arm64': 0.8.1 + '@yuku-codegen/binding-win32-x64': 0.8.1 + + yuku-parser@0.8.1: + dependencies: + '@yuku-toolchain/types': 0.8.1 + yuku-ast: 0.8.1 + optionalDependencies: + '@yuku-parser/binding-darwin-arm64': 0.8.1 + '@yuku-parser/binding-darwin-x64': 0.8.1 + '@yuku-parser/binding-freebsd-x64': 0.8.1 + '@yuku-parser/binding-linux-arm-gnu': 0.8.1 + '@yuku-parser/binding-linux-arm-musl': 0.8.1 + '@yuku-parser/binding-linux-arm64-gnu': 0.8.1 + '@yuku-parser/binding-linux-arm64-musl': 0.8.1 + '@yuku-parser/binding-linux-x64-gnu': 0.8.1 + '@yuku-parser/binding-linux-x64-musl': 0.8.1 + '@yuku-parser/binding-win32-arm64': 0.8.1 + '@yuku-parser/binding-win32-x64': 0.8.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 02eb41a..ae492bd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,5 +2,8 @@ packages: - . - e2e +overrides: + vite: 7.3.5 + ignoredBuiltDependencies: - esbuild diff --git a/src/callbacks.ts b/src/callbacks.ts index ddb8891..468e4cb 100644 --- a/src/callbacks.ts +++ b/src/callbacks.ts @@ -1,22 +1,24 @@ -import type { - FetchInterceptorError, - FetchInterceptorOptions, - RuntimeInterceptorOptions, -} from "./types"; +import type { ResolvedInterceptorOptions } from "./internal-types"; +import type { FetchInterceptorError, FetchInterceptorOptions } from "./types"; -type InterceptorCallbackKind = "matcher" | "onError" | "onIntercept"; +type InterceptionFailureSource = + | "matcher callback" + | "onError callback" + | "onIntercept callback" + | "request observation" + | "response observation"; -export type InterceptionSnapshot = Array<{ +export type InterceptionSnapshot = readonly Readonly<{ onError?: FetchInterceptorOptions["onError"]; onIntercept: FetchInterceptorOptions["onIntercept"]; request: Request; -}>; +}>[]; -function reportCallbackError( - kind: InterceptorCallbackKind, +function reportInterceptionFailure( + source: InterceptionFailureSource, error: unknown, ): void { - const message = `[fetch-interceptor] ${kind} callback failed. The original request result was preserved.`; + const message = `[fetch-interceptor] ${source} failed. The original request result was preserved.`; try { console.error(message, error); @@ -32,7 +34,7 @@ export function matchesRequestSafely( try { return matcher(request); } catch (error) { - reportCallbackError("matcher", error); + reportInterceptionFailure("matcher callback", error); return false; } } @@ -45,10 +47,10 @@ export function runOnInterceptSafely( try { const result = onIntercept(request, response); void Promise.resolve(result).catch((error) => { - reportCallbackError("onIntercept", error); + reportInterceptionFailure("onIntercept callback", error); }); } catch (error) { - reportCallbackError("onIntercept", error); + reportInterceptionFailure("onIntercept callback", error); } } @@ -64,18 +66,18 @@ export function runOnErrorSafely( try { const result = onError(request, error); void Promise.resolve(result).catch((callbackError) => { - reportCallbackError("onError", callbackError); + reportInterceptionFailure("onError callback", callbackError); }); } catch (error) { - reportCallbackError("onError", error); + reportInterceptionFailure("onError callback", error); } } -export function createInterceptionSnapshot( +function createInterceptionSnapshot( request: Request, - interceptors: RuntimeInterceptorOptions[], + interceptors: readonly ResolvedInterceptorOptions[], ): InterceptionSnapshot { - const snapshot: InterceptionSnapshot = []; + const snapshot: InterceptionSnapshot[number][] = []; for (const interceptor of interceptors) { const interceptedRequest = request.clone(); @@ -92,18 +94,52 @@ export function createInterceptionSnapshot( return snapshot; } +export function createInterceptionSnapshotSafely( + createRequest: () => Request, + interceptors: readonly ResolvedInterceptorOptions[], +): InterceptionSnapshot { + if (interceptors.length === 0) { + return []; + } + + try { + return createInterceptionSnapshot(createRequest(), interceptors); + } catch (error) { + reportInterceptionFailure("request observation", error); + return []; + } +} + export function runInterceptionSnapshotOnSuccess( snapshot: InterceptionSnapshot, createResponse: () => Response, ): void { - let sharedResponse: Response | null = null; + if (snapshot.length === 0) { + return; + } + + let sharedResponse: Response; + + try { + sharedResponse = createResponse(); + } catch (error) { + reportInterceptionFailure("response observation", error); + return; + } for (const interceptor of snapshot) { - sharedResponse ??= createResponse(); + let response: Response; + + try { + response = sharedResponse.clone(); + } catch (error) { + reportInterceptionFailure("response observation", error); + return; + } runOnInterceptSafely( interceptor.request, - sharedResponse.clone(), + response, interceptor.onIntercept, ); } diff --git a/src/fetch.test.ts b/src/fetch.test.ts index b28e882..a0ef49c 100644 --- a/src/fetch.test.ts +++ b/src/fetch.test.ts @@ -1,10 +1,6 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; -import { createFetchInterceptorHandler, createFetchRequest } from "./fetch"; - -afterEach(() => { - vi.restoreAllMocks(); -}); +import { createFetchRequest } from "./fetch"; describe("createFetchRequest", () => { it("clones Request inputs", async () => { @@ -21,7 +17,7 @@ describe("createFetchRequest", () => { expect(await clonedRequest.text()).toBe("hello"); }); - it("applies init overrides to Request inputs without consuming the original body", async () => { + it("applies init overrides without consuming the original body", async () => { const originalRequest = new Request("https://example.com/base", { body: "original-body", headers: { @@ -58,166 +54,3 @@ describe("createFetchRequest", () => { expect(await request.json()).toEqual({ hello: "world" }); }); }); - -describe("createFetchInterceptorHandler", () => { - it("matches against the effective Request when fetch receives Request and init", async () => { - const onIntercept = vi.fn(); - const originalFetch = vi.fn(async () => new Response("ok")); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: (request) => request.method === "POST", - onIntercept, - }); - const baseRequest = new Request("https://example.com/base", { - headers: { - "x-original": "1", - }, - method: "PUT", - }); - - await interceptedFetch(baseRequest, { - body: "override-body", - headers: { - "x-override": "1", - }, - method: "POST", - }); - - expect(originalFetch).toHaveBeenCalledOnce(); - expect(onIntercept).toHaveBeenCalledOnce(); - - const [request] = onIntercept.mock.calls[0]; - - expect(request.method).toBe("POST"); - expect(request.headers.get("x-original")).toBeNull(); - expect(request.headers.get("x-override")).toBe("1"); - expect(await request.text()).toBe("override-body"); - expect(baseRequest.bodyUsed).toBe(false); - }); - - it("intercepts matching requests with a cloned response", async () => { - const onIntercept = vi.fn(); - const originalFetch = vi.fn(async () => { - return new Response(JSON.stringify({ ok: true }), { - headers: { "content-type": "application/json" }, - }); - }); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: (request) => request.url === "https://example.com/target", - onIntercept, - }); - - const response = await interceptedFetch("https://example.com/target"); - - expect(originalFetch).toHaveBeenCalledOnce(); - expect(onIntercept).toHaveBeenCalledOnce(); - - const [request, interceptedResponse] = onIntercept.mock.calls[0]; - - expect(request.url).toBe("https://example.com/target"); - expect(interceptedResponse).not.toBe(response); - expect(await interceptedResponse.json()).toEqual({ ok: true }); - expect(await response.json()).toEqual({ ok: true }); - }); - - it("skips onIntercept when matcher returns false", async () => { - const onIntercept = vi.fn(); - const originalFetch = vi.fn(async () => new Response("ok")); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: () => false, - onIntercept, - }); - - await interceptedFetch("https://example.com/ignored"); - - expect(originalFetch).toHaveBeenCalledOnce(); - expect(onIntercept).not.toHaveBeenCalled(); - }); - - it("preserves successful fetch responses when matcher throws", async () => { - const consoleError = vi - .spyOn(console, "error") - .mockImplementation(() => undefined); - const originalFetch = vi.fn(async () => new Response("ok")); - const onIntercept = vi.fn(); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: () => { - throw new Error("matcher failed"); - }, - onIntercept, - }); - - const response = await interceptedFetch("https://example.com/target"); - - expect(await response.text()).toBe("ok"); - expect(onIntercept).not.toHaveBeenCalled(); - expect(consoleError).toHaveBeenCalledOnce(); - }); - - it("preserves successful fetch responses when onIntercept throws", async () => { - const consoleError = vi - .spyOn(console, "error") - .mockImplementation(() => undefined); - const originalFetch = vi.fn(async () => new Response("ok")); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: () => true, - onIntercept: () => { - throw new Error("onIntercept failed"); - }, - }); - - const response = await interceptedFetch("https://example.com/target"); - - expect(await response.text()).toBe("ok"); - expect(consoleError).toHaveBeenCalledOnce(); - }); - - it("reports rejected async onIntercept callbacks without rejecting fetch", async () => { - const consoleError = vi - .spyOn(console, "error") - .mockImplementation(() => undefined); - const originalFetch = vi.fn(async () => new Response("ok")); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: () => true, - onIntercept: async () => { - throw new Error("async onIntercept failed"); - }, - }); - - const response = await interceptedFetch("https://example.com/target"); - - await Promise.resolve(); - - expect(await response.text()).toBe("ok"); - expect(consoleError).toHaveBeenCalledOnce(); - }); - - it("reports rejected fetch requests through onError and rethrows the original error", async () => { - const networkError = new TypeError("network failed"); - const onError = vi.fn(); - const onIntercept = vi.fn(); - const originalFetch = vi.fn(async () => { - throw networkError; - }); - const interceptedFetch = createFetchInterceptorHandler(originalFetch, { - matcher: () => true, - onIntercept, - onError, - }); - - await expect(interceptedFetch("https://example.com/target")).rejects.toBe( - networkError, - ); - - expect(onIntercept).not.toHaveBeenCalled(); - expect(onError).toHaveBeenCalledOnce(); - - const [request, error] = onError.mock.calls[0]; - - expect(request.url).toBe("https://example.com/target"); - expect(error).toMatchObject({ - cause: networkError, - reason: "error", - transport: "fetch", - }); - }); -}); diff --git a/src/fetch.ts b/src/fetch.ts index dcbe0b5..901d7d0 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,12 +1,15 @@ import { - createInterceptionSnapshot, - matchesRequestSafely, + createInterceptionSnapshotSafely, runInterceptionSnapshotOnError, runInterceptionSnapshotOnSuccess, - runOnErrorSafely, - runOnInterceptSafely, } from "./callbacks"; -import type { FetchInterceptorError, RuntimeInterceptorOptions } from "./types"; +import type { ResolvedInterceptorOptions } from "./internal-types"; +import type { FetchInterceptorError } from "./types"; + +type NormalizedFetchError = Extract< + FetchInterceptorError, + { transport: "fetch" } +>; function isAbortError(error: unknown): boolean { if (typeof DOMException !== "undefined" && error instanceof DOMException) { @@ -20,7 +23,7 @@ function isAbortError(error: unknown): boolean { return "name" in error && error.name === "AbortError"; } -function createFetchInterceptorError(error: unknown): FetchInterceptorError { +function createFetchInterceptorError(error: unknown): NormalizedFetchError { return { cause: error, reason: isAbortError(error) ? "abort" : "error", @@ -40,60 +43,23 @@ export function createFetchRequest( return new Request(input, init); } -export function createFetchInterceptorHandler( - originalFetch: typeof globalThis.fetch, - options: RuntimeInterceptorOptions, -): typeof globalThis.fetch { - return async function interceptedFetch( - ...args: Parameters - ) { - const request = createFetchRequest(...args); - const shouldIntercept = matchesRequestSafely(request, options.matcher); - - try { - const response = await originalFetch(...args); - - if (shouldIntercept) { - runOnInterceptSafely(request, response.clone(), options.onIntercept); - } - - return response; - } catch (error) { - if (shouldIntercept) { - runOnErrorSafely( - request, - createFetchInterceptorError(error), - options.onError, - ); - } - - throw error; - } - }; -} - -function createSharedFetchInterceptorHandler( +function createFetchHandlerForActiveInterceptors( originalFetch: typeof globalThis.fetch, - getActiveInterceptors: () => RuntimeInterceptorOptions[], + getActiveInterceptors: () => readonly ResolvedInterceptorOptions[], ): typeof globalThis.fetch { return async function interceptedFetch( ...args: Parameters ) { const activeInterceptors = getActiveInterceptors(); - const interceptionSnapshot = - activeInterceptors.length === 0 - ? [] - : createInterceptionSnapshot( - createFetchRequest(...args), - activeInterceptors, - ); + const interceptionSnapshot = createInterceptionSnapshotSafely( + () => createFetchRequest(...args), + activeInterceptors, + ); + + let response: Response; try { - const response = await originalFetch(...args); - runInterceptionSnapshotOnSuccess(interceptionSnapshot, () => - response.clone(), - ); - return response; + response = await originalFetch(...args); } catch (error) { runInterceptionSnapshotOnError( interceptionSnapshot, @@ -101,18 +67,18 @@ function createSharedFetchInterceptorHandler( ); throw error; } + + runInterceptionSnapshotOnSuccess(interceptionSnapshot, () => response); + return response; }; } -/** - * Intercepts globalThis.fetch and returns a restore function. - */ export function interceptFetch( - getActiveInterceptors: () => RuntimeInterceptorOptions[], + getActiveInterceptors: () => readonly ResolvedInterceptorOptions[], ): () => void { const originalFetch = globalThis.fetch; - globalThis.fetch = createSharedFetchInterceptorHandler( + globalThis.fetch = createFetchHandlerForActiveInterceptors( originalFetch, getActiveInterceptors, ); diff --git a/src/index.test.ts b/src/index.test.ts index 1451326..4831c45 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -28,8 +28,8 @@ type MockXhrResponse = { function createDeferred(): Deferred { let resolve!: (value: T) => void; - const promise = new Promise((res) => { - resolve = res; + const promise = new Promise((resolvePromise) => { + resolve = resolvePromise; }); return { promise, resolve }; @@ -185,6 +185,48 @@ describe("createFetchInterceptor", () => { interceptor.stop(); }); + it("matches the effective Request when fetch receives Request and init", async () => { + const onIntercept = vi.fn(); + const originalFetch = vi.fn(async () => new Response("ok")); + const baseRequest = new Request("https://example.com/base", { + headers: { + "x-original": "1", + }, + method: "PUT", + }); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ + matcher: (request) => request.method === "POST", + onIntercept, + }); + + interceptor.start(); + + await fetch(baseRequest, { + body: "override-body", + headers: { + "x-override": "1", + }, + method: "POST", + }); + + expect(originalFetch).toHaveBeenCalledOnce(); + expect(onIntercept).toHaveBeenCalledOnce(); + + const [request] = onIntercept.mock.calls[0]; + + expect(request.method).toBe("POST"); + expect(request.headers.get("x-original")).toBeNull(); + expect(request.headers.get("x-override")).toBe("1"); + expect(await request.text()).toBe("override-body"); + expect(baseRequest.bodyUsed).toBe(false); + + interceptor.stop(); + }); + it("skips fetch callbacks when the matcher returns false", async () => { const onIntercept = vi.fn(); const originalFetch = vi.fn(async () => new Response("ok")); @@ -206,6 +248,144 @@ describe("createFetchInterceptor", () => { interceptor.stop(); }); + it("preserves fetch responses when the matcher throws", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + const originalFetch = vi.fn(async () => new Response("ok")); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ + matcher: () => { + throw new Error("matcher failed"); + }, + onIntercept, + }); + + interceptor.start(); + + const response = await fetch("https://example.com/matcher-error"); + + expect(await response.text()).toBe("ok"); + expect(onIntercept).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + + interceptor.stop(); + }); + + it.each([ + [ + "throws", + () => { + throw new Error("onIntercept failed"); + }, + ], + [ + "rejects", + async () => { + throw new Error("async onIntercept failed"); + }, + ], + ] as const)("preserves fetch responses when onIntercept %s", async (_label, onIntercept) => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const originalFetch = vi.fn(async () => new Response("ok")); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ onIntercept }); + + interceptor.start(); + + const response = await fetch("https://example.com/callback-error"); + await Promise.resolve(); + + expect(await response.text()).toBe("ok"); + expect(consoleError).toHaveBeenCalledOnce(); + + interceptor.stop(); + }); + + it("preserves a successful fetch response when observation cannot clone it", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + const onError = vi.fn(); + const consumedResponse = new Response("already consumed"); + + await consumedResponse.text(); + + const originalFetch = vi.fn(async () => consumedResponse); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ + onIntercept, + onError, + }); + + interceptor.start(); + + try { + const response = await fetch("https://example.com/consumed-response"); + + expect(response).toBe(consumedResponse); + expect(onIntercept).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + } finally { + interceptor.stop(); + } + }); + + it("preserves fetch results when request observation cannot clone the input", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + const onError = vi.fn(); + const consumedRequest = new Request( + "https://example.com/consumed-request", + { + body: "already consumed", + method: "POST", + }, + ); + + await consumedRequest.text(); + + const originalFetch = vi.fn(async () => new Response("ok")); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ + onIntercept, + onError, + }); + + interceptor.start(); + + try { + const response = await fetch(consumedRequest); + + expect(await response.text()).toBe("ok"); + expect(originalFetch).toHaveBeenCalledWith(consumedRequest); + expect(onIntercept).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + } finally { + interceptor.stop(); + } + }); + it("defaults matcher to true for XMLHttpRequest calls", async () => { const intercepted = createDeferred<{ request: Request; @@ -248,6 +428,86 @@ describe("createFetchInterceptor", () => { interceptor.stop(); }); + it("skips XMLHttpRequest callbacks when the matcher returns false", () => { + const onIntercept = vi.fn(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ body: "ignored" }); + + const interceptor = createFetchInterceptor({ + matcher: () => false, + onIntercept, + }); + + interceptor.start(); + + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://example.com/ignored-xhr"); + xhr.send(); + + expect(onIntercept).not.toHaveBeenCalled(); + + interceptor.stop(); + }); + + it("preserves XMLHttpRequest results when the matcher throws", () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ body: "ok" }); + + const interceptor = createFetchInterceptor({ + matcher: () => { + throw new Error("matcher failed"); + }, + onIntercept, + }); + + interceptor.start(); + + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://example.com/xhr-matcher-error"); + expect(() => xhr.send()).not.toThrow(); + + expect(onIntercept).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + + interceptor.stop(); + }); + + it("preserves XMLHttpRequest results when request observation fails", () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + const onError = vi.fn(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ body: "ok" }); + + const interceptor = createFetchInterceptor({ + onIntercept, + onError, + }); + + interceptor.start(); + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "http://[invalid-url"); + expect(() => xhr.send()).not.toThrow(); + + expect(onIntercept).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + } finally { + interceptor.stop(); + } + }); + it("reports rejected fetch requests through onError and preserves the original rejection", async () => { const networkError = new TypeError("network failed"); const onIntercept = vi.fn(); @@ -286,6 +546,37 @@ describe("createFetchInterceptor", () => { interceptor.stop(); }); + it("preserves fetch rejection when onError rejects", async () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const networkError = new TypeError("network failed"); + const originalFetch = vi.fn(async () => { + throw networkError; + }); + + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const interceptor = createFetchInterceptor({ + onIntercept: vi.fn(), + onError: async () => { + throw new Error("onError failed"); + }, + }); + + interceptor.start(); + + await expect(fetch("https://example.com/on-error-failure")).rejects.toBe( + networkError, + ); + await Promise.resolve(); + + expect(consoleError).toHaveBeenCalledOnce(); + + interceptor.stop(); + }); + it.each([ ["error"], ["abort"], @@ -387,6 +678,103 @@ describe("createFetchInterceptor", () => { interceptor.stop(); }); + it.each([ + 204, 205, 304, + ])("normalizes XMLHttpRequest status %s without a response body", async (status) => { + const intercepted = createDeferred(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ + body: "", + status, + statusText: "No Body", + }); + + const interceptor = createFetchInterceptor({ + onIntercept: (_request, response) => intercepted.resolve(response), + }); + + interceptor.start(); + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", `https://example.com/status-${status}`); + xhr.send(); + + const response = await intercepted.promise; + + expect(response.status).toBe(status); + expect(await response.text()).toBe(""); + } finally { + interceptor.stop(); + } + }); + + it("represents XMLHttpRequest status 0 as a standard error Response", async () => { + const intercepted = createDeferred(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ + body: "local response", + status: 0, + statusText: "", + }); + + const interceptor = createFetchInterceptor({ + onIntercept: (_request, response) => intercepted.resolve(response), + }); + + interceptor.start(); + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "file:///status-zero"); + xhr.send(); + + const response = await intercepted.promise; + + expect(response.status).toBe(0); + expect(response.type).toBe("error"); + expect(await response.text()).toBe(""); + } finally { + interceptor.stop(); + } + }); + + it("preserves XMLHttpRequest results when response normalization fails", () => { + const consoleError = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + const onIntercept = vi.fn(); + const onError = vi.fn(); + + useMockXmlHttpRequest(); + MockXMLHttpRequest.enqueueResponse({ + body: "unsupported status", + status: 199, + statusText: "Unsupported", + }); + + const interceptor = createFetchInterceptor({ + onIntercept, + onError, + }); + + interceptor.start(); + + try { + const xhr = new XMLHttpRequest(); + xhr.open("GET", "https://example.com/unsupported-status"); + expect(() => xhr.send()).not.toThrow(); + + expect(onIntercept).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + expect(consoleError).toHaveBeenCalledOnce(); + } finally { + interceptor.stop(); + } + }); + it("starts only once and restores the original globals on stop", async () => { const onIntercept = vi.fn(); const originalFetch = vi.fn(async () => new Response("ok")); @@ -430,6 +818,92 @@ describe("createFetchInterceptor", () => { expect(onIntercept).toHaveBeenCalledTimes(1); }); + it.each([ + "open", + "setRequestHeader", + "send", + ] as const)("rolls back installation when XMLHttpRequest.%s cannot be patched", (nonPatchableMethod) => { + class NonPatchableXMLHttpRequest extends MockXMLHttpRequest {} + + Object.defineProperty( + NonPatchableXMLHttpRequest.prototype, + nonPatchableMethod, + { + configurable: true, + value: MockXMLHttpRequest.prototype[nonPatchableMethod], + writable: false, + }, + ); + + const originalFetch = vi.fn(async () => new Response("ok")); + globalThis.fetch = originalFetch as unknown as typeof fetch; + globalThis.XMLHttpRequest = + NonPatchableXMLHttpRequest as unknown as typeof XMLHttpRequest; + + const interceptor = createFetchInterceptor({ + onIntercept: vi.fn(), + }); + + try { + expect(() => interceptor.start()).toThrow(); + expect(globalThis.fetch).toBe(originalFetch); + expect(NonPatchableXMLHttpRequest.prototype.open).toBe( + MockXMLHttpRequest.prototype.open, + ); + expect(NonPatchableXMLHttpRequest.prototype.setRequestHeader).toBe( + MockXMLHttpRequest.prototype.setRequestHeader, + ); + expect(NonPatchableXMLHttpRequest.prototype.send).toBe( + MockXMLHttpRequest.prototype.send, + ); + expect(() => interceptor.start()).toThrow(); + } finally { + interceptor.stop(); + } + }); + + it("restores XMLHttpRequest even when restoring fetch fails", () => { + const originalFetch = vi.fn(async () => new Response("ok")); + globalThis.fetch = originalFetch as unknown as typeof fetch; + useMockXmlHttpRequest(); + + const originalXhrOpen = XMLHttpRequest.prototype.open; + const originalXhrSend = XMLHttpRequest.prototype.send; + const originalXhrSetRequestHeader = + XMLHttpRequest.prototype.setRequestHeader; + const interceptor = createFetchInterceptor({ + onIntercept: vi.fn(), + }); + + interceptor.start(); + + Object.defineProperty(globalThis, "fetch", { + configurable: true, + value: globalThis.fetch, + writable: false, + }); + + try { + expect(() => interceptor.stop()).toThrow(); + expect(XMLHttpRequest.prototype.open).toBe(originalXhrOpen); + expect(XMLHttpRequest.prototype.send).toBe(originalXhrSend); + expect(XMLHttpRequest.prototype.setRequestHeader).toBe( + originalXhrSetRequestHeader, + ); + } finally { + Object.defineProperty(globalThis, "fetch", { + configurable: true, + value: originalFetch, + writable: true, + }); + XMLHttpRequest.prototype.open = originalXhrOpen; + XMLHttpRequest.prototype.send = originalXhrSend; + XMLHttpRequest.prototype.setRequestHeader = originalXhrSetRequestHeader; + } + + expect(() => interceptor.stop()).not.toThrow(); + }); + it("keeps remaining interceptors active until the last one stops", async () => { const originalFetch = vi.fn(async () => new Response("ok")); const seenByA: string[] = []; diff --git a/src/index.ts b/src/index.ts index ad7d65c..6c10954 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,37 +1,45 @@ +import type { ResolvedInterceptorOptions } from "./internal-types"; import { registerInterceptor, unregisterInterceptor } from "./runtime"; import type { FetchInterceptor, FetchInterceptorOptions } from "./types"; -export * from "./types"; +export type { + FetchInterceptor, + FetchInterceptorError, + FetchInterceptorErrorReason, + FetchInterceptorOptions, +} from "./types"; const matchAllRequests = () => true; /** - * Creates a FetchInterceptor instance that intercepts network traffic. - * @param options Matcher and callback options for interception. - * @returns A control interface with start and stop methods. + * Creates an inactive interceptor. Omitting `matcher` observes every request; + * call `start()` to register it and `stop()` to restore it. */ export function createFetchInterceptor( options: FetchInterceptorOptions, ): FetchInterceptor { let isRunning = false; const interceptorId = Symbol("fetch-interceptor"); - const resolvedOptions = { + const resolvedOptions: ResolvedInterceptorOptions = { ...options, matcher: options.matcher ?? matchAllRequests, }; const start = () => { if (isRunning) return; - isRunning = true; registerInterceptor(interceptorId, resolvedOptions); + isRunning = true; }; const stop = () => { if (!isRunning) return; - isRunning = false; - unregisterInterceptor(interceptorId); + try { + unregisterInterceptor(interceptorId); + } finally { + isRunning = false; + } }; return { start, stop }; diff --git a/src/internal-types.ts b/src/internal-types.ts new file mode 100644 index 0000000..aae8a1e --- /dev/null +++ b/src/internal-types.ts @@ -0,0 +1,7 @@ +import type { FetchInterceptorOptions } from "./types"; + +export type ResolvedInterceptorOptions = Readonly< + Omit & { + matcher: NonNullable; + } +>; diff --git a/src/runtime.ts b/src/runtime.ts index 5b20f50..34b002d 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -1,32 +1,69 @@ import { interceptFetch } from "./fetch"; -import type { RuntimeInterceptorOptions } from "./types"; +import type { ResolvedInterceptorOptions } from "./internal-types"; import { interceptXhr } from "./xhr"; -const activeInterceptors = new Map(); +const activeInterceptors = new Map(); let restoreFetch: (() => void) | null = null; let restoreXhr: (() => void) | null = null; -function getActiveInterceptors(): RuntimeInterceptorOptions[] { +function getActiveInterceptors(): ResolvedInterceptorOptions[] { return Array.from(activeInterceptors.values()); } +function throwCollectedErrors( + message: string, + errors: readonly unknown[], +): void { + if (errors.length === 0) { + return; + } + + if (errors.length === 1) { + throw errors[0]; + } + + throw new AggregateError(errors, message); +} + export function registerInterceptor( interceptorId: symbol, - options: RuntimeInterceptorOptions, + options: ResolvedInterceptorOptions, ): void { if (activeInterceptors.has(interceptorId)) { return; } - activeInterceptors.set(interceptorId, options); - - if (activeInterceptors.size !== 1) { + if (activeInterceptors.size > 0) { + activeInterceptors.set(interceptorId, options); return; } - restoreFetch = interceptFetch(getActiveInterceptors); - restoreXhr = interceptXhr(getActiveInterceptors); + let nextRestoreFetch: (() => void) | null = null; + + try { + nextRestoreFetch = interceptFetch(getActiveInterceptors); + const nextRestoreXhr = interceptXhr(getActiveInterceptors); + + activeInterceptors.set(interceptorId, options); + restoreFetch = nextRestoreFetch; + restoreXhr = nextRestoreXhr; + } catch (error) { + const rollbackErrors: unknown[] = [error]; + + if (nextRestoreFetch) { + try { + nextRestoreFetch(); + } catch (rollbackError) { + rollbackErrors.push(rollbackError); + } + } + + throwCollectedErrors( + "Failed to install interception and restore the original globals.", + rollbackErrors, + ); + } } export function unregisterInterceptor(interceptorId: symbol): void { @@ -38,13 +75,26 @@ export function unregisterInterceptor(interceptorId: symbol): void { return; } - if (restoreFetch) { - restoreFetch(); - restoreFetch = null; - } + const restoreFunctions = [restoreFetch, restoreXhr]; + const restorationErrors: unknown[] = []; + + restoreFetch = null; + restoreXhr = null; - if (restoreXhr) { - restoreXhr(); - restoreXhr = null; + for (const restore of restoreFunctions) { + if (!restore) { + continue; + } + + try { + restore(); + } catch (error) { + restorationErrors.push(error); + } } + + throwCollectedErrors( + "Failed to restore one or more original globals.", + restorationErrors, + ); } diff --git a/src/types.ts b/src/types.ts index 60becb9..265be64 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,56 +1,52 @@ -export type FetchInterceptorErrorReason = "abort" | "error" | "timeout"; - -export interface FetchInterceptorError { +type FetchTransportError = Readonly<{ cause: unknown; - reason: FetchInterceptorErrorReason; - transport: "fetch" | "xhr"; -} + reason: "abort" | "error"; + transport: "fetch"; +}>; -/** - * Initialization options for FetchInterceptor. - */ +type XhrTransportError = Readonly<{ + cause: ProgressEvent; + reason: "abort" | "error" | "timeout"; + transport: "xhr"; +}>; + +/** A failure produced by the underlying transport before a response exists. */ +export type FetchInterceptorError = FetchTransportError | XhrTransportError; + +export type FetchInterceptorErrorReason = FetchInterceptorError["reason"]; + +/** Consumer callbacks and filtering for an interceptor instance. */ export interface FetchInterceptorOptions { /** - * Determines whether a request should be intercepted. - * @param req A standard Request object. - * @returns Returns true when the request should be intercepted. + * Selects requests to observe. A thrown exception is reported and treated as + * a non-match without changing the network result. */ - matcher?: (req: Request) => boolean; + matcher?: (request: Request) => boolean; /** - * Callback invoked when an intercepted request completes successfully. - * @param req A standard Request object. - * @param res A standard Response object, or a cloned equivalent. + * Observes a matched request and an independent response clone. Exceptions + * and rejected promises are reported without changing the network result. */ - onIntercept: (req: Request, res: Response) => void | Promise; + onIntercept: (request: Request, response: Response) => void | Promise; /** - * Callback invoked when an intercepted request fails before producing a response. - * @param req A standard Request object. - * @param error Normalized fetch/XHR failure details. + * Observes an underlying transport failure before a response exists. Callback + * failures are reported without replacing the original transport failure. */ onError?: ( - req: Request, + request: Request, error: FetchInterceptorError, ) => void | Promise; } /** - * Runtime options after default values have been resolved. - */ -export type RuntimeInterceptorOptions = Omit< - FetchInterceptorOptions, - "matcher" -> & { - matcher: NonNullable; -}; - -/** - * FetchInterceptor instance exposed to consumers. + * Lifecycle control for one registration. Both operations are idempotent. + * Failed installation leaves the interceptor stopped; failed restoration still + * transitions it to stopped after attempting every installed transport. */ export interface FetchInterceptor { - /** Starts interception. */ + /** Installs both transport adapters or rolls back and throws. */ start: () => void; - /** Stops interception and restores the original global objects. */ + /** Unregisters this instance and attempts every required restoration. */ stop: () => void; } diff --git a/src/types.type-test.ts b/src/types.type-test.ts new file mode 100644 index 0000000..fd4d3d0 --- /dev/null +++ b/src/types.type-test.ts @@ -0,0 +1,23 @@ +import type { FetchInterceptorError } from "./types"; + +export function assertErrorNarrowing(error: FetchInterceptorError): void { + if (error.transport === "fetch") { + const reason: "abort" | "error" = error.reason; + void reason; + return; + } + + const reason: "abort" | "error" | "timeout" = error.reason; + const cause: ProgressEvent = error.cause; + void reason; + void cause; +} + +// @ts-expect-error Fetch does not produce the XHR-only timeout reason. +const invalidFetchTimeout: FetchInterceptorError = { + cause: new Error("timeout"), + reason: "timeout", + transport: "fetch", +}; + +void invalidFetchTimeout; diff --git a/src/xhr.test.ts b/src/xhr-normalization.test.ts similarity index 50% rename from src/xhr.test.ts rename to src/xhr-normalization.test.ts index 43b844c..4017c96 100644 --- a/src/xhr.test.ts +++ b/src/xhr-normalization.test.ts @@ -1,15 +1,10 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; import { - createXhrLoadHandler, createXhrRequest, createXhrResponse, parseXhrResponseHeaders, -} from "./xhr"; - -afterEach(() => { - vi.restoreAllMocks(); -}); +} from "./xhr-normalization"; describe("createXhrRequest", () => { it("keeps request bodies for non-GET methods, including empty strings", async () => { @@ -104,117 +99,35 @@ describe("createXhrResponse", () => { expect(await response.text()).toBe(""); }); -}); -describe("createXhrLoadHandler", () => { - it("invokes onIntercept when matcher returns true", async () => { - const onIntercept = vi.fn(); - const request = new Request("https://example.com/xhr", { - method: "POST", + it.each([ + 204, 205, 304, + ])("omits the body for HTTP status %s", async (status) => { + const response = createXhrResponse({ + getAllResponseHeaders: () => "content-type: text/plain", + response: "", + responseType: "", + responseText: "", + status, + statusText: "No Body", }); - const handleLoad = createXhrLoadHandler( - { - getAllResponseHeaders: () => "content-type: text/plain", - response: "ok", - responseType: "", - responseText: "fallback", - status: 200, - statusText: "OK", - }, - request, - { - matcher: () => true, - onIntercept, - }, - ); - - handleLoad(); - - expect(onIntercept).toHaveBeenCalledOnce(); - - const [interceptedRequest, response] = onIntercept.mock.calls[0]; - - expect(interceptedRequest).toBe(request); - expect(await response.text()).toBe("ok"); - }); - - it("skips onIntercept when matcher returns false", () => { - const onIntercept = vi.fn(); - const handleLoad = createXhrLoadHandler( - { - getAllResponseHeaders: () => "", - response: null, - responseType: "", - responseText: "", - status: 204, - statusText: "No Content", - }, - new Request("https://example.com/xhr"), - { - matcher: () => false, - onIntercept, - }, - ); - handleLoad(); - - expect(onIntercept).not.toHaveBeenCalled(); - }); - - it("reports matcher errors without throwing", () => { - const consoleError = vi - .spyOn(console, "error") - .mockImplementation(() => undefined); - const onIntercept = vi.fn(); - const handleLoad = createXhrLoadHandler( - { - getAllResponseHeaders: () => "", - response: "ok", - responseType: "", - responseText: "ok", - status: 200, - statusText: "OK", - }, - new Request("https://example.com/xhr"), - { - matcher: () => { - throw new Error("matcher failed"); - }, - onIntercept, - }, - ); - - expect(() => handleLoad()).not.toThrow(); - expect(onIntercept).not.toHaveBeenCalled(); - expect(consoleError).toHaveBeenCalledOnce(); + expect(response.status).toBe(status); + expect(await response.text()).toBe(""); }); - it("reports rejected async onIntercept callbacks without throwing", async () => { - const consoleError = vi - .spyOn(console, "error") - .mockImplementation(() => undefined); - const handleLoad = createXhrLoadHandler( - { - getAllResponseHeaders: () => "", - response: "ok", - responseType: "", - responseText: "ok", - status: 200, - statusText: "OK", - }, - new Request("https://example.com/xhr"), - { - matcher: () => true, - onIntercept: async () => { - throw new Error("async onIntercept failed"); - }, - }, - ); - - expect(() => handleLoad()).not.toThrow(); - - await Promise.resolve(); + it("represents XHR status 0 with Response.error", async () => { + const response = createXhrResponse({ + getAllResponseHeaders: () => "content-type: text/plain", + response: "local response", + responseType: "", + responseText: "local response", + status: 0, + statusText: "", + }); - expect(consoleError).toHaveBeenCalledOnce(); + expect(response.status).toBe(0); + expect(response.type).toBe("error"); + expect(await response.text()).toBe(""); }); }); diff --git a/src/xhr-normalization.ts b/src/xhr-normalization.ts new file mode 100644 index 0000000..d48869f --- /dev/null +++ b/src/xhr-normalization.ts @@ -0,0 +1,138 @@ +export type XhrRequestMetadata = Readonly<{ + method: string; + url: string; + headers: Headers; +}>; + +type XhrResponseSource = Pick< + XMLHttpRequest, + | "getAllResponseHeaders" + | "response" + | "responseType" + | "responseText" + | "status" + | "statusText" +>; + +const nullBodyStatusCodes: ReadonlySet = new Set([204, 205, 304]); + +function toRequestBody(body: Document | XMLHttpRequestBodyInit): BodyInit { + if (typeof Document !== "undefined" && body instanceof Document) { + return new XMLSerializer().serializeToString(body); + } + + return body as BodyInit; +} + +function cloneArrayBufferView( + bufferView: ArrayBufferView, +): Uint8Array { + const copy = new Uint8Array(new ArrayBuffer(bufferView.byteLength)); + copy.set( + new Uint8Array( + bufferView.buffer, + bufferView.byteOffset, + bufferView.byteLength, + ), + ); + return copy; +} + +export function createXhrRequest( + metadata: XhrRequestMetadata, + body?: Document | XMLHttpRequestBodyInit | null, +): Request { + const requestInit: RequestInit = { + method: metadata.method, + headers: metadata.headers, + }; + + if (body != null && metadata.method !== "GET" && metadata.method !== "HEAD") { + requestInit.body = toRequestBody(body); + } + + return new Request(metadata.url, requestInit); +} + +export function parseXhrResponseHeaders(rawHeaders: string): Headers { + const headers = new Headers(); + const trimmedHeaders = rawHeaders.trim(); + + if (!trimmedHeaders) { + return headers; + } + + for (const line of trimmedHeaders.split(/[\r\n]+/)) { + const separatorIndex = line.indexOf(":"); + + if (separatorIndex === -1) { + continue; + } + + const name = line.slice(0, separatorIndex).trim(); + const value = line.slice(separatorIndex + 1).trim(); + + if (name) { + headers.append(name, value); + } + } + + return headers; +} + +function readXhrResponseText(xhr: XhrResponseSource): string | null { + try { + return xhr.responseText; + } catch { + return null; + } +} + +function toResponseBody(xhr: XhrResponseSource): BodyInit | null { + const { response, responseType } = xhr; + + if (response == null) { + return readXhrResponseText(xhr); + } + + if (typeof response === "string") { + return response; + } + + if (typeof Blob !== "undefined" && response instanceof Blob) { + return response; + } + + if (response instanceof ArrayBuffer || ArrayBuffer.isView(response)) { + return response instanceof ArrayBuffer + ? response + : cloneArrayBufferView(response); + } + + if (typeof Document !== "undefined" && response instanceof Document) { + return new XMLSerializer().serializeToString(response); + } + + if (responseType === "json") { + return JSON.stringify(response); + } + + return readXhrResponseText(xhr); +} + +export function createXhrResponse(xhr: XhrResponseSource): Response { + if (xhr.status === 0) { + // Response.error() is the only standard Response representation with status 0. + return Response.error(); + } + + const responseBody = nullBodyStatusCodes.has(xhr.status) + ? null + : toResponseBody(xhr); + + return new Response(responseBody, { + status: xhr.status, + statusText: xhr.statusText, + headers: parseXhrResponseHeaders(xhr.getAllResponseHeaders()), + }); +} diff --git a/src/xhr.ts b/src/xhr.ts index a0af18d..1a42905 100644 --- a/src/xhr.ts +++ b/src/xhr.ts @@ -1,22 +1,15 @@ import { - createInterceptionSnapshot, - matchesRequestSafely, + createInterceptionSnapshotSafely, runInterceptionSnapshotOnError, runInterceptionSnapshotOnSuccess, - runOnInterceptSafely, } from "./callbacks"; -import type { - FetchInterceptorError, - FetchInterceptorErrorReason, - RuntimeInterceptorOptions, -} from "./types"; - -// Tracks request metadata associated with each XHR instance. -export interface XhrInterceptorData { - method: string; - url: string; - headers: Headers; -} +import type { ResolvedInterceptorOptions } from "./internal-types"; +import type { FetchInterceptorError } from "./types"; +import { + createXhrRequest, + createXhrResponse, + type XhrRequestMetadata, +} from "./xhr-normalization"; type XhrOpenShort = (method: string, url: string | URL) => void; type XhrOpenLong = ( @@ -27,154 +20,19 @@ type XhrOpenLong = ( password?: string | null, ) => void; -type XhrResponseSource = Pick< - XMLHttpRequest, - | "getAllResponseHeaders" - | "response" - | "responseType" - | "responseText" - | "status" - | "statusText" ->; - -type XhrFailureReason = FetchInterceptorErrorReason; +type NormalizedXhrError = Extract; +type XhrFailureReason = NormalizedXhrError["reason"]; type XhrTerminalEventType = XhrFailureReason | "load"; type XhrTerminalEvent = ProgressEvent; type XhrTerminalHandler = (event: XhrTerminalEvent) => void; -type XhrTerminalHandlers = Record; - -function toRequestBody(body: Document | XMLHttpRequestBodyInit): BodyInit { - if (typeof Document !== "undefined" && body instanceof Document) { - return new XMLSerializer().serializeToString(body); - } - - return body as BodyInit; -} - -function cloneArrayBufferView( - bufferView: ArrayBufferView, -): Uint8Array { - const copy = new Uint8Array(new ArrayBuffer(bufferView.byteLength)); - copy.set( - new Uint8Array( - bufferView.buffer, - bufferView.byteOffset, - bufferView.byteLength, - ), - ); - return copy; -} - -export function createXhrRequest( - data: XhrInterceptorData, - body?: Document | XMLHttpRequestBodyInit | null, -): Request { - const requestInit: RequestInit = { - method: data.method, - headers: data.headers, - }; - - if (body != null && data.method !== "GET" && data.method !== "HEAD") { - requestInit.body = toRequestBody(body); - } - - return new Request(data.url, requestInit); -} - -export function parseXhrResponseHeaders(rawHeaders: string): Headers { - const headers = new Headers(); - const trimmedHeaders = rawHeaders.trim(); - - if (!trimmedHeaders) { - return headers; - } - - for (const line of trimmedHeaders.split(/[\r\n]+/)) { - const separatorIndex = line.indexOf(":"); - - if (separatorIndex === -1) { - continue; - } - - const name = line.slice(0, separatorIndex).trim(); - const value = line.slice(separatorIndex + 1).trim(); - - if (name) { - headers.append(name, value); - } - } - - return headers; -} - -function readXhrResponseText(xhr: XhrResponseSource): string | null { - try { - return xhr.responseText; - } catch { - return null; - } -} - -function toResponseBody(xhr: XhrResponseSource): BodyInit | null { - const { response, responseType } = xhr; - - if (response == null) { - return readXhrResponseText(xhr); - } - - if (typeof response === "string") { - return response; - } - - if (typeof Blob !== "undefined" && response instanceof Blob) { - return response; - } - - if (response instanceof ArrayBuffer || ArrayBuffer.isView(response)) { - return response instanceof ArrayBuffer - ? response - : cloneArrayBufferView(response); - } - - if (typeof Document !== "undefined" && response instanceof Document) { - return new XMLSerializer().serializeToString(response); - } - - if (responseType === "json") { - return JSON.stringify(response); - } - - return readXhrResponseText(xhr); -} - -export function createXhrResponse(xhr: XhrResponseSource): Response { - const responseBody = toResponseBody(xhr); - - return new Response(responseBody, { - status: xhr.status, - statusText: xhr.statusText, - headers: parseXhrResponseHeaders(xhr.getAllResponseHeaders()), - }); -} - -export function createXhrLoadHandler( - xhr: XhrResponseSource, - request: Request, - options: RuntimeInterceptorOptions, -): () => void { - return () => { - if (!matchesRequestSafely(request, options.matcher)) { - return; - } - - runOnInterceptSafely(request, createXhrResponse(xhr), options.onIntercept); - }; -} +type XhrTerminalHandlers = Readonly< + Record +>; function createXhrInterceptorError( cause: XhrTerminalEvent, reason: XhrFailureReason, -): FetchInterceptorError { +): NormalizedXhrError { return { cause, reason, @@ -198,7 +56,7 @@ function detachXhrTerminalHandlers( function attachXhrTerminalHandlers( xhr: XMLHttpRequest, - interceptionSnapshot: ReturnType, + interceptionSnapshot: ReturnType, xhrTerminalHandlersMap: WeakMap, ): void { detachXhrTerminalHandlers(xhr, xhrTerminalHandlersMap.get(xhr)); @@ -245,38 +103,37 @@ function attachXhrTerminalHandlers( xhr.addEventListener("timeout", handlers.timeout); } -/** - * Intercepts XMLHttpRequest and returns a restore function. - */ export function interceptXhr( - getActiveInterceptors: () => RuntimeInterceptorOptions[], + getActiveInterceptors: () => readonly ResolvedInterceptorOptions[], ): () => void { if (typeof XMLHttpRequest === "undefined") { return () => {}; } - const originalXhrOpen = XMLHttpRequest.prototype.open; + const xhrPrototype = XMLHttpRequest.prototype; + const originalXhrOpen = xhrPrototype.open; const originalXhrOpenShort = originalXhrOpen as XhrOpenShort; const originalXhrOpenLong = originalXhrOpen as XhrOpenLong; - const originalXhrSend = XMLHttpRequest.prototype.send; - const originalXhrSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; + const originalXhrSend = xhrPrototype.send; + const originalXhrSetRequestHeader = xhrPrototype.setRequestHeader; // Store metadata in a WeakMap keyed by each XHR instance. // Entries disappear with the instance, so this does not leak memory. - const xhrDataMap = new WeakMap(); + const xhrMetadataMap = new WeakMap(); const xhrTerminalHandlersMap = new WeakMap< XMLHttpRequest, XhrTerminalHandlers >(); - XMLHttpRequest.prototype.open = function ( + const interceptedXhrOpen = function ( + this: XMLHttpRequest, method: string, url: string | URL, async?: boolean, username?: string | null, password?: string | null, ) { - xhrDataMap.set(this, { + xhrMetadataMap.set(this, { method: method.toUpperCase(), url: url.toString(), headers: new Headers(), @@ -300,35 +157,36 @@ export function interceptXhr( ); }; - XMLHttpRequest.prototype.setRequestHeader = function ( + const interceptedXhrSetRequestHeader = function ( + this: XMLHttpRequest, ...args: Parameters ) { const [name, value] = args; - const data = xhrDataMap.get(this); + const metadata = xhrMetadataMap.get(this); - if (data) { - data.headers.append(name, value); + if (metadata) { + metadata.headers.append(name, value); } return originalXhrSetRequestHeader.apply(this, args); }; - XMLHttpRequest.prototype.send = function ( + const interceptedXhrSend = function ( + this: XMLHttpRequest, ...args: Parameters ) { const [body] = args; - const data = xhrDataMap.get(this); + const metadata = xhrMetadataMap.get(this); detachXhrTerminalHandlers(this, xhrTerminalHandlersMap.get(this)); xhrTerminalHandlersMap.delete(this); - if (data) { + if (metadata) { const activeInterceptors = getActiveInterceptors(); if (activeInterceptors.length > 0) { - const request = createXhrRequest(data, body); - const interceptionSnapshot = createInterceptionSnapshot( - request, + const interceptionSnapshot = createInterceptionSnapshotSafely( + () => createXhrRequest(metadata, body), activeInterceptors, ); @@ -345,9 +203,70 @@ export function interceptXhr( return originalXhrSend.apply(this, args); }; + let installedOpen = false; + let installedSend = false; + let installedSetRequestHeader = false; + + const restoreInstalledMethods = () => { + const restorationErrors: unknown[] = []; + + if (installedSend) { + try { + xhrPrototype.send = originalXhrSend; + } catch (error) { + restorationErrors.push(error); + } + } + + if (installedSetRequestHeader) { + try { + xhrPrototype.setRequestHeader = originalXhrSetRequestHeader; + } catch (error) { + restorationErrors.push(error); + } + } + + if (installedOpen) { + try { + xhrPrototype.open = originalXhrOpen; + } catch (error) { + restorationErrors.push(error); + } + } + + if (restorationErrors.length === 1) { + throw restorationErrors[0]; + } + + if (restorationErrors.length > 1) { + throw new AggregateError( + restorationErrors, + "Failed to restore one or more XMLHttpRequest methods.", + ); + } + }; + + try { + xhrPrototype.open = interceptedXhrOpen; + installedOpen = true; + xhrPrototype.setRequestHeader = interceptedXhrSetRequestHeader; + installedSetRequestHeader = true; + xhrPrototype.send = interceptedXhrSend; + installedSend = true; + } catch (error) { + try { + restoreInstalledMethods(); + } catch (rollbackError) { + throw new AggregateError( + [error, rollbackError], + "Failed to install and roll back XMLHttpRequest interception.", + ); + } + + throw error; + } + return function restoreXhr() { - XMLHttpRequest.prototype.open = originalXhrOpen; - XMLHttpRequest.prototype.send = originalXhrSend; - XMLHttpRequest.prototype.setRequestHeader = originalXhrSetRequestHeader; + restoreInstalledMethods(); }; } diff --git a/tsconfig.json b/tsconfig.json index 1651e3d..c6d5b67 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "declaration": true, "declarationMap": true, "sourceMap": true, - "removeComments": true, + "removeComments": false, "isolatedModules": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true,