diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..7f69505
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,30 @@
+## ✨ 작업 배경 [#](#background)
+
+
+
+
+
+
+
+
+✔️ Related issues #
+
+
+
+
+
+
+## 💻 작업 내용 [#](#work_detail)
+
+
+
+
+
+
+## 🏃 기능 동작 시연 [#](#prove)
+
+
+
+## 💬 코멘트 [#](#comments)
+
+
diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
index 9377de1..bd4aae6 100644
Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a2795e2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 dhlab-fe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/package.json b/package.json
index 9e20e10..458bc81 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,8 @@
"types": "dist/index.d.ts",
"files": [
"dist",
- "README.md"
+ "README.md",
+ "LICENSE"
],
"scripts": {
"build": "rollup -c",
@@ -37,8 +38,7 @@
"peerDependencies": {
"@tanstack/react-query": "^4.0.0 || ^5.0.0",
"react": "^17.0.0 || ^18.0.0",
- "react-dom": "^17.0.0 || ^18.0.0",
- "react-error-boundary": "^4.0.0"
+ "react-dom": "^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"axios": {
@@ -49,6 +49,7 @@
}
},
"dependencies": {
+ "react-error-boundary": "^4.0.0",
"ts-pattern": "^5.0.0"
},
"devDependencies": {
@@ -63,12 +64,12 @@
"@testing-library/user-event": "^14.6.1",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
+ "@vitest/coverage-v8": "^3.2.4",
"axios": "^1.10.0",
"jsdom": "^26.1.0",
"ky": "^1.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-error-boundary": "^4.0.0",
"rollup": "^4.0.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"tslib": "^2.8.1",
diff --git a/src/components/__tests__/api-error-boundary.integration.test.tsx b/src/components/__tests__/api-error-boundary.integration.test.tsx
index e9d83bb..bada966 100644
--- a/src/components/__tests__/api-error-boundary.integration.test.tsx
+++ b/src/components/__tests__/api-error-boundary.integration.test.tsx
@@ -12,7 +12,7 @@ import {
it,
vi,
} from "vitest";
-import type { PartialErrorConfig } from "../../types/api-error";
+import type { TPartialErrorConfig } from "../../types/api-error";
import { ApiErrorBoundary } from "../api-error-boundary";
// Mock HTTPError (실제 HTTPError를 사용하되 Response 객체 생성)
@@ -196,7 +196,7 @@ describe("ApiErrorBoundary Integration Tests", () => {
describe("커스텀 설정", () => {
it("커스텀 fallback 컴포넌트를 사용해야 함", async () => {
- const customConfig: PartialErrorConfig = {
+ const customConfig: TPartialErrorConfig = {
404: {
type: "custom",
fallback: (
@@ -218,7 +218,7 @@ describe("ApiErrorBoundary Integration Tests", () => {
});
it("커스텀 fallback 함수를 사용해야 함", async () => {
- const customConfig: PartialErrorConfig = {
+ const customConfig: TPartialErrorConfig = {
404: {
type: "custom",
fallback: (error, resetErrorBoundary) => (
@@ -307,7 +307,7 @@ describe("ApiErrorBoundary Integration Tests", () => {
describe("onError 콜백", () => {
it("에러 발생 시 onError 콜백이 호출되어야 함", async () => {
const onErrorSpy = vi.fn();
- const customConfig: PartialErrorConfig = {
+ const customConfig: TPartialErrorConfig = {
404: {
type: "default",
message: "오류가 발생했습니다.",
diff --git a/src/components/__tests__/error-boundary.test.tsx b/src/components/__tests__/error-boundary.test.tsx
new file mode 100644
index 0000000..d400ae5
--- /dev/null
+++ b/src/components/__tests__/error-boundary.test.tsx
@@ -0,0 +1,359 @@
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import {
+ afterAll,
+ afterEach,
+ beforeAll,
+ describe,
+ expect,
+ it,
+ vi,
+} from "vitest";
+import { ErrorBoundary } from "../error-boundary";
+
+// Mock 컴포넌트들
+const NormalComponent = () =>
정상적인 컴포넌트
;
+
+const ErrorComponent = ({ message = "Test error" }: { message?: string }) => {
+ throw new Error(message);
+};
+
+const NetworkErrorComponent = () => {
+ const error = new Error("Network connection failed");
+ error.name = "NetworkError";
+ throw error;
+};
+
+const CustomErrorComponent = ({ code = 404 }: { code?: number }) => {
+ const error = new Error(`Error ${code}`);
+ error.name = code.toString();
+ throw error;
+};
+
+// Custom Fallback Component
+const CustomFallbackComponent = ({ error, resetErrorBoundary }: any) => (
+
+
Custom Error: {error.message}
+
+
+);
+
+describe("ErrorBoundary", () => {
+ const originalConsoleError = console.error;
+
+ beforeAll(() => {
+ console.error = vi.fn();
+ });
+
+ afterAll(() => {
+ console.error = originalConsoleError;
+ });
+
+ afterEach(() => {
+ vi.clearAllMocks();
+ });
+
+ describe("정상 동작", () => {
+ it("에러가 없을 때 자식 컴포넌트가 정상적으로 렌더링되어야 함", () => {
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+
+ expect(screen.getByText("정상적인 컴포넌트")).toBeInTheDocument();
+ });
+ });
+
+ describe("에러 처리", () => {
+ it("기본 에러를 처리하고 fallbackRender를 사용해야 함", async () => {
+ render(
+ (
+
+
에러 발생: {error.message}
+
+
+ )}
+ >
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByText("에러 발생: Test error occurred"),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: /다시 시도/i }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it("FallbackComponent를 사용해야 함", async () => {
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByText("Custom Error: Custom component error"),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("button", { name: /Reset Error/i }),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it("fallback ReactNode를 사용해야 함", async () => {
+ render(
+ Simple fallback}>
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText("Simple fallback")).toBeInTheDocument();
+ });
+ });
+
+ it("onError 콜백이 호출되어야 함", async () => {
+ const onError = vi.fn();
+
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: "Callback test error",
+ }),
+ expect.any(Object),
+ );
+ });
+ });
+ });
+
+ describe("ignoreError 기능", () => {
+ it("문자열 패턴으로 에러를 무시해야 함", () => {
+ expect(() => {
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+ }).toThrow("Network connection failed");
+ });
+
+ it("숫자 패턴으로 에러를 무시해야 함", () => {
+ expect(() => {
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+ }).toThrow("Error 404");
+ });
+
+ it("함수 패턴으로 에러를 무시해야 함", () => {
+ const ignoreFunction = vi.fn().mockReturnValue(true);
+
+ expect(() => {
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+ }).toThrow("Function test error");
+ });
+
+ it("무시하지 않는 에러는 정상적으로 처리해야 함", async () => {
+ const onError = vi.fn();
+
+ render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText("Error: Different error")).toBeInTheDocument();
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: "Different error",
+ }),
+ expect.any(Object),
+ );
+ });
+ });
+
+ it("여러 무시 조건을 함께 사용할 수 있어야 함", () => {
+ expect(() => {
+ render(
+ error.message.includes("timeout"),
+ ]}
+ fallbackRender={({ error }) => Error: {error.message}
}
+ >
+
+ ,
+ );
+ }).toThrow("Request timeout occurred");
+ });
+ });
+
+ describe("에러 경계 리셋", () => {
+ it("resetErrorBoundary 호출 시 에러 상태가 초기화되어야 함", async () => {
+ const user = userEvent.setup();
+ let hasError = true;
+
+ const ConditionalErrorComponent = () => {
+ if (hasError) {
+ throw new Error("Conditional error");
+ }
+ return No error now
;
+ };
+
+ render(
+ (
+
+
Error: {error.message}
+
+
+ )}
+ >
+
+ ,
+ );
+
+ // 에러 상태 확인
+ await waitFor(() => {
+ expect(
+ screen.getByText("Error: Conditional error"),
+ ).toBeInTheDocument();
+ });
+
+ // 리셋 버튼 클릭
+ await user.click(screen.getByRole("button", { name: /Reset/i }));
+
+ // 에러가 해결되어 정상 컴포넌트가 렌더링되어야 함
+ await waitFor(() => {
+ expect(screen.getByText("No error now")).toBeInTheDocument();
+ });
+ });
+
+ it("resetKeys 변경 시 에러 상태가 초기화되어야 함", async () => {
+ let hasError = true;
+ const ConditionalErrorComponent = () => {
+ if (hasError) {
+ throw new Error("Reset keys test error");
+ }
+ return Reset successful
;
+ };
+
+ const { rerender } = render(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+
+ // 에러 상태 확인
+ await waitFor(() => {
+ expect(
+ screen.getByText("Error: Reset keys test error"),
+ ).toBeInTheDocument();
+ });
+
+ // 에러 조건 해제 후 resetKeys 변경
+ hasError = false;
+ rerender(
+ Error: {error.message}
}
+ >
+
+ ,
+ );
+
+ // 에러가 해결되어 정상 컴포넌트가 렌더링되어야 함
+ await waitFor(() => {
+ expect(screen.getByText("Reset successful")).toBeInTheDocument();
+ });
+ });
+
+ it("onReset 콜백이 호출되어야 함", async () => {
+ const user = userEvent.setup();
+ const onReset = vi.fn();
+
+ render(
+ (
+
+
Error: {error.message}
+
+
+ )}
+ >
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(
+ screen.getByText("Error: Reset callback test"),
+ ).toBeInTheDocument();
+ });
+
+ await user.click(screen.getByRole("button", { name: /Reset/i }));
+
+ await waitFor(() => {
+ expect(onReset).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/src/components/api-error-boundary.tsx b/src/components/api-error-boundary.tsx
index 31cf224..5f59632 100644
--- a/src/components/api-error-boundary.tsx
+++ b/src/components/api-error-boundary.tsx
@@ -10,10 +10,10 @@ import {
type FallbackProps,
} from "react-error-boundary";
import { P, match } from "ts-pattern";
-import type { PartialErrorConfig } from "../types/api-error";
+import type { TPartialErrorConfig } from "../types/api-error";
import { getErrorConfig, isApiError } from "../utils/api-error";
-type IgnoreErrorType =
+export type TIgnoreErrorType =
| string
| number
| ((error: HTTPError | AxiosError) => boolean);
@@ -28,13 +28,13 @@ const DefaultButton = (props: React.ComponentProps<"button">) => (
);
-type ApiErrorBoundaryProps = {
+export type TApiErrorBoundaryProps = {
children: React.ReactNode;
FallbackContainer?: React.ComponentType<{ children: React.ReactNode }>;
Button?: React.ComponentType>;
- overrideConfig?: PartialErrorConfig;
+ overrideConfig?: TPartialErrorConfig;
resetKeys?: ErrorBoundaryProps["resetKeys"];
- ignoreError?: IgnoreErrorType[];
+ ignoreError?: TIgnoreErrorType[];
};
export function ApiErrorBoundary({
@@ -46,7 +46,7 @@ export function ApiErrorBoundary({
overrideConfig,
resetKeys,
ignoreError = [],
-}: ApiErrorBoundaryProps) {
+}: TApiErrorBoundaryProps) {
const handleError: ErrorBoundaryProps["onError"] = (error, info) => {
if (!isApiError(error)) {
throw error;
@@ -125,7 +125,7 @@ export function ApiErrorBoundary({
type ApiErrorFallbackProps = {
error: HTTPError | AxiosError;
resetErrorBoundary: FallbackProps["resetErrorBoundary"];
- overrideConfig?: PartialErrorConfig;
+ overrideConfig?: TPartialErrorConfig;
Button: React.ComponentType>;
};
diff --git a/src/components/error-boundary.tsx b/src/components/error-boundary.tsx
new file mode 100644
index 0000000..7dc46fb
--- /dev/null
+++ b/src/components/error-boundary.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import type React from "react";
+import type { ErrorInfo } from "react";
+import { ErrorBoundary as ReactErrorBoundary } from "react-error-boundary";
+
+export type TErrorBoundaryProps = React.ComponentProps<
+ typeof ReactErrorBoundary
+> & {
+ ignoreError?: Array boolean)>;
+};
+
+export function ErrorBoundary({
+ children,
+ ignoreError = [],
+ onError,
+ ...rest
+}: TErrorBoundaryProps) {
+ const handleError = (error: Error, errorInfo: ErrorInfo) => {
+ const shouldIgnore = ignoreError.some((ignore) => {
+ if (typeof ignore === "string") {
+ return error.message.includes(ignore);
+ }
+ if (typeof ignore === "number") {
+ return error.name === ignore.toString();
+ }
+ if (typeof ignore === "function") {
+ return ignore(error);
+ }
+ return false;
+ });
+
+ if (shouldIgnore) {
+ throw error;
+ }
+
+ onError?.(error, errorInfo);
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/index.ts b/src/index.ts
index faf139e..7648a98 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,10 +1,20 @@
// Components
-export { ApiErrorBoundary } from "./components/api-error-boundary";
+export {
+ ApiErrorBoundary,
+ type TApiErrorBoundaryProps,
+} from "./components/api-error-boundary";
+export {
+ ErrorBoundary,
+ type TErrorBoundaryProps,
+} from "./components/error-boundary";
+
+export * from "react-error-boundary";
+export { ErrorBoundary as BaseErrorBoundary } from "react-error-boundary";
// Types
export type {
- PartialErrorConfig,
- ErrorConfigElementType,
+ TPartialErrorConfig as PartialErrorConfig,
+ TErrorConfigElementType as ErrorConfigElementType,
} from "./types/api-error";
// Utils
diff --git a/src/types/api-error.ts b/src/types/api-error.ts
index fbfb110..0b7a058 100644
--- a/src/types/api-error.ts
+++ b/src/types/api-error.ts
@@ -3,28 +3,28 @@ import type { HTTPError } from "ky";
import type { ErrorInfo } from "react";
import type { HTTP_ERROR_CONFIG } from "../constants/http-error-message";
-export type ErrorConfigElementType =
+export type TErrorConfigElementType =
(typeof HTTP_ERROR_CONFIG)[keyof typeof HTTP_ERROR_CONFIG];
-type OnErrorCallback = (
+type TOnErrorCallback = (
error: HTTPError | AxiosError,
info: ErrorInfo,
statusCode: number,
) => void;
-type DefaultErrorConfigType = {
+type TDefaultErrorConfigType = {
type: "default";
- onError?: OnErrorCallback;
+ onError?: TOnErrorCallback;
} & Partial<
- Omit & {
- action: Partial;
+ Omit & {
+ action: Partial;
message: string;
}
>;
-type CustomErrorConfigType = {
+type TCustomErrorConfigType = {
type: "custom";
- onError?: OnErrorCallback;
+ onError?: TOnErrorCallback;
fallback:
| React.ReactNode
| ((
@@ -33,8 +33,8 @@ type CustomErrorConfigType = {
) => React.ReactNode);
};
-export type PartialErrorConfig = {
+export type TPartialErrorConfig = {
[K in keyof typeof HTTP_ERROR_CONFIG]?:
- | ({ type: "default" } & DefaultErrorConfigType)
- | ({ type: "custom" } & CustomErrorConfigType);
+ | ({ type: "default" } & TDefaultErrorConfigType)
+ | ({ type: "custom" } & TCustomErrorConfigType);
};
diff --git a/src/utils/api-error.ts b/src/utils/api-error.ts
index 2d7fb32..9c403f1 100644
--- a/src/utils/api-error.ts
+++ b/src/utils/api-error.ts
@@ -1,11 +1,11 @@
import type { AxiosError } from "axios";
import type { HTTPError } from "ky";
import { HTTP_ERROR_CONFIG } from "../constants/http-error-message";
-import type { PartialErrorConfig } from "../types/api-error";
+import type { TPartialErrorConfig } from "../types/api-error";
export const getErrorConfig = (
error: HTTPError | AxiosError,
- overrideConfig?: PartialErrorConfig,
+ overrideConfig?: TPartialErrorConfig,
) => {
const statusCode = Object.keys(HTTP_ERROR_CONFIG).includes(
error.response?.status?.toString() ?? "",
diff --git a/yarn.lock b/yarn.lock
index 945f65a..185e910 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12,6 +12,16 @@ __metadata:
languageName: node
linkType: hard
+"@ampproject/remapping@npm:^2.3.0":
+ version: 2.3.0
+ resolution: "@ampproject/remapping@npm:2.3.0"
+ dependencies:
+ "@jridgewell/gen-mapping": "npm:^0.3.5"
+ "@jridgewell/trace-mapping": "npm:^0.3.24"
+ checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed
+ languageName: node
+ linkType: hard
+
"@asamuzakjp/css-color@npm:^3.2.0":
version: 3.2.0
resolution: "@asamuzakjp/css-color@npm:3.2.0"
@@ -36,6 +46,13 @@ __metadata:
languageName: node
linkType: hard
+"@babel/helper-string-parser@npm:^7.27.1":
+ version: 7.27.1
+ resolution: "@babel/helper-string-parser@npm:7.27.1"
+ checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602
+ languageName: node
+ linkType: hard
+
"@babel/helper-validator-identifier@npm:^7.27.1":
version: 7.27.1
resolution: "@babel/helper-validator-identifier@npm:7.27.1"
@@ -43,6 +60,17 @@ __metadata:
languageName: node
linkType: hard
+"@babel/parser@npm:^7.25.4":
+ version: 7.28.0
+ resolution: "@babel/parser@npm:7.28.0"
+ dependencies:
+ "@babel/types": "npm:^7.28.0"
+ bin:
+ parser: ./bin/babel-parser.js
+ checksum: 10c0/c2ef81d598990fa949d1d388429df327420357cb5200271d0d0a2784f1e6d54afc8301eb8bdf96d8f6c77781e402da93c7dc07980fcc136ac5b9d5f1fce701b5
+ languageName: node
+ linkType: hard
+
"@babel/runtime@npm:^7.12.5":
version: 7.27.6
resolution: "@babel/runtime@npm:7.27.6"
@@ -50,6 +78,23 @@ __metadata:
languageName: node
linkType: hard
+"@babel/types@npm:^7.25.4, @babel/types@npm:^7.28.0":
+ version: 7.28.0
+ resolution: "@babel/types@npm:7.28.0"
+ dependencies:
+ "@babel/helper-string-parser": "npm:^7.27.1"
+ "@babel/helper-validator-identifier": "npm:^7.27.1"
+ checksum: 10c0/7ca8521bf5e2d2ed4db31176efaaf94463a6b7a4d16dcc60e34e963b3596c2ecadb85457bebed13a9ee9a5829ef5f515d05b55a991b6a8f3b835451843482e39
+ languageName: node
+ linkType: hard
+
+"@bcoe/v8-coverage@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "@bcoe/v8-coverage@npm:1.0.2"
+ checksum: 10c0/1eb1dc93cc17fb7abdcef21a6e7b867d6aa99a7ec88ec8207402b23d9083ab22a8011213f04b2cf26d535f1d22dc26139b7929e6c2134c254bd1e14ba5e678c3
+ languageName: node
+ linkType: hard
+
"@biomejs/biome@npm:^1.8.0":
version: 1.9.4
resolution: "@biomejs/biome@npm:1.9.4"
@@ -202,6 +247,7 @@ __metadata:
"@testing-library/user-event": "npm:^14.6.1"
"@types/react": "npm:^18.2.0"
"@types/react-dom": "npm:^18.2.0"
+ "@vitest/coverage-v8": "npm:^3.2.4"
axios: "npm:^1.10.0"
jsdom: "npm:^26.1.0"
ky: "npm:^1.8.1"
@@ -219,7 +265,6 @@ __metadata:
"@tanstack/react-query": ^4.0.0 || ^5.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
- react-error-boundary: ^4.0.0
peerDependenciesMeta:
axios:
optional: true
@@ -426,13 +471,47 @@ __metadata:
languageName: node
linkType: hard
-"@jridgewell/sourcemap-codec@npm:^1.5.0":
+"@istanbuljs/schema@npm:^0.1.2":
+ version: 0.1.3
+ resolution: "@istanbuljs/schema@npm:0.1.3"
+ checksum: 10c0/61c5286771676c9ca3eb2bd8a7310a9c063fb6e0e9712225c8471c582d157392c88f5353581c8c9adbe0dff98892317d2fdfc56c3499aa42e0194405206a963a
+ languageName: node
+ linkType: hard
+
+"@jridgewell/gen-mapping@npm:^0.3.5":
+ version: 0.3.12
+ resolution: "@jridgewell/gen-mapping@npm:0.3.12"
+ dependencies:
+ "@jridgewell/sourcemap-codec": "npm:^1.5.0"
+ "@jridgewell/trace-mapping": "npm:^0.3.24"
+ checksum: 10c0/32f771ae2467e4d440be609581f7338d786d3d621bac3469e943b9d6d116c23c4becb36f84898a92bbf2f3c0511365c54a945a3b86a83141547a2a360a5ec0c7
+ languageName: node
+ linkType: hard
+
+"@jridgewell/resolve-uri@npm:^3.1.0":
+ version: 3.1.2
+ resolution: "@jridgewell/resolve-uri@npm:3.1.2"
+ checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e
+ languageName: node
+ linkType: hard
+
+"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0":
version: 1.5.4
resolution: "@jridgewell/sourcemap-codec@npm:1.5.4"
checksum: 10c0/c5aab3e6362a8dd94ad80ab90845730c825fc4c8d9cf07ebca7a2eb8a832d155d62558800fc41d42785f989ddbb21db6df004d1786e8ecb65e428ab8dff71309
languageName: node
linkType: hard
+"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25":
+ version: 0.3.29
+ resolution: "@jridgewell/trace-mapping@npm:0.3.29"
+ dependencies:
+ "@jridgewell/resolve-uri": "npm:^3.1.0"
+ "@jridgewell/sourcemap-codec": "npm:^1.4.14"
+ checksum: 10c0/fb547ba31658c4d74eb17e7389f4908bf7c44cef47acb4c5baa57289daf68e6fe53c639f41f751b3923aca67010501264f70e7b49978ad1f040294b22c37b333
+ languageName: node
+ linkType: hard
+
"@npmcli/agent@npm:^3.0.0":
version: 3.0.0
resolution: "@npmcli/agent@npm:3.0.0"
@@ -815,6 +894,33 @@ __metadata:
languageName: node
linkType: hard
+"@vitest/coverage-v8@npm:^3.2.4":
+ version: 3.2.4
+ resolution: "@vitest/coverage-v8@npm:3.2.4"
+ dependencies:
+ "@ampproject/remapping": "npm:^2.3.0"
+ "@bcoe/v8-coverage": "npm:^1.0.2"
+ ast-v8-to-istanbul: "npm:^0.3.3"
+ debug: "npm:^4.4.1"
+ istanbul-lib-coverage: "npm:^3.2.2"
+ istanbul-lib-report: "npm:^3.0.1"
+ istanbul-lib-source-maps: "npm:^5.0.6"
+ istanbul-reports: "npm:^3.1.7"
+ magic-string: "npm:^0.30.17"
+ magicast: "npm:^0.3.5"
+ std-env: "npm:^3.9.0"
+ test-exclude: "npm:^7.0.1"
+ tinyrainbow: "npm:^2.0.0"
+ peerDependencies:
+ "@vitest/browser": 3.2.4
+ vitest: 3.2.4
+ peerDependenciesMeta:
+ "@vitest/browser":
+ optional: true
+ checksum: 10c0/cae3e58d81d56e7e1cdecd7b5baab7edd0ad9dee8dec9353c52796e390e452377d3f04174d40b6986b17c73241a5e773e422931eaa8102dcba0605ff24b25193
+ languageName: node
+ linkType: hard
+
"@vitest/expect@npm:3.2.4":
version: 3.2.4
resolution: "@vitest/expect@npm:3.2.4"
@@ -972,6 +1078,17 @@ __metadata:
languageName: node
linkType: hard
+"ast-v8-to-istanbul@npm:^0.3.3":
+ version: 0.3.3
+ resolution: "ast-v8-to-istanbul@npm:0.3.3"
+ dependencies:
+ "@jridgewell/trace-mapping": "npm:^0.3.25"
+ estree-walker: "npm:^3.0.3"
+ js-tokens: "npm:^9.0.1"
+ checksum: 10c0/ffc39bc3ab4b8c1f7aea945960ce6b1e518bab3da7c800277eab2da07d397eeae4a2cb8a5a5f817225646c8ea495c1e4434fbe082c84bae8042abddef53f50b2
+ languageName: node
+ linkType: hard
+
"asynckit@npm:^0.4.0":
version: 0.4.0
resolution: "asynckit@npm:0.4.0"
@@ -1167,7 +1284,7 @@ __metadata:
languageName: node
linkType: hard
-"debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.4.1":
+"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.4, debug@npm:^4.4.1":
version: 4.4.1
resolution: "debug@npm:4.4.1"
dependencies:
@@ -1563,7 +1680,7 @@ __metadata:
languageName: node
linkType: hard
-"glob@npm:^10.2.2":
+"glob@npm:^10.2.2, glob@npm:^10.4.1":
version: 10.4.5
resolution: "glob@npm:10.4.5"
dependencies:
@@ -1647,6 +1764,13 @@ __metadata:
languageName: node
linkType: hard
+"html-escaper@npm:^2.0.0":
+ version: 2.0.2
+ resolution: "html-escaper@npm:2.0.2"
+ checksum: 10c0/208e8a12de1a6569edbb14544f4567e6ce8ecc30b9394fcaa4e7bb1e60c12a7c9a1ed27e31290817157e8626f3a4f29e76c8747030822eb84a6abb15c255f0a0
+ languageName: node
+ linkType: hard
+
"http-cache-semantics@npm:^4.1.1":
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
@@ -1777,6 +1901,45 @@ __metadata:
languageName: node
linkType: hard
+"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2":
+ version: 3.2.2
+ resolution: "istanbul-lib-coverage@npm:3.2.2"
+ checksum: 10c0/6c7ff2106769e5f592ded1fb418f9f73b4411fd5a084387a5410538332b6567cd1763ff6b6cadca9b9eb2c443cce2f7ea7d7f1b8d315f9ce58539793b1e0922b
+ languageName: node
+ linkType: hard
+
+"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "istanbul-lib-report@npm:3.0.1"
+ dependencies:
+ istanbul-lib-coverage: "npm:^3.0.0"
+ make-dir: "npm:^4.0.0"
+ supports-color: "npm:^7.1.0"
+ checksum: 10c0/84323afb14392de8b6a5714bd7e9af845cfbd56cfe71ed276cda2f5f1201aea673c7111901227ee33e68e4364e288d73861eb2ed48f6679d1e69a43b6d9b3ba7
+ languageName: node
+ linkType: hard
+
+"istanbul-lib-source-maps@npm:^5.0.6":
+ version: 5.0.6
+ resolution: "istanbul-lib-source-maps@npm:5.0.6"
+ dependencies:
+ "@jridgewell/trace-mapping": "npm:^0.3.23"
+ debug: "npm:^4.1.1"
+ istanbul-lib-coverage: "npm:^3.0.0"
+ checksum: 10c0/ffe75d70b303a3621ee4671554f306e0831b16f39ab7f4ab52e54d356a5d33e534d97563e318f1333a6aae1d42f91ec49c76b6cd3f3fb378addcb5c81da0255f
+ languageName: node
+ linkType: hard
+
+"istanbul-reports@npm:^3.1.7":
+ version: 3.1.7
+ resolution: "istanbul-reports@npm:3.1.7"
+ dependencies:
+ html-escaper: "npm:^2.0.0"
+ istanbul-lib-report: "npm:^3.0.0"
+ checksum: 10c0/a379fadf9cf8dc5dfe25568115721d4a7eb82fbd50b005a6672aff9c6989b20cc9312d7865814e0859cd8df58cbf664482e1d3604be0afde1f7fc3ccc1394a51
+ languageName: node
+ linkType: hard
+
"jackspeak@npm:^3.1.2":
version: 3.4.3
resolution: "jackspeak@npm:3.4.3"
@@ -1901,6 +2064,26 @@ __metadata:
languageName: node
linkType: hard
+"magicast@npm:^0.3.5":
+ version: 0.3.5
+ resolution: "magicast@npm:0.3.5"
+ dependencies:
+ "@babel/parser": "npm:^7.25.4"
+ "@babel/types": "npm:^7.25.4"
+ source-map-js: "npm:^1.2.0"
+ checksum: 10c0/a6cacc0a848af84f03e3f5bda7b0de75e4d0aa9ddce5517fd23ed0f31b5ddd51b2d0ff0b7e09b51f7de0f4053c7a1107117edda6b0732dca3e9e39e6c5a68c64
+ languageName: node
+ linkType: hard
+
+"make-dir@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "make-dir@npm:4.0.0"
+ dependencies:
+ semver: "npm:^7.5.3"
+ checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68
+ languageName: node
+ linkType: hard
+
"make-fetch-happen@npm:^14.0.3":
version: 14.0.3
resolution: "make-fetch-happen@npm:14.0.3"
@@ -2449,7 +2632,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.3.5":
+"semver@npm:^7.3.5, semver@npm:^7.5.3":
version: 7.7.2
resolution: "semver@npm:7.7.2"
bin:
@@ -2516,7 +2699,7 @@ __metadata:
languageName: node
linkType: hard
-"source-map-js@npm:^1.2.1":
+"source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1":
version: 1.2.1
resolution: "source-map-js@npm:1.2.1"
checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf
@@ -2648,6 +2831,17 @@ __metadata:
languageName: node
linkType: hard
+"test-exclude@npm:^7.0.1":
+ version: 7.0.1
+ resolution: "test-exclude@npm:7.0.1"
+ dependencies:
+ "@istanbuljs/schema": "npm:^0.1.2"
+ glob: "npm:^10.4.1"
+ minimatch: "npm:^9.0.4"
+ checksum: 10c0/6d67b9af4336a2e12b26a68c83308c7863534c65f27ed4ff7068a56f5a58f7ac703e8fc80f698a19bb154fd8f705cdf7ec347d9512b2c522c737269507e7b263
+ languageName: node
+ linkType: hard
+
"tinybench@npm:^2.9.0":
version: 2.9.0
resolution: "tinybench@npm:2.9.0"